diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-04 10:56:14 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-04 10:56:14 -0700 |
commit | 77b0a4aa0732f1856aef85b8db085864e5971a14 (patch) | |
tree | 3b55b24d79afbc1fb358bc63e03469ec770d2150 /drivers/hwmon | |
parent | f80fa1822d6ccca369578108dc70576cff6c67a0 (diff) | |
parent | 7ce4190c4ca466ccd609845b97dce665317aee2a (diff) |
Merge tag 'hwmon-for-linus-v4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck:
- New hwmon registration API, including ports of several drivers to the
new API
- New hwmon driver for APM X-Gene SoC
- Added support for UCD90160, DPS-460, DPS-800, and SGD009 PMBUs chips
- Various cleanups, minor improvements, and fixes in several drivers
* tag 'hwmon-for-linus-v4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (54 commits)
hwmon: (nct6775) Add support for multiple virtual temperature sources
hwmon: (adt7470) No need for additional synchronization on kthread_stop()
hwmon: (lm95241) Update module description to include LM95231
hwmon: (lm95245) Select REGMAP_I2C
hwmon: (ibmpowernv) Fix label for cores numbers not threads
hwmon: (adt7470) Allow faster removal
hwmon: (adt7470) Add write support to alarm_mask
hwmon: (xgene) access mailbox as RAM
hwmon: (lm95245) Use new hwmon registration API
hwmon: (lm95241) Convert to use new hwmon registration API
hwmon: (jc42) Convert to use new hwmon registration API
hwmon: (max31790) Convert to use new hwmon registration API
hwmon: (nct7904) Convert to use new hwmon registration API
hwmon: (ltc4245) Convert to use new hwmon registration API
hwmon: (tmp421) Convert to use new hwmon registration API
hwmon: (tmp102) Convert to use new hwmon registration API
hwmon: (lm90) Convert to use new hwmon registration API
hwmon: (lm75) Convert to use new hwmon registration API
hwmon: (xgene) Fix crash when alarm occurs before driver probe
hwmon: (iio_hwmon) defer probe when no channel is found
...
Diffstat (limited to 'drivers/hwmon')
28 files changed, 3945 insertions, 2152 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index eaf2f916d48c..45cef3d2c75c 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -969,7 +969,6 @@ config SENSORS_LM73 config SENSORS_LM75 tristate "National Semiconductor LM75 and compatibles" depends on I2C - depends on THERMAL || !THERMAL_OF select REGMAP_I2C help If you say yes here you get support for one common type of @@ -1119,6 +1118,7 @@ config SENSORS_LM95241 config SENSORS_LM95245 tristate "National Semiconductor LM95245 and compatibles" depends on I2C + select REGMAP_I2C help If you say yes here you get support for LM95235 and LM95245 temperature sensor chips. @@ -1572,7 +1572,6 @@ config SENSORS_THMC50 config SENSORS_TMP102 tristate "Texas Instruments TMP102" depends on I2C - depends on THERMAL || !THERMAL_OF select REGMAP_I2C help If you say yes here you get support for Texas Instruments TMP102 @@ -1823,6 +1822,13 @@ config SENSORS_ULTRA45 This driver provides support for the Ultra45 workstation environmental sensors. +config SENSORS_XGENE + tristate "APM X-Gene SoC hardware monitoring driver" + depends on XGENE_SLIMPRO_MBOX || PCC + help + If you say yes here you get support for the temperature + and power sensors for APM X-Gene SoC. + if ACPI comment "ACPI drivers" diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index fe87d2895a97..aecf4ba17460 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -165,6 +165,7 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o +obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o obj-$(CONFIG_PMBUS) += pmbus/ diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c index fc1e65a263a4..812fbc00f693 100644 --- a/drivers/hwmon/adt7411.c +++ b/drivers/hwmon/adt7411.c @@ -7,8 +7,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * TODO: SPI, support for external temperature sensor - * use power-down mode for suspend?, interrupt handling? + * TODO: SPI, use power-down mode for suspend?, interrupt handling? */ #include <linux/kernel.h> @@ -31,6 +30,7 @@ #define ADT7411_REG_CFG1 0x18 #define ADT7411_CFG1_START_MONITOR (1 << 0) #define ADT7411_CFG1_RESERVED_BIT1 (1 << 1) +#define ADT7411_CFG1_EXT_TDM (1 << 2) #define ADT7411_CFG1_RESERVED_BIT3 (1 << 3) #define ADT7411_REG_CFG2 0x19 @@ -57,6 +57,7 @@ struct adt7411_data { unsigned long next_update; int vref_cached; struct i2c_client *client; + bool use_ext_temp; }; /* @@ -127,11 +128,20 @@ static ssize_t adt7411_show_vdd(struct device *dev, static ssize_t adt7411_show_temp(struct device *dev, struct device_attribute *attr, char *buf) { + int nr = to_sensor_dev_attr(attr)->index; struct adt7411_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; - int val = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB, - ADT7411_REG_INT_TEMP_MSB, 0); - + int val; + struct { + u8 low; + u8 high; + } reg[2] = { + { ADT7411_REG_INT_TEMP_VDD_LSB, ADT7411_REG_INT_TEMP_MSB }, + { ADT7411_REG_EXT_TEMP_AIN14_LSB, + ADT7411_REG_EXT_TEMP_AIN1_MSB }, + }; + + val = adt7411_read_10_bit(client, reg[nr].low, reg[nr].high, 0); if (val < 0) return val; @@ -218,11 +228,13 @@ static ssize_t adt7411_set_bit(struct device *dev, return ret < 0 ? ret : count; } + #define ADT7411_BIT_ATTR(__name, __reg, __bit) \ SENSOR_DEVICE_ATTR_2(__name, S_IRUGO | S_IWUSR, adt7411_show_bit, \ adt7411_set_bit, __bit, __reg) -static DEVICE_ATTR(temp1_input, S_IRUGO, adt7411_show_temp, NULL); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7411_show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, adt7411_show_temp, NULL, 1); static DEVICE_ATTR(in0_input, S_IRUGO, adt7411_show_vdd, NULL); static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, adt7411_show_input, NULL, 0); static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, adt7411_show_input, NULL, 1); @@ -237,7 +249,8 @@ static ADT7411_BIT_ATTR(fast_sampling, ADT7411_REG_CFG3, ADT7411_CFG3_ADC_CLK_22 static ADT7411_BIT_ATTR(adc_ref_vdd, ADT7411_REG_CFG3, ADT7411_CFG3_REF_VDD); static struct attribute *adt7411_attrs[] = { - &dev_attr_temp1_input.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, &dev_attr_in0_input.attr, &sensor_dev_attr_in1_input.dev_attr.attr, &sensor_dev_attr_in2_input.dev_attr.attr, @@ -253,7 +266,27 @@ static struct attribute *adt7411_attrs[] = { NULL }; -ATTRIBUTE_GROUPS(adt7411); +static umode_t adt7411_attrs_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct adt7411_data *data = dev_get_drvdata(dev); + bool visible = true; + + if (attr == &sensor_dev_attr_temp2_input.dev_attr.attr) + visible = data->use_ext_temp; + else if (attr == &sensor_dev_attr_in1_input.dev_attr.attr || + attr == &sensor_dev_attr_in2_input.dev_attr.attr) + visible = !data->use_ext_temp; + + return visible ? attr->mode : 0; +} + +static const struct attribute_group adt7411_group = { + .attrs = adt7411_attrs, + .is_visible = adt7411_attrs_visible, +}; +__ATTRIBUTE_GROUPS(adt7411); static int adt7411_detect(struct i2c_client *client, struct i2c_board_info *info) @@ -309,6 +342,8 @@ static int adt7411_init_device(struct adt7411_data *data) if (ret < 0) return ret; + data->use_ext_temp = ret & ADT7411_CFG1_EXT_TDM; + /* * We must only write zero to bit 1 and only one to bit 3 according to * the datasheet. diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index f5da39a68929..6e60ca53406e 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -32,6 +32,7 @@ #include <linux/log2.h> #include <linux/kthread.h> #include <linux/slab.h> +#include <linux/util_macros.h> /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; @@ -83,6 +84,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; #define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E #define ADT7470_REG_PWM_TEMP_MIN_MAX_ADDR 0x71 +#define ADT7470_REG_CFG_2 0x74 #define ADT7470_REG_ACOUSTICS12 0x75 #define ADT7470_REG_ACOUSTICS34 0x76 #define ADT7470_REG_DEVICE 0x3D @@ -142,6 +144,11 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; #define FAN_PERIOD_INVALID 65535 #define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) +/* Config registers 1 and 2 include fields for selecting the PWM frequency */ +#define ADT7470_CFG_LF 0x40 +#define ADT7470_FREQ_MASK 0x70 +#define ADT7470_FREQ_SHIFT 4 + struct adt7470_data { struct i2c_client *client; struct mutex lock; @@ -170,7 +177,6 @@ struct adt7470_data { u8 pwm_auto_temp[ADT7470_PWM_COUNT]; struct task_struct *auto_update; - struct completion auto_update_stop; unsigned int auto_update_interval; }; @@ -266,12 +272,14 @@ static int adt7470_update_thread(void *p) mutex_lock(&data->lock); adt7470_read_temperatures(client, data); mutex_unlock(&data->lock); + + set_current_state(TASK_INTERRUPTIBLE); if (kthread_should_stop()) break; - msleep_interruptible(data->auto_update_interval); + + schedule_timeout(msecs_to_jiffies(data->auto_update_interval)); } - complete_all(&data->auto_update_stop); return 0; } @@ -538,6 +546,28 @@ static ssize_t show_alarm_mask(struct device *dev, return sprintf(buf, "%x\n", data->alarms_mask); } +static ssize_t set_alarm_mask(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct adt7470_data *data = dev_get_drvdata(dev); + long mask; + + if (kstrtoul(buf, 0, &mask)) + return -EINVAL; + + if (mask & ~0xffff) + return -EINVAL; + + mutex_lock(&data->lock); + data->alarms_mask = mask; + adt7470_write_word_data(data->client, ADT7470_REG_ALARM1_MASK, mask); + mutex_unlock(&data->lock); + + return count; +} + static ssize_t show_fan_max(struct device *dev, struct device_attribute *devattr, char *buf) @@ -688,6 +718,70 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, return count; } +/* These are the valid PWM frequencies to the nearest Hz */ +static const int adt7470_freq_map[] = { + 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500 +}; + +static ssize_t show_pwm_freq(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct adt7470_data *data = adt7470_update_device(dev); + unsigned char cfg_reg_1; + unsigned char cfg_reg_2; + int index; + + mutex_lock(&data->lock); + cfg_reg_1 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG); + cfg_reg_2 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG_2); + mutex_unlock(&data->lock); + + index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT; + if (!(cfg_reg_1 & ADT7470_CFG_LF)) + index += 8; + if (index >= ARRAY_SIZE(adt7470_freq_map)) + index = ARRAY_SIZE(adt7470_freq_map) - 1; + + return scnprintf(buf, PAGE_SIZE, "%d\n", adt7470_freq_map[index]); +} + +static ssize_t set_pwm_freq(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct adt7470_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + long freq; + int index; + int low_freq = ADT7470_CFG_LF; + unsigned char val; + + if (kstrtol(buf, 10, &freq)) + return -EINVAL; + + /* Round the user value given to the closest available frequency */ + index = find_closest(freq, adt7470_freq_map, + ARRAY_SIZE(adt7470_freq_map)); + + if (index >= 8) { + index -= 8; + low_freq = 0; + } + + mutex_lock(&data->lock); + /* Configuration Register 1 */ + val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); + i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, + (val & ~ADT7470_CFG_LF) | low_freq); + /* Configuration Register 2 */ + val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG_2); + i2c_smbus_write_byte_data(client, ADT7470_REG_CFG_2, + (val & ~ADT7470_FREQ_MASK) | (index << ADT7470_FREQ_SHIFT)); + mutex_unlock(&data->lock); + + return count; +} + static ssize_t show_pwm_max(struct device *dev, struct device_attribute *devattr, char *buf) @@ -918,7 +1012,8 @@ static ssize_t show_alarm(struct device *dev, return sprintf(buf, "0\n"); } -static DEVICE_ATTR(alarm_mask, S_IRUGO, show_alarm_mask, NULL); +static DEVICE_ATTR(alarm_mask, S_IWUSR | S_IRUGO, show_alarm_mask, + set_alarm_mask); static DEVICE_ATTR(num_temp_sensors, S_IWUSR | S_IRUGO, show_num_temp_sensors, set_num_temp_sensors); static DEVICE_ATTR(auto_update_interval, S_IWUSR | S_IRUGO, @@ -1038,6 +1133,8 @@ static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1); static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2); static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3); +static DEVICE_ATTR(pwm1_freq, S_IWUSR | S_IRUGO, show_pwm_freq, set_pwm_freq); + static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO, show_pwm_min, set_pwm_min, 0); static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO, @@ -1154,6 +1251,7 @@ static struct attribute *adt7470_attrs[] = { &sensor_dev_attr_fan4_alarm.dev_attr.attr, &sensor_dev_attr_force_pwm_max.dev_attr.attr, &sensor_dev_attr_pwm1.dev_attr.attr, + &dev_attr_pwm1_freq.attr, &sensor_dev_attr_pwm2.dev_attr.attr, &sensor_dev_attr_pwm3.dev_attr.attr, &sensor_dev_attr_pwm4.dev_attr.attr, @@ -1256,7 +1354,6 @@ static int adt7470_probe(struct i2c_client *client, if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); - init_completion(&data->auto_update_stop); data->auto_update = kthread_run(adt7470_update_thread, client, "%s", dev_name(hwmon_dev)); if (IS_ERR(data->auto_update)) { @@ -1271,7 +1368,6 @@ static int adt7470_remove(struct i2c_client *client) struct adt7470_data *data = i2c_get_clientdata(client); kthread_stop(data->auto_update); - wait_for_completion(&data->auto_update_stop); return 0; } diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c index 48633e541dc3..0f0277e7aae5 100644 --- a/drivers/hwmon/ftsteutates.c +++ b/drivers/hwmon/ftsteutates.c @@ -36,6 +36,10 @@ #define FTS_EVENT_STATUS_REG 0x0006 #define FTS_GLOBAL_CONTROL_REG 0x0007 +#define FTS_DEVICE_DETECT_REG_1 0x0C +#define FTS_DEVICE_DETECT_REG_2 0x0D +#define FTS_DEVICE_DETECT_REG_3 0x0E + #define FTS_SENSOR_EVENT_REG 0x0010 #define FTS_FAN_EVENT_REG 0x0014 @@ -54,6 +58,8 @@ #define FTS_NO_TEMP_SENSORS 0x10 #define FTS_NO_VOLT_SENSORS 0x04 +static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; + static struct i2c_device_id fts_id[] = { { "ftsteutates", 0 }, { } @@ -734,6 +740,42 @@ static const struct attribute_group *fts_attr_groups[] = { /*****************************************************************************/ /* Module initialization / remove functions */ /*****************************************************************************/ +static int fts_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + int val; + + /* detection works with revsion greater or equal to 0x2b */ + val = i2c_smbus_read_byte_data(client, FTS_DEVICE_REVISION_REG); + if (val < 0x2b) + return -ENODEV; + + /* Device Detect Regs must have 0x17 0x34 and 0x54 */ + val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_1); + if (val != 0x17) + return -ENODEV; + + val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_2); + if (val != 0x34) + return -ENODEV; + + val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_3); + if (val != 0x54) + return -ENODEV; + + /* + * 0x10 == Baseboard Management Controller, 0x01 == Teutates + * Device ID Reg needs to be 0x11 + */ + val = i2c_smbus_read_byte_data(client, FTS_DEVICE_ID_REG); + if (val != 0x11) + return -ENODEV; + + strlcpy(info->type, fts_id[0].name, I2C_NAME_SIZE); + info->flags = 0; + return 0; +} + static int fts_remove(struct i2c_client *client) { struct fts_data *data = dev_get_drvdata(&client->dev); @@ -804,12 +846,15 @@ static int fts_probe(struct i2c_client *client, const struct i2c_device_id *id) /* Module Details */ /*****************************************************************************/ static struct i2c_driver fts_driver = { + .class = I2C_CLASS_HWMON, .driver = { .name = "ftsteutates", }, .id_table = fts_id, .probe = fts_probe, .remove = fts_remove, + .detect = fts_detect, + .address_list = normal_i2c, }; module_i2c_driver(fts_driver); diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index a26c385a435b..adae6848ffb2 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -12,17 +12,17 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/module.h> +#include <linux/bitops.h> #include <linux/device.h> #include <linux/err.h> -#include <linux/slab.h> -#include <linux/kdev_t.h> -#include <linux/idr.h> -#include <linux/hwmon.h> #include <linux/gfp.h> -#include <linux/spinlock.h> +#include <linux/hwmon.h> +#include <linux/idr.h> +#include <linux/module.h> #include <linux/pci.h> +#include <linux/slab.h> #include <linux/string.h> +#include <linux/thermal.h> #define HWMON_ID_PREFIX "hwmon" #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" @@ -30,9 +30,35 @@ struct hwmon_device { const char *name; struct device dev; + const struct hwmon_chip_info *chip; + + struct attribute_group group; + const struct attribute_group **groups; }; + #define to_hwmon_device(d) container_of(d, struct hwmon_device, dev) +struct hwmon_device_attribute { + struct device_attribute dev_attr; + const struct hwmon_ops *ops; + enum hwmon_sensor_types type; + u32 attr; + int index; +}; + +#define to_hwmon_attr(d) \ + container_of(d, struct hwmon_device_attribute, dev_attr) + +/* + * Thermal zone information + * In addition to the reference to the hwmon device, + * also provides the sensor index. + */ +struct hwmon_thermal_data { + struct hwmon_device *hwdev; /* Reference to hwmon device */ + int index; /* sensor index */ +}; + static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) { @@ -80,25 +106,409 @@ static struct class hwmon_class = { static DEFINE_IDA(hwmon_ida); -/** - * hwmon_device_register_with_groups - register w/ hwmon - * @dev: the parent device - * @name: hwmon name attribute - * @drvdata: driver data to attach to created device - * @groups: List of attribute groups to create - * - * hwmon_device_unregister() must be called when the device is no - * longer needed. - * - * Returns the pointer to the new device. +/* Thermal zone handling */ + +/* + * The complex conditional is necessary to avoid a cyclic dependency + * between hwmon and thermal_sys modules. */ -struct device * -hwmon_device_register_with_groups(struct device *dev, const char *name, - void *drvdata, - const struct attribute_group **groups) +#if IS_REACHABLE(CONFIG_THERMAL) && defined(CONFIG_THERMAL_OF) && \ + (!defined(CONFIG_THERMAL_HWMON) || \ + !(defined(MODULE) && IS_MODULE(CONFIG_THERMAL))) +static int hwmon_thermal_get_temp(void *data, int *temp) +{ + struct hwmon_thermal_data *tdata = data; + struct hwmon_device *hwdev = tdata->hwdev; + int ret; + long t; + + ret = hwdev->chip->ops->read(&hwdev->dev, hwmon_temp, hwmon_temp_input, + tdata->index, &t); + if (ret < 0) + return ret; + + *temp = t; + + return 0; +} + +static struct thermal_zone_of_device_ops hwmon_thermal_ops = { + .get_temp = hwmon_thermal_get_temp, +}; + +static int hwmon_thermal_add_sensor(struct device *dev, + struct hwmon_device *hwdev, int index) +{ + struct hwmon_thermal_data *tdata; + + tdata = devm_kzalloc(dev, sizeof(*tdata), GFP_KERNEL); + if (!tdata) + return -ENOMEM; + + tdata->hwdev = hwdev; + tdata->index = index; + + devm_thermal_zone_of_sensor_register(&hwdev->dev, index, tdata, + &hwmon_thermal_ops); + + return 0; +} +#else +static int hwmon_thermal_add_sensor(struct device *dev, + struct hwmon_device *hwdev, int index) +{ + return 0; +} +#endif /* IS_REACHABLE(CONFIG_THERMAL) && ... */ + +/* sysfs attribute management */ + +static ssize_t hwmon_attr_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); + long val; + int ret; + + ret = hattr->ops->read(dev, hattr->type, hattr->attr, hattr->index, + &val); + if (ret < 0) + return ret; + + return sprintf(buf, "%ld\n", val); +} + +static ssize_t hwmon_attr_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); + long val; + int ret; + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + ret = hattr->ops->write(dev, hattr->type, hattr->attr, hattr->index, + val); + if (ret < 0) + return ret; + + return count; +} + +static int hwmon_attr_base(enum hwmon_sensor_types type) +{ + if (type == hwmon_in) + return 0; + return 1; +} + +static struct attribute *hwmon_genattr(struct device *dev, + const void *drvdata, + enum hwmon_sensor_types type, + u32 attr, + int index, + const char *template, + const struct hwmon_ops *ops) +{ + struct hwmon_device_attribute *hattr; + struct device_attribute *dattr; + struct attribute *a; + umode_t mode; + char *name; + + /* The attribute is invisible if there is no template string */ + if (!template) + return ERR_PTR(-ENOENT); + + mode = ops->is_visible(drvdata, type, attr, index); + if (!mode) + return ERR_PTR(-ENOENT); + + if ((mode & S_IRUGO) && !ops->read) + return ERR_PTR(-EINVAL); + if ((mode & S_IWUGO) && !ops->write) + return ERR_PTR(-EINVAL); + + if (type == hwmon_chip) { + name = (char *)template; + } else { + name = devm_kzalloc(dev, strlen(template) + 16, GFP_KERNEL); + if (!name) + return ERR_PTR(-ENOMEM); + scnprintf(name, strlen(template) + 16, template, + index + hwmon_attr_base(type)); + } + + hattr = devm_kzalloc(dev, sizeof(*hattr), GFP_KERNEL); + if (!hattr) + return ERR_PTR(-ENOMEM); + + hattr->type = type; + hattr->attr = attr; + hattr->index = index; + hattr->ops = ops; + + dattr = &hattr->dev_attr; + dattr->show = hwmon_attr_show; + dattr->store = hwmon_attr_store; + + a = &dattr->attr; + sysfs_attr_init(a); + a->name = name; + a->mode = mode; + + return a; +} + +static const char * const hwmon_chip_attr_templates[] = { + [hwmon_chip_temp_reset_history] = "temp_reset_history", + [hwmon_chip_in_reset_history] = "in_reset_history", + [hwmon_chip_curr_reset_history] = "curr_reset_history", + [hwmon_chip_power_reset_history] = "power_reset_history", + [hwmon_chip_update_interval] = "update_interval", + [hwmon_chip_alarms] = "alarms", +}; + +static const char * const hwmon_temp_attr_templates[] = { + [hwmon_temp_input] = "temp%d_input", + [hwmon_temp_type] = "temp%d_type", + [hwmon_temp_lcrit] = "temp%d_lcrit", + [hwmon_temp_lcrit_hyst] = "temp%d_lcrit_hyst", + [hwmon_temp_min] = "temp%d_min", + [hwmon_temp_min_hyst] = "temp%d_min_hyst", + [hwmon_temp_max] = "temp%d_max", + [hwmon_temp_max_hyst] = "temp%d_max_hyst", + [hwmon_temp_crit] = "temp%d_crit", + [hwmon_temp_crit_hyst] = "temp%d_crit_hyst", + [hwmon_temp_emergency] = "temp%d_emergency", + [hwmon_temp_emergency_hyst] = "temp%d_emergency_hyst", + [hwmon_temp_alarm] = "temp%d_alarm", + [hwmon_temp_lcrit_alarm] = "temp%d_lcrit_alarm", + [hwmon_temp_min_alarm] = "temp%d_min_alarm", + [hwmon_temp_max_alarm] = "temp%d_max_alarm", + [hwmon_temp_crit_alarm] = "temp%d_crit_alarm", + [hwmon_temp_emergency_alarm] = "temp%d_emergency_alarm", + [hwmon_temp_fault] = "temp%d_fault", + [hwmon_temp_offset] = "temp%d_offset", + [hwmon_temp_label] = "temp%d_label", + [hwmon_temp_lowest] = "temp%d_lowest", + [hwmon_temp_highest] = "temp%d_highest", + [hwmon_temp_reset_history] = "temp%d_reset_history", +}; + +static const char * const hwmon_in_attr_templates[] = { + [hwmon_in_input] = "in%d_input", + [hwmon_in_min] = "in%d_min", + [hwmon_in_max] = "in%d_max", + [hwmon_in_lcrit] = "in%d_lcrit", + [hwmon_in_crit] = "in%d_crit", + [hwmon_in_average] = "in%d_average", + [hwmon_in_lowest] = "in%d_lowest", + [hwmon_in_highest] = "in%d_highest", + [hwmon_in_reset_history] = "in%d_reset_history", + [hwmon_in_label] = "in%d_label", + [hwmon_in_alarm] = "in%d_alarm", + [hwmon_in_min_alarm] = "in%d_min_alarm", + [hwmon_in_max_alarm] = "in%d_max_alarm", + [hwmon_in_lcrit_alarm] = "in%d_lcrit_alarm", + [hwmon_in_crit_alarm] = "in%d_crit_alarm", +}; + +static const char * const hwmon_curr_attr_templates[] = { + [hwmon_curr_input] = "curr%d_input", + [hwmon_curr_min] = "curr%d_min", + [hwmon_curr_max] = "curr%d_max", + [hwmon_curr_lcrit] = "curr%d_lcrit", + [hwmon_curr_crit] = "curr%d_crit", + [hwmon_curr_average] = "curr%d_average", + [hwmon_curr_lowest] = "curr%d_lowest", + [hwmon_curr_highest] = "curr%d_highest", + [hwmon_curr_reset_history] = "curr%d_reset_history", + [hwmon_curr_label] = "curr%d_label", + [hwmon_curr_alarm] = "curr%d_alarm", + [hwmon_curr_min_alarm] = "curr%d_min_alarm", + [hwmon_curr_max_alarm] = "curr%d_max_alarm", + [hwmon_curr_lcrit_alarm] = "curr%d_lcrit_alarm", + [hwmon_curr_crit_alarm] = "curr%d_crit_alarm", +}; + +static const char * const hwmon_power_attr_templates[] = { + [hwmon_power_average] = "power%d_average", + [hwmon_power_average_interval] = "power%d_average_interval", + [hwmon_power_average_interval_max] = "power%d_interval_max", + [hwmon_power_average_interval_min] = "power%d_interval_min", + [hwmon_power_average_highest] = "power%d_average_highest", + [hwmon_power_average_lowest] = "power%d_average_lowest", + [hwmon_power_average_max] = "power%d_average_max", + [hwmon_power_average_min] = "power%d_average_min", + [hwmon_power_input] = "power%d_input", + [hwmon_power_input_highest] = "power%d_input_highest", + [hwmon_power_input_lowest] = "power%d_input_lowest", + [hwmon_power_reset_history] = "power%d_reset_history", + [hwmon_power_accuracy] = "power%d_accuracy", + [hwmon_power_cap] = "power%d_cap", + [hwmon_power_cap_hyst] = "power%d_cap_hyst", + [hwmon_power_cap_max] = "power%d_cap_max", + [hwmon_power_cap_min] = "power%d_cap_min", + [hwmon_power_max] = "power%d_max", + [hwmon_power_crit] = "power%d_crit", + [hwmon_power_label] = "power%d_label", + [hwmon_power_alarm] = "power%d_alarm", + [hwmon_power_cap_alarm] = "power%d_cap_alarm", + [hwmon_power_max_alarm] = "power%d_max_alarm", + [hwmon_power_crit_alarm] = "power%d_crit_alarm", +}; + +static const char * const hwmon_energy_attr_templates[] = { + [hwmon_energy_input] = "energy%d_input", + [hwmon_energy_label] = "energy%d_label", +}; + +static const char * const hwmon_humidity_attr_templates[] = { + [hwmon_humidity_input] = "humidity%d_input", + [hwmon_humidity_label] = "humidity%d_label", + [hwmon_humidity_min] = "humidity%d_min", + [hwmon_humidity_min_hyst] = "humidity%d_min_hyst", + [hwmon_humidity_max] = "humidity%d_max", + [hwmon_humidity_max_hyst] = "humidity%d_max_hyst", + [hwmon_humidity_alarm] = "humidity%d_alarm", + [hwmon_humidity_fault] = "humidity%d_fault", +}; + +static const char * const hwmon_fan_attr_templates[] = { + [hwmon_fan_input] = "fan%d_input", + [hwmon_fan_label] = "fan%d_label", + [hwmon_fan_min] = "fan%d_min", + [hwmon_fan_max] = "fan%d_max", + [hwmon_fan_div] = "fan%d_div", + [hwmon_fan_pulses] = "fan%d_pulses", + [hwmon_fan_target] = "fan%d_target", + [hwmon_fan_alarm] = "fan%d_alarm", + [hwmon_fan_min_alarm] = "fan%d_min_alarm", + [hwmon_fan_max_alarm] = "fan%d_max_alarm", + [hwmon_fan_fault] = "fan%d_fault", +}; + +static const char * const hwmon_pwm_attr_templates[] = { + [hwmon_pwm_input] = "pwm%d", + [hwmon_pwm_enable] = "pwm%d_enable", + [hwmon_pwm_mode] = "pwm%d_mode", + [hwmon_pwm_freq] = "pwm%d_freq", +}; + +static const char * const *__templates[] = { + [hwmon_chip] = hwmon_chip_attr_templates, + [hwmon_temp] = hwmon_temp_attr_templates, + [hwmon_in] = hwmon_in_attr_templates, + [hwmon_curr] = hwmon_curr_attr_templates, + [hwmon_power] = hwmon_power_attr_templates, + [hwmon_energy] = hwmon_energy_attr_templates, + [hwmon_humidity] = hwmon_humidity_attr_templates, + [hwmon_fan] = hwmon_fan_attr_templates, + [hwmon_pwm] = hwmon_pwm_attr_templates, +}; + +static const int __templates_size[] = { + [hwmon_chip] = ARRAY_SIZE(hwmon_chip_attr_templates), + [hwmon_temp] = ARRAY_SIZE(hwmon_temp_attr_templates), + [hwmon_in] = ARRAY_SIZE(hwmon_in_attr_templates), + [hwmon_curr] = ARRAY_SIZE(hwmon_curr_attr_templates), + [hwmon_power] = ARRAY_SIZE(hwmon_power_attr_templates), + [hwmon_energy] = ARRAY_SIZE(hwmon_energy_attr_templates), + [hwmon_humidity] = ARRAY_SIZE(hwmon_humidity_attr_templates), + [hwmon_fan] = ARRAY_SIZE(hwmon_fan_attr_templates), + [hwmon_pwm] = ARRAY_SIZE(hwmon_pwm_attr_templates), +}; + +static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info) +{ + int i, n; + + for (i = n = 0; info->config[i]; i++) + n += hweight32(info->config[i]); + + return n; +} + +static int hwmon_genattrs(struct device *dev, + const void *drvdata, + struct attribute **attrs, + const struct hwmon_ops *ops, + const struct hwmon_channel_info *info) +{ + const char * const *templates; + int template_size; + int i, aindex = 0; + + if (info->type >= ARRAY_SIZE(__templates)) + return -EINVAL; + + templates = __templates[info->type]; + template_size = __templates_size[info->type]; + + for (i = 0; info->config[i]; i++) { + u32 attr_mask = info->config[i]; + u32 attr; + + while (attr_mask) { + struct attribute *a; + + attr = __ffs(attr_mask); + attr_mask &= ~BIT(attr); + if (attr >= template_size) + return -EINVAL; + a = hwmon_genattr(dev, drvdata, info->type, attr, i, + templates[attr], ops); + if (IS_ERR(a)) { + if (PTR_ERR(a) != -ENOENT) + return PTR_ERR(a); + continue; + } + attrs[aindex++] = a; + } + } + return aindex; +} + +static struct attribute ** +__hwmon_create_attrs(struct device *dev, const void *drvdata, + const struct hwmon_chip_info *chip) +{ + int ret, i, aindex = 0, nattrs = 0; + struct attribute **attrs; + + for (i = 0; chip->info[i]; i++) + nattrs += hwmon_num_channel_attrs(chip->info[i]); + + if (nattrs == 0) + return ERR_PTR(-EINVAL); + + attrs = devm_kcalloc(dev, nattrs + 1, sizeof(*attrs), GFP_KERNEL); + if (!attrs) + return ERR_PTR(-ENOMEM); + + for (i = 0; chip->info[i]; i++) { + ret = hwmon_genattrs(dev, drvdata, &attrs[aindex], chip->ops, + chip->info[i]); + if (ret < 0) + return ERR_PTR(ret); + aindex += ret; + } + + return attrs; +} + +static struct device * +__hwmon_device_register(struct device *dev, const char *name, void *drvdata, + const struct hwmon_chip_info *chip, + const struct attribute_group **groups) { struct hwmon_device *hwdev; - int err, id; + struct device *hdev; + int i, j, err, id; /* Do not accept invalid characters in hwmon name attribute */ if (name && (!strlen(name) || strpbrk(name, "-* \t\n"))) @@ -114,28 +524,128 @@ hwmon_device_register_with_groups(struct device *dev, const char *name, goto ida_remove; } + hdev = &hwdev->dev; + + if (chip && chip->ops->is_visible) { + struct attribute **attrs; + int ngroups = 2; + + if (groups) + for (i = 0; groups[i]; i++) + ngroups++; + + hwdev->groups = devm_kcalloc(dev, ngroups, sizeof(*groups), + GFP_KERNEL); + if (!hwdev->groups) + return ERR_PTR(-ENOMEM); + + attrs = __hwmon_create_attrs(dev, drvdata, chip); + if (IS_ERR(attrs)) { + err = PTR_ERR(attrs); + goto free_hwmon; + } + + hwdev->group.attrs = attrs; + ngroups = 0; + hwdev->groups[ngroups++] = &hwdev->group; + + if (groups) { + for (i = 0; groups[i]; i++) + hwdev->groups[ngroups++] = groups[i]; + } + + hdev->groups = hwdev->groups; + } else { + hdev->groups = groups; + } + hwdev->name = name; - hwdev->dev.class = &hwmon_class; - hwdev->dev.parent = dev; - hwdev->dev.groups = groups; - hwdev->dev.of_node = dev ? dev->of_node : NULL; - dev_set_drvdata(&hwdev->dev, drvdata); - dev_set_name(&hwdev->dev, HWMON_ID_FORMAT, id); - err = device_register(&hwdev->dev); + hdev->class = &hwmon_class; + hdev->parent = dev; + hdev->of_node = dev ? dev->of_node : NULL; + hwdev->chip = chip; + dev_set_drvdata(hdev, drvdata); + dev_set_name(hdev, HWMON_ID_FORMAT, id); + err = device_register(hdev); if (err) - goto free; + goto free_hwmon; + + if (chip && chip->ops->is_visible && chip->ops->read && + chip->info[0]->type == hwmon_chip && + (chip->info[0]->config[0] & HWMON_C_REGISTER_TZ)) { + const struct hwmon_channel_info **info = chip->info; + + for (i = 1; info[i]; i++) { + if (info[i]->type != hwmon_temp) + continue; + + for (j = 0; info[i]->config[j]; j++) { + if (!chip->ops->is_visible(drvdata, hwmon_temp, + hwmon_temp_input, j)) + continue; + if (info[i]->config[j] & HWMON_T_INPUT) + hwmon_thermal_add_sensor(dev, hwdev, j); + } + } + } - return &hwdev->dev; + return hdev; -free: +free_hwmon: kfree(hwdev); ida_remove: ida_simple_remove(&hwmon_ida, id); return ERR_PTR(err); } + +/** + * hwmon_device_register_with_groups - register w/ hwmon + * @dev: the parent device + * @name: hwmon name attribute + * @drvdata: driver data to attach to created device + * @groups: List of attribute groups to create + * + * hwmon_device_unregister() must be called when the device is no + * longer needed. + * + * Returns the pointer to the new device. + */ +struct device * +hwmon_device_register_with_groups(struct device *dev, const char *name, + void *drvdata, + const struct attribute_group **groups) +{ + return __hwmon_device_register(dev, name, drvdata, NULL, groups); +} EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups); /** + * hwmon_device_register_with_info - register w/ hwmon + * @dev: the parent device + * @name: hwmon name attribute + * @drvdata: driver data to attach to created device + * @info: Pointer to hwmon chip information + * @groups - pointer to list of driver specific attribute groups + * + * hwmon_device_unregister() must be called when the device is no + * longer needed. + * + * Returns the pointer to the new device. + */ +struct device * +hwmon_device_register_with_info(struct device *dev, const char *name, + void *drvdata, + const struct hwmon_chip_info *chip, + const struct attribute_group **groups) +{ + if (chip && (!chip->ops || !chip->info)) + return ERR_PTR(-EINVAL); + + return __hwmon_device_register(dev, name, drvdata, chip, groups); +} +EXPORT_SYMBOL_GPL(hwmon_device_register_with_info); + +/** * hwmon_device_register - register w/ hwmon * @dev: the device to register * @@ -213,6 +723,48 @@ error: } EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups); +/** + * devm_hwmon_device_register_with_info - register w/ hwmon + * @dev: the parent device + * @name: hwmon name attribute + * @drvdata: driver data to attach to created device + * @info: Pointer to hwmon chip information + * @groups - pointer to list of driver specific attribute groups + * + * Returns the pointer to the new device. The new device is automatically + * unregistered with the parent device. + */ +struct device * +devm_hwmon_device_register_with_info(struct device *dev, const char *name, + void *drvdata, + const struct hwmon_chip_info *chip, + const struct attribute_group **groups) +{ + struct device **ptr, *hwdev; + + if (!dev) + return ERR_PTR(-EINVAL); + + ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + hwdev = hwmon_device_register_with_info(dev, name, drvdata, chip, + groups); + if (IS_ERR(hwdev)) + goto error; + + *ptr = hwdev; + devres_add(dev, ptr); + + return hwdev; + +error: + devres_free(ptr); + return hwdev; +} +EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_info); + static int devm_hwmon_match(struct device *dev, void *res, void *data) { struct device **hwdev = res; diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c index 55b5a8ff1cfe..6d2e6605751c 100644 --- a/drivers/hwmon/ibmpowernv.c +++ b/drivers/hwmon/ibmpowernv.c @@ -143,13 +143,11 @@ static void __init make_sensor_label(struct device_node *np, if (cpuid >= 0) /* * The digital thermal sensors are associated - * with a core. Let's print out the range of - * cpu ids corresponding to the hardware - * threads of the core. + * with a core. */ n += snprintf(sdata->label + n, - sizeof(sdata->label) - n, " %d-%d", - cpuid, cpuid + threads_per_core - 1); + sizeof(sdata->label) - n, " %d", + cpuid); else n += snprintf(sdata->label + n, sizeof(sdata->label) - n, " phy%d", id); diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index 89449871bca7..f6a76679c650 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -73,8 +73,11 @@ static int iio_hwmon_probe(struct platform_device *pdev) name = dev->of_node->name; channels = iio_channel_get_all(dev); - if (IS_ERR(channels)) + if (IS_ERR(channels)) { + if (PTR_ERR(channels) == -ENODEV) + return -EPROBE_DEFER; return PTR_ERR(channels); + } st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); if (st == NULL) { diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 4667012b46b7..ad82cb28d87a 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -2011,10 +2011,10 @@ static struct attribute *it87_attributes_in[] = { &sensor_dev_attr_in7_beep.dev_attr.attr, /* 39 */ &sensor_dev_attr_in8_input.dev_attr.attr, /* 40 */ - &sensor_dev_attr_in9_input.dev_attr.attr, /* 41 */ - &sensor_dev_attr_in10_input.dev_attr.attr, /* 41 */ - &sensor_dev_attr_in11_input.dev_attr.attr, /* 41 */ - &sensor_dev_attr_in12_input.dev_attr.attr, /* 41 */ + &sensor_dev_attr_in9_input.dev_attr.attr, + &sensor_dev_attr_in10_input.dev_attr.attr, + &sensor_dev_attr_in11_input.dev_attr.attr, + &sensor_dev_attr_in12_input.dev_attr.attr, NULL }; diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 9d5f85f3384f..1bf22eff0b08 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -28,7 +28,6 @@ #include <linux/jiffies.h> #include <linux/i2c.h> #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> #include <linux/of.h> @@ -254,170 +253,148 @@ abort: return ret; } -/* sysfs functions */ - -static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct jc42_data *data = jc42_update_device(dev); - if (IS_ERR(data)) - return PTR_ERR(data); - return sprintf(buf, "%d\n", - jc42_temp_from_reg(data->temp[attr->index])); -} - -static ssize_t show_temp_hyst(struct device *dev, - struct device_attribute *devattr, char *buf) +static int jc42_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct jc42_data *data = jc42_update_device(dev); int temp, hyst; if (IS_ERR(data)) return PTR_ERR(data); - temp = jc42_temp_from_reg(data->temp[attr->index]); - hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK) - >> JC42_CFG_HYST_SHIFT]; - return sprintf(buf, "%d\n", temp - hyst); + switch (attr) { + case hwmon_temp_input: + *val = jc42_temp_from_reg(data->temp[t_input]); + return 0; + case hwmon_temp_min: + *val = jc42_temp_from_reg(data->temp[t_min]); + return 0; + case hwmon_temp_max: + *val = jc42_temp_from_reg(data->temp[t_max]); + return 0; + case hwmon_temp_crit: + *val = jc42_temp_from_reg(data->temp[t_crit]); + return 0; + case hwmon_temp_max_hyst: + temp = jc42_temp_from_reg(data->temp[t_max]); + hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK) + >> JC42_CFG_HYST_SHIFT]; + *val = temp - hyst; + return 0; + case hwmon_temp_crit_hyst: + temp = jc42_temp_from_reg(data->temp[t_crit]); + hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK) + >> JC42_CFG_HYST_SHIFT]; + *val = temp - hyst; + return 0; + case hwmon_temp_min_alarm: + *val = (data->temp[t_input] >> JC42_ALARM_MIN_BIT) & 1; + return 0; + case hwmon_temp_max_alarm: + *val = (data->temp[t_input] >> JC42_ALARM_MAX_BIT) & 1; + return 0; + case hwmon_temp_crit_alarm: + *val = (data->temp[t_input] >> JC42_ALARM_CRIT_BIT) & 1; + return 0; + default: + return -EOPNOTSUPP; + } } -static ssize_t set_temp(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +static int jc42_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct jc42_data *data = dev_get_drvdata(dev); - int err, ret = count; - int nr = attr->index; - long val; + struct i2c_client *client = data->client; + int diff, hyst; + int ret; - if (kstrtol(buf, 10, &val) < 0) - return -EINVAL; mutex_lock(&data->update_lock); - data->temp[nr] = jc42_temp_to_reg(val, data->extended); - err = i2c_smbus_write_word_swapped(data->client, temp_regs[nr], - data->temp[nr]); - if (err < 0) - ret = err; - mutex_unlock(&data->update_lock); - return ret; -} -/* - * JC42.4 compliant chips only support four hysteresis values. - * Pick best choice and go from there. - */ -static ssize_t set_temp_crit_hyst(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct jc42_data *data = dev_get_drvdata(dev); - long val; - int diff, hyst; - int err; - int ret = count; - - if (kstrtol(buf, 10, &val) < 0) - return -EINVAL; - - val = clamp_val(val, (data->extended ? JC42_TEMP_MIN_EXTENDED : - JC42_TEMP_MIN) - 6000, JC42_TEMP_MAX); - diff = jc42_temp_from_reg(data->temp[t_crit]) - val; - - hyst = 0; - if (diff > 0) { - if (diff < 2250) - hyst = 1; /* 1.5 degrees C */ - else if (diff < 4500) - hyst = 2; /* 3.0 degrees C */ - else - hyst = 3; /* 6.0 degrees C */ + switch (attr) { + case hwmon_temp_min: + data->temp[t_min] = jc42_temp_to_reg(val, data->extended); + ret = i2c_smbus_write_word_swapped(client, temp_regs[t_min], + data->temp[t_min]); + break; + case hwmon_temp_max: + data->temp[t_max] = jc42_temp_to_reg(val, data->extended); + ret = i2c_smbus_write_word_swapped(client, temp_regs[t_max], + data->temp[t_max]); + break; + case hwmon_temp_crit: + data->temp[t_crit] = jc42_temp_to_reg(val, data->extended); + ret = i2c_smbus_write_word_swapped(client, temp_regs[t_crit], + data->temp[t_crit]); + break; + case hwmon_temp_crit_hyst: + /* + * JC42.4 compliant chips only support four hysteresis values. + * Pick best choice and go from there. + */ + val = clamp_val(val, (data->extended ? JC42_TEMP_MIN_EXTENDED + : JC42_TEMP_MIN) - 6000, + JC42_TEMP_MAX); + diff = jc42_temp_from_reg(data->temp[t_crit]) - val; + hyst = 0; + if (diff > 0) { + if (diff < 2250) + hyst = 1; /* 1.5 degrees C */ + else if (diff < 4500) + hyst = 2; /* 3.0 degrees C */ + else + hyst = 3; /* 6.0 degrees C */ + } + data->config = (data->config & ~JC42_CFG_HYST_MASK) | + (hyst << JC42_CFG_HYST_SHIFT); + ret = i2c_smbus_write_word_swapped(data->client, + JC42_REG_CONFIG, + data->config); + break; + default: + ret = -EOPNOTSUPP; + break; } - mutex_lock(&data->update_lock); - data->config = (data->config & ~JC42_CFG_HYST_MASK) - | (hyst << JC42_CFG_HYST_SHIFT); - err = i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG, - data->config); - if (err < 0) - ret = err; mutex_unlock(&data->update_lock); - return ret; -} -static ssize_t show_alarm(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u16 bit = to_sensor_dev_attr(attr)->index; - struct jc42_data *data = jc42_update_device(dev); - u16 val; - - if (IS_ERR(data)) - return PTR_ERR(data); - - val = data->temp[t_input]; - if (bit != JC42_ALARM_CRIT_BIT && (data->config & JC42_CFG_CRIT_ONLY)) - val = 0; - return sprintf(buf, "%u\n", (val >> bit) & 1); + return ret; } -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, t_input); -static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, set_temp, t_crit); -static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp, set_temp, t_min); -static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, set_temp, t_max); - -static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_hyst, - set_temp_crit_hyst, t_crit); -static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp_hyst, NULL, t_max); - -static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, - JC42_ALARM_CRIT_BIT); -static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, - JC42_ALARM_MIN_BIT); -static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, - JC42_ALARM_MAX_BIT); - -static struct attribute *jc42_attributes[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp1_crit.dev_attr.attr, - &sensor_dev_attr_temp1_min.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - NULL -}; - -static umode_t jc42_attribute_mode(struct kobject *kobj, - struct attribute *attr, int index) +static umode_t jc42_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) { - struct device *dev = container_of(kobj, struct device, kobj); - struct jc42_data *data = dev_get_drvdata(dev); + const struct jc42_data *data = _data; unsigned int config = data->config; - bool readonly; - - if (attr == &sensor_dev_attr_temp1_crit.dev_attr.attr) - readonly = config & JC42_CFG_TCRIT_LOCK; - else if (attr == &sensor_dev_attr_temp1_min.dev_attr.attr || - attr == &sensor_dev_attr_temp1_max.dev_attr.attr) - readonly = config & JC42_CFG_EVENT_LOCK; - else if (attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr) - readonly = config & (JC42_CFG_EVENT_LOCK | JC42_CFG_TCRIT_LOCK); - else - readonly = true; - - return S_IRUGO | (readonly ? 0 : S_IWUSR); + umode_t mode = S_IRUGO; + + switch (attr) { + case hwmon_temp_min: + case hwmon_temp_max: + if (!(config & JC42_CFG_EVENT_LOCK)) + mode |= S_IWUSR; + break; + case hwmon_temp_crit: + if (!(config & JC42_CFG_TCRIT_LOCK)) + mode |= S_IWUSR; + break; + case hwmon_temp_crit_hyst: + if (!(config & (JC42_CFG_EVENT_LOCK | JC42_CFG_TCRIT_LOCK))) + mode |= S_IWUSR; + break; + case hwmon_temp_input: + case hwmon_temp_max_hyst: + case hwmon_temp_min_alarm: + case hwmon_temp_max_alarm: + case hwmon_temp_crit_alarm: + break; + default: + mode = 0; + break; + } + return mode; } -static const struct attribute_group jc42_group = { - .attrs = jc42_attributes, - .is_visible = jc42_attribute_mode, -}; -__ATTRIBUTE_GROUPS(jc42); - /* Return 0 if detection is successful, -ENODEV otherwise */ static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info) { @@ -450,6 +427,34 @@ static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info) return -ENODEV; } +static const u32 jc42_temp_config[] = { + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_MAX_HYST | HWMON_T_CRIT_HYST | + HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM, + 0 +}; + +static const struct hwmon_channel_info jc42_temp = { + .type = hwmon_temp, + .config = jc42_temp_config, +}; + +static const struct hwmon_channel_info *jc42_info[] = { + &jc42_temp, + NULL +}; + +static const struct hwmon_ops jc42_hwmon_ops = { + .is_visible = jc42_is_visible, + .read = jc42_read, + .write = jc42_write, +}; + +static const struct hwmon_chip_info jc42_chip_info = { + .ops = &jc42_hwmon_ops, + .info = jc42_info, +}; + static int jc42_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; @@ -482,9 +487,9 @@ static int jc42_probe(struct i2c_client *client, const struct i2c_device_id *id) } data->config = config; - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, - jc42_groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, &jc42_chip_info, + NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index 92f9d4bbf597..eff3b24d8473 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -28,7 +28,6 @@ #include <linux/err.h> #include <linux/of.h> #include <linux/regmap.h> -#include <linux/thermal.h> #include "lm75.h" @@ -88,56 +87,75 @@ static inline long lm75_reg_to_mc(s16 temp, u8 resolution) return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8); } -/* sysfs attributes for hwmon */ - -static int lm75_read_temp(void *dev, int *temp) +static int lm75_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) { struct lm75_data *data = dev_get_drvdata(dev); - unsigned int _temp; - int err; - - err = regmap_read(data->regmap, LM75_REG_TEMP, &_temp); - if (err < 0) - return err; - - *temp = lm75_reg_to_mc(_temp, data->resolution); - + unsigned int regval; + int err, reg; + + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + *val = data->sample_time; + break;; + default: + return -EINVAL; + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + reg = LM75_REG_TEMP; + break; + case hwmon_temp_max: + reg = LM75_REG_MAX; + break; + case hwmon_temp_max_hyst: + reg = LM75_REG_HYST; + break; + default: + return -EINVAL; + } + err = regmap_read(data->regmap, reg, ®val); + if (err < 0) + return err; + + *val = lm75_reg_to_mc(regval, data->resolution); + break; + default: + return -EINVAL; + } return 0; } -static ssize_t show_temp(struct device *dev, struct device_attribute *da, - char *buf) +static int lm75_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long temp) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct lm75_data *data = dev_get_drvdata(dev); - unsigned int temp = 0; - int err; - - err = regmap_read(data->regmap, attr->index, &temp); - if (err < 0) - return err; - - return sprintf(buf, "%ld\n", lm75_reg_to_mc(temp, data->resolution)); -} - -static ssize_t set_temp(struct device *dev, struct device_attribute *da, - const char *buf, size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct lm75_data *data = dev_get_drvdata(dev); - long temp; - int error; u8 resolution; + int reg; + + if (type != hwmon_temp) + return -EINVAL; - error = kstrtol(buf, 10, &temp); - if (error) - return error; + switch (attr) { + case hwmon_temp_max: + reg = LM75_REG_MAX; + break; + case hwmon_temp_max_hyst: + reg = LM75_REG_HYST; + break; + default: + return -EINVAL; + } /* * Resolution of limit registers is assumed to be the same as the * temperature input register resolution unless given explicitly. */ - if (attr->index && data->resolution_limits) + if (data->resolution_limits) resolution = data->resolution_limits; else resolution = data->resolution; @@ -145,45 +163,77 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da, temp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX); temp = DIV_ROUND_CLOSEST(temp << (resolution - 8), 1000) << (16 - resolution); - error = regmap_write(data->regmap, attr->index, temp); - if (error < 0) - return error; - return count; + return regmap_write(data->regmap, reg, temp); } -static ssize_t show_update_interval(struct device *dev, - struct device_attribute *da, char *buf) +static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) { - struct lm75_data *data = dev_get_drvdata(dev); - - return sprintf(buf, "%u\n", data->sample_time); + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + return S_IRUGO; + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + return S_IRUGO; + case hwmon_temp_max: + case hwmon_temp_max_hyst: + return S_IRUGO | S_IWUSR; + } + break; + default: + break; + } + return 0; } -static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, - show_temp, set_temp, LM75_REG_MAX); -static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, - show_temp, set_temp, LM75_REG_HYST); -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, LM75_REG_TEMP); -static DEVICE_ATTR(update_interval, S_IRUGO, show_update_interval, NULL); +/*-----------------------------------------------------------------------*/ -static struct attribute *lm75_attrs[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, - &dev_attr_update_interval.attr, +/* device probe and removal */ - NULL +/* chip configuration */ + +static const u32 lm75_chip_config[] = { + HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL, + 0 }; -ATTRIBUTE_GROUPS(lm75); -static const struct thermal_zone_of_device_ops lm75_of_thermal_ops = { - .get_temp = lm75_read_temp, +static const struct hwmon_channel_info lm75_chip = { + .type = hwmon_chip, + .config = lm75_chip_config, }; -/*-----------------------------------------------------------------------*/ +static const u32 lm75_temp_config[] = { + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST, + 0 +}; -/* device probe and removal */ +static const struct hwmon_channel_info lm75_temp = { + .type = hwmon_temp, + .config = lm75_temp_config, +}; + +static const struct hwmon_channel_info *lm75_info[] = { + &lm75_chip, + &lm75_temp, + NULL +}; + +static const struct hwmon_ops lm75_hwmon_ops = { + .is_visible = lm75_is_visible, + .read = lm75_read, + .write = lm75_write, +}; + +static const struct hwmon_chip_info lm75_chip_info = { + .ops = &lm75_hwmon_ops, + .info = lm75_info, +}; static bool lm75_is_writeable_reg(struct device *dev, unsigned int reg) { @@ -337,15 +387,12 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) dev_dbg(dev, "Config %02x\n", new); - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, lm75_groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, &lm75_chip_info, + NULL); if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); - devm_thermal_zone_of_sensor_register(hwmon_dev, 0, - hwmon_dev, - &lm75_of_thermal_ops); - dev_info(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), client->name); return 0; diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 496e771b363f..322ed9272811 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -89,7 +89,6 @@ #include <linux/slab.h> #include <linux/jiffies.h> #include <linux/i2c.h> -#include <linux/hwmon-sysfs.h> #include <linux/hwmon.h> #include <linux/err.h> #include <linux/mutex.h> @@ -326,7 +325,7 @@ static const struct lm90_params lm90_params[] = { .alert_alarms = 0x7c, .max_convrate = 9, .reg_local_ext = TMP451_REG_R_LOCAL_TEMPL, - } + }, }; /* @@ -365,7 +364,10 @@ enum lm90_temp11_reg_index { struct lm90_data { struct i2c_client *client; - const struct attribute_group *groups[6]; + u32 channel_config[4]; + struct hwmon_channel_info temp_info; + const struct hwmon_channel_info *info[3]; + struct hwmon_chip_info chip; struct mutex update_lock; bool valid; /* true if register values are valid */ unsigned long last_updated; /* in jiffies */ @@ -489,11 +491,11 @@ static inline int lm90_select_remote_channel(struct i2c_client *client, * client->update_lock must be held when calling this function (unless we are * in detection or initialization steps). */ -static void lm90_set_convrate(struct i2c_client *client, struct lm90_data *data, - unsigned int interval) +static int lm90_set_convrate(struct i2c_client *client, struct lm90_data *data, + unsigned int interval) { - int i; unsigned int update_interval; + int i, err; /* Shift calculations to avoid rounding errors */ interval <<= 6; @@ -504,8 +506,9 @@ static void lm90_set_convrate(struct i2c_client *client, struct lm90_data *data, if (interval >= update_interval * 3 / 4) break; - i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, i); + err = i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, i); data->update_interval = DIV_ROUND_CLOSEST(update_interval, 64); + return err; } static int lm90_update_limits(struct device *dev) @@ -604,19 +607,17 @@ static int lm90_update_limits(struct device *dev) return 0; } -static struct lm90_data *lm90_update_device(struct device *dev) +static int lm90_update_device(struct device *dev) { struct lm90_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; unsigned long next_update; - int val = 0; - - mutex_lock(&data->update_lock); + int val; if (!data->valid) { val = lm90_update_limits(dev); if (val < 0) - goto error; + return val; } next_update = data->last_updated + @@ -628,53 +629,55 @@ static struct lm90_data *lm90_update_device(struct device *dev) val = lm90_read_reg(client, LM90_REG_R_LOCAL_LOW); if (val < 0) - goto error; + return val; data->temp8[LOCAL_LOW] = val; val = lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH); if (val < 0) - goto error; + return val; data->temp8[LOCAL_HIGH] = val; if (data->reg_local_ext) { val = lm90_read16(client, LM90_REG_R_LOCAL_TEMP, data->reg_local_ext); if (val < 0) - goto error; + return val; data->temp11[LOCAL_TEMP] = val; } else { val = lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP); if (val < 0) - goto error; + return val; data->temp11[LOCAL_TEMP] = val << 8; } val = lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, LM90_REG_R_REMOTE_TEMPL); if (val < 0) - goto error; + return val; data->temp11[REMOTE_TEMP] = val; val = lm90_read_reg(client, LM90_REG_R_STATUS); if (val < 0) - goto error; + return val; data->alarms = val; /* lower 8 bit of alarms */ if (data->kind == max6696) { val = lm90_select_remote_channel(client, data, 1); if (val < 0) - goto error; + return val; val = lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, LM90_REG_R_REMOTE_TEMPL); - if (val < 0) - goto error; + if (val < 0) { + lm90_select_remote_channel(client, data, 0); + return val; + } data->temp11[REMOTE2_TEMP] = val; lm90_select_remote_channel(client, data, 0); val = lm90_read_reg(client, MAX6696_REG_R_STATUS2); if (val < 0) - goto error; + return val; data->alarms |= val << 8; } @@ -686,7 +689,7 @@ static struct lm90_data *lm90_update_device(struct device *dev) !(data->alarms & data->alert_alarms)) { val = lm90_read_reg(client, LM90_REG_R_CONFIG1); if (val < 0) - goto error; + return val; if (val & 0x80) { dev_dbg(&client->dev, "Re-enabling ALERT#\n"); @@ -700,13 +703,7 @@ static struct lm90_data *lm90_update_device(struct device *dev) data->valid = true; } -error: - mutex_unlock(&data->update_lock); - - if (val < 0) - return ERR_PTR(val); - - return data; + return 0; } /* @@ -832,52 +829,19 @@ static u16 temp_to_u16_adt7461(struct lm90_data *data, long val) return (val + 125) / 250 * 64; } -/* - * Sysfs stuff - */ - -static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, - char *buf) +/* pec used for ADM1032 only */ +static ssize_t show_pec(struct device *dev, struct device_attribute *dummy, + char *buf) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct lm90_data *data = lm90_update_device(dev); - int temp; - - if (IS_ERR(data)) - return PTR_ERR(data); - - if (data->kind == adt7461 || data->kind == tmp451) - temp = temp_from_u8_adt7461(data, data->temp8[attr->index]); - else if (data->kind == max6646) - temp = temp_from_u8(data->temp8[attr->index]); - else - temp = temp_from_s8(data->temp8[attr->index]); - - /* +16 degrees offset for temp2 for the LM99 */ - if (data->kind == lm99 && attr->index == 3) - temp += 16000; + struct i2c_client *client = to_i2c_client(dev); - return sprintf(buf, "%d\n", temp); + return sprintf(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC)); } -static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +static ssize_t set_pec(struct device *dev, struct device_attribute *dummy, + const char *buf, size_t count) { - static const u8 reg[TEMP8_REG_NUM] = { - LM90_REG_W_LOCAL_LOW, - LM90_REG_W_LOCAL_HIGH, - LM90_REG_W_LOCAL_CRIT, - LM90_REG_W_REMOTE_CRIT, - MAX6659_REG_W_LOCAL_EMERG, - MAX6659_REG_W_REMOTE_EMERG, - LM90_REG_W_REMOTE_CRIT, - MAX6659_REG_W_REMOTE_EMERG, - }; - - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct lm90_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int nr = attr->index; + struct i2c_client *client = to_i2c_client(dev); long val; int err; @@ -885,82 +849,61 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, if (err < 0) return err; - /* +16 degrees offset for temp2 for the LM99 */ - if (data->kind == lm99 && attr->index == 3) - val -= 16000; - - mutex_lock(&data->update_lock); - if (data->kind == adt7461 || data->kind == tmp451) - data->temp8[nr] = temp_to_u8_adt7461(data, val); - else if (data->kind == max6646) - data->temp8[nr] = temp_to_u8(val); - else - data->temp8[nr] = temp_to_s8(val); - - lm90_select_remote_channel(client, data, nr >= 6); - i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); - lm90_select_remote_channel(client, data, 0); + switch (val) { + case 0: + client->flags &= ~I2C_CLIENT_PEC; + break; + case 1: + client->flags |= I2C_CLIENT_PEC; + break; + default: + return -EINVAL; + } - mutex_unlock(&data->update_lock); return count; } -static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, - char *buf) +static DEVICE_ATTR(pec, S_IWUSR | S_IRUGO, show_pec, set_pec); + +static int lm90_get_temp11(struct lm90_data *data, int index) { - struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); - struct lm90_data *data = lm90_update_device(dev); + s16 temp11 = data->temp11[index]; int temp; - if (IS_ERR(data)) - return PTR_ERR(data); - if (data->kind == adt7461 || data->kind == tmp451) - temp = temp_from_u16_adt7461(data, data->temp11[attr->index]); + temp = temp_from_u16_adt7461(data, temp11); else if (data->kind == max6646) - temp = temp_from_u16(data->temp11[attr->index]); + temp = temp_from_u16(temp11); else - temp = temp_from_s16(data->temp11[attr->index]); + temp = temp_from_s16(temp11); /* +16 degrees offset for temp2 for the LM99 */ - if (data->kind == lm99 && attr->index <= 2) + if (data->kind == lm99 && index <= 2) temp += 16000; - return sprintf(buf, "%d\n", temp); + return temp; } -static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +static int lm90_set_temp11(struct lm90_data *data, int index, long val) { - struct { + static struct reg { u8 high; u8 low; - int channel; - } reg[5] = { - { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL, 0 }, - { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL, 0 }, - { LM90_REG_W_REMOTE_OFFSH, LM90_REG_W_REMOTE_OFFSL, 0 }, - { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL, 1 }, - { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL, 1 } + } reg[] = { + [REMOTE_LOW] = { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL }, + [REMOTE_HIGH] = { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL }, + [REMOTE_OFFSET] = { LM90_REG_W_REMOTE_OFFSH, LM90_REG_W_REMOTE_OFFSL }, + [REMOTE2_LOW] = { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL }, + [REMOTE2_HIGH] = { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL } }; - - struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); - struct lm90_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; - int nr = attr->nr; - int index = attr->index; - long val; + struct reg *regp = ®[index]; int err; - err = kstrtol(buf, 10, &val); - if (err < 0) - return err; - /* +16 degrees offset for temp2 for the LM99 */ if (data->kind == lm99 && index <= 2) val -= 16000; - mutex_lock(&data->update_lock); if (data->kind == adt7461 || data->kind == tmp451) data->temp11[index] = temp_to_u16_adt7461(data, val); else if (data->kind == max6646) @@ -970,317 +913,383 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, else data->temp11[index] = temp_to_s8(val) << 8; - lm90_select_remote_channel(client, data, reg[nr].channel); - i2c_smbus_write_byte_data(client, reg[nr].high, + lm90_select_remote_channel(client, data, index >= 3); + err = i2c_smbus_write_byte_data(client, regp->high, data->temp11[index] >> 8); + if (err < 0) + return err; if (data->flags & LM90_HAVE_REM_LIMIT_EXT) - i2c_smbus_write_byte_data(client, reg[nr].low, - data->temp11[index] & 0xff); - lm90_select_remote_channel(client, data, 0); + err = i2c_smbus_write_byte_data(client, regp->low, + data->temp11[index] & 0xff); - mutex_unlock(&data->update_lock); - return count; + lm90_select_remote_channel(client, data, 0); + return err; } -static ssize_t show_temphyst(struct device *dev, - struct device_attribute *devattr, - char *buf) +static int lm90_get_temp8(struct lm90_data *data, int index) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct lm90_data *data = lm90_update_device(dev); + s8 temp8 = data->temp8[index]; int temp; - if (IS_ERR(data)) - return PTR_ERR(data); - if (data->kind == adt7461 || data->kind == tmp451) - temp = temp_from_u8_adt7461(data, data->temp8[attr->index]); + temp = temp_from_u8_adt7461(data, temp8); else if (data->kind == max6646) - temp = temp_from_u8(data->temp8[attr->index]); + temp = temp_from_u8(temp8); else - temp = temp_from_s8(data->temp8[attr->index]); + temp = temp_from_s8(temp8); /* +16 degrees offset for temp2 for the LM99 */ - if (data->kind == lm99 && attr->index == 3) + if (data->kind == lm99 && index == 3) temp += 16000; - return sprintf(buf, "%d\n", temp - temp_from_s8(data->temp_hyst)); + return temp; } -static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, - const char *buf, size_t count) +static int lm90_set_temp8(struct lm90_data *data, int index, long val) { - struct lm90_data *data = dev_get_drvdata(dev); + static const u8 reg[TEMP8_REG_NUM] = { + LM90_REG_W_LOCAL_LOW, + LM90_REG_W_LOCAL_HIGH, + LM90_REG_W_LOCAL_CRIT, + LM90_REG_W_REMOTE_CRIT, + MAX6659_REG_W_LOCAL_EMERG, + MAX6659_REG_W_REMOTE_EMERG, + LM90_REG_W_REMOTE_CRIT, + MAX6659_REG_W_REMOTE_EMERG, + }; struct i2c_client *client = data->client; - long val; int err; - int temp; - err = kstrtol(buf, 10, &val); - if (err < 0) - return err; + /* +16 degrees offset for temp2 for the LM99 */ + if (data->kind == lm99 && index == 3) + val -= 16000; - mutex_lock(&data->update_lock); if (data->kind == adt7461 || data->kind == tmp451) - temp = temp_from_u8_adt7461(data, data->temp8[LOCAL_CRIT]); + data->temp8[index] = temp_to_u8_adt7461(data, val); else if (data->kind == max6646) - temp = temp_from_u8(data->temp8[LOCAL_CRIT]); + data->temp8[index] = temp_to_u8(val); else - temp = temp_from_s8(data->temp8[LOCAL_CRIT]); + data->temp8[index] = temp_to_s8(val); - data->temp_hyst = hyst_to_reg(temp - val); - i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, - data->temp_hyst); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy, - char *buf) -{ - struct lm90_data *data = lm90_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); + lm90_select_remote_channel(client, data, index >= 6); + err = i2c_smbus_write_byte_data(client, reg[index], data->temp8[index]); + lm90_select_remote_channel(client, data, 0); - return sprintf(buf, "%d\n", data->alarms); + return err; } -static ssize_t show_alarm(struct device *dev, struct device_attribute - *devattr, char *buf) +static int lm90_get_temphyst(struct lm90_data *data, int index) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct lm90_data *data = lm90_update_device(dev); - int bitnr = attr->index; - - if (IS_ERR(data)) - return PTR_ERR(data); + int temp; - return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); -} + if (data->kind == adt7461 || data->kind == tmp451) + temp = temp_from_u8_adt7461(data, data->temp8[index]); + else if (data->kind == max6646) + temp = temp_from_u8(data->temp8[index]); + else + temp = temp_from_s8(data->temp8[index]); -static ssize_t show_update_interval(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct lm90_data *data = dev_get_drvdata(dev); + /* +16 degrees offset for temp2 for the LM99 */ + if (data->kind == lm99 && index == 3) + temp += 16000; - return sprintf(buf, "%u\n", data->update_interval); + return temp - temp_from_s8(data->temp_hyst); } -static ssize_t set_update_interval(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int lm90_set_temphyst(struct lm90_data *data, long val) { - struct lm90_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; - unsigned long val; + int temp; int err; - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - mutex_lock(&data->update_lock); - lm90_set_convrate(client, data, clamp_val(val, 0, 100000)); - mutex_unlock(&data->update_lock); + if (data->kind == adt7461 || data->kind == tmp451) + temp = temp_from_u8_adt7461(data, data->temp8[LOCAL_CRIT]); + else if (data->kind == max6646) + temp = temp_from_u8(data->temp8[LOCAL_CRIT]); + else + temp = temp_from_s8(data->temp8[LOCAL_CRIT]); - return count; + data->temp_hyst = hyst_to_reg(temp - val); + err = i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, + data->temp_hyst); + return err; } -static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp11, NULL, - 0, LOCAL_TEMP); -static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp11, NULL, - 0, REMOTE_TEMP); -static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, LOCAL_LOW); -static SENSOR_DEVICE_ATTR_2(temp2_min, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 0, REMOTE_LOW); -static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, LOCAL_HIGH); -static SENSOR_DEVICE_ATTR_2(temp2_max, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 1, REMOTE_HIGH); -static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, LOCAL_CRIT); -static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, REMOTE_CRIT); -static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst, - set_temphyst, LOCAL_CRIT); -static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, - REMOTE_CRIT); -static SENSOR_DEVICE_ATTR_2(temp2_offset, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 2, REMOTE_OFFSET); - -/* Individual alarm files */ -static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0); -static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1); -static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 2); -static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3); -static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4); -static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 5); -static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6); -/* Raw alarm file for compatibility */ -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); - -static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval, - set_update_interval); - -static struct attribute *lm90_attributes[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp1_min.dev_attr.attr, - &sensor_dev_attr_temp2_min.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp1_crit.dev_attr.attr, - &sensor_dev_attr_temp2_crit.dev_attr.attr, - &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, - &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, - - &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_fault.dev_attr.attr, - &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - &dev_attr_alarms.attr, - &dev_attr_update_interval.attr, - NULL +static const u8 lm90_temp_index[3] = { + LOCAL_TEMP, REMOTE_TEMP, REMOTE2_TEMP }; -static const struct attribute_group lm90_group = { - .attrs = lm90_attributes, +static const u8 lm90_temp_min_index[3] = { + LOCAL_LOW, REMOTE_LOW, REMOTE2_LOW }; -static struct attribute *lm90_temp2_offset_attributes[] = { - &sensor_dev_attr_temp2_offset.dev_attr.attr, - NULL +static const u8 lm90_temp_max_index[3] = { + LOCAL_HIGH, REMOTE_HIGH, REMOTE2_HIGH }; -static const struct attribute_group lm90_temp2_offset_group = { - .attrs = lm90_temp2_offset_attributes, +static const u8 lm90_temp_crit_index[3] = { + LOCAL_CRIT, REMOTE_CRIT, REMOTE2_CRIT }; -/* - * Additional attributes for devices with emergency sensors - */ -static SENSOR_DEVICE_ATTR(temp1_emergency, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, LOCAL_EMERG); -static SENSOR_DEVICE_ATTR(temp2_emergency, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, REMOTE_EMERG); -static SENSOR_DEVICE_ATTR(temp1_emergency_hyst, S_IRUGO, show_temphyst, - NULL, LOCAL_EMERG); -static SENSOR_DEVICE_ATTR(temp2_emergency_hyst, S_IRUGO, show_temphyst, - NULL, REMOTE_EMERG); - -static struct attribute *lm90_emergency_attributes[] = { - &sensor_dev_attr_temp1_emergency.dev_attr.attr, - &sensor_dev_attr_temp2_emergency.dev_attr.attr, - &sensor_dev_attr_temp1_emergency_hyst.dev_attr.attr, - &sensor_dev_attr_temp2_emergency_hyst.dev_attr.attr, - NULL +static const u8 lm90_temp_emerg_index[3] = { + LOCAL_EMERG, REMOTE_EMERG, REMOTE2_EMERG }; -static const struct attribute_group lm90_emergency_group = { - .attrs = lm90_emergency_attributes, -}; +static const u8 lm90_min_alarm_bits[3] = { 5, 3, 11 }; +static const u8 lm90_max_alarm_bits[3] = { 0, 4, 12 }; +static const u8 lm90_crit_alarm_bits[3] = { 0, 1, 9 }; +static const u8 lm90_emergency_alarm_bits[3] = { 15, 13, 14 }; +static const u8 lm90_fault_bits[3] = { 0, 2, 10 }; -static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO, show_alarm, NULL, 15); -static SENSOR_DEVICE_ATTR(temp2_emergency_alarm, S_IRUGO, show_alarm, NULL, 13); +static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val) +{ + struct lm90_data *data = dev_get_drvdata(dev); + int err; -static struct attribute *lm90_emergency_alarm_attributes[] = { - &sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_emergency_alarm.dev_attr.attr, - NULL -}; + mutex_lock(&data->update_lock); + err = lm90_update_device(dev); + mutex_unlock(&data->update_lock); + if (err) + return err; -static const struct attribute_group lm90_emergency_alarm_group = { - .attrs = lm90_emergency_alarm_attributes, -}; + switch (attr) { + case hwmon_temp_input: + *val = lm90_get_temp11(data, lm90_temp_index[channel]); + break; + case hwmon_temp_min_alarm: + *val = (data->alarms >> lm90_min_alarm_bits[channel]) & 1; + break; + case hwmon_temp_max_alarm: + *val = (data->alarms >> lm90_max_alarm_bits[channel]) & 1; + break; + case hwmon_temp_crit_alarm: + *val = (data->alarms >> lm90_crit_alarm_bits[channel]) & 1; + break; + case hwmon_temp_emergency_alarm: + *val = (data->alarms >> lm90_emergency_alarm_bits[channel]) & 1; + break; + case hwmon_temp_fault: + *val = (data->alarms >> lm90_fault_bits[channel]) & 1; + break; + case hwmon_temp_min: + if (channel == 0) + *val = lm90_get_temp8(data, + lm90_temp_min_index[channel]); + else + *val = lm90_get_temp11(data, + lm90_temp_min_index[channel]); + break; + case hwmon_temp_max: + if (channel == 0) + *val = lm90_get_temp8(data, + lm90_temp_max_index[channel]); + else + *val = lm90_get_temp11(data, + lm90_temp_max_index[channel]); + break; + case hwmon_temp_crit: + *val = lm90_get_temp8(data, lm90_temp_crit_index[channel]); + break; + case hwmon_temp_crit_hyst: + *val = lm90_get_temphyst(data, lm90_temp_crit_index[channel]); + break; + case hwmon_temp_emergency: + *val = lm90_get_temp8(data, lm90_temp_emerg_index[channel]); + break; + case hwmon_temp_emergency_hyst: + *val = lm90_get_temphyst(data, lm90_temp_emerg_index[channel]); + break; + case hwmon_temp_offset: + *val = lm90_get_temp11(data, REMOTE_OFFSET); + break; + default: + return -EOPNOTSUPP; + } + return 0; +} -/* - * Additional attributes for devices with 3 temperature sensors - */ -static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp11, NULL, - 0, REMOTE2_TEMP); -static SENSOR_DEVICE_ATTR_2(temp3_min, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 3, REMOTE2_LOW); -static SENSOR_DEVICE_ATTR_2(temp3_max, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 4, REMOTE2_HIGH); -static SENSOR_DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, REMOTE2_CRIT); -static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_temphyst, NULL, - REMOTE2_CRIT); -static SENSOR_DEVICE_ATTR(temp3_emergency, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, REMOTE2_EMERG); -static SENSOR_DEVICE_ATTR(temp3_emergency_hyst, S_IRUGO, show_temphyst, - NULL, REMOTE2_EMERG); - -static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, 9); -static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 10); -static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_alarm, NULL, 11); -static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 12); -static SENSOR_DEVICE_ATTR(temp3_emergency_alarm, S_IRUGO, show_alarm, NULL, 14); - -static struct attribute *lm90_temp3_attributes[] = { - &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp3_min.dev_attr.attr, - &sensor_dev_attr_temp3_max.dev_attr.attr, - &sensor_dev_attr_temp3_crit.dev_attr.attr, - &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr, - &sensor_dev_attr_temp3_emergency.dev_attr.attr, - &sensor_dev_attr_temp3_emergency_hyst.dev_attr.attr, - - &sensor_dev_attr_temp3_fault.dev_attr.attr, - &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_emergency_alarm.dev_attr.attr, - NULL -}; +static int lm90_temp_write(struct device *dev, u32 attr, int channel, long val) +{ + struct lm90_data *data = dev_get_drvdata(dev); + int err; -static const struct attribute_group lm90_temp3_group = { - .attrs = lm90_temp3_attributes, -}; + mutex_lock(&data->update_lock); -/* pec used for ADM1032 only */ -static ssize_t show_pec(struct device *dev, struct device_attribute *dummy, - char *buf) + err = lm90_update_device(dev); + if (err) + goto error; + + switch (attr) { + case hwmon_temp_min: + if (channel == 0) + err = lm90_set_temp8(data, + lm90_temp_min_index[channel], + val); + else + err = lm90_set_temp11(data, + lm90_temp_min_index[channel], + val); + break; + case hwmon_temp_max: + if (channel == 0) + err = lm90_set_temp8(data, + lm90_temp_max_index[channel], + val); + else + err = lm90_set_temp11(data, + lm90_temp_max_index[channel], + val); + break; + case hwmon_temp_crit: + err = lm90_set_temp8(data, lm90_temp_crit_index[channel], val); + break; + case hwmon_temp_crit_hyst: + err = lm90_set_temphyst(data, val); + break; + case hwmon_temp_emergency: + err = lm90_set_temp8(data, lm90_temp_emerg_index[channel], val); + break; + case hwmon_temp_offset: + err = lm90_set_temp11(data, REMOTE_OFFSET, val); + break; + default: + err = -EOPNOTSUPP; + break; + } +error: + mutex_unlock(&data->update_lock); + + return err; +} + +static umode_t lm90_temp_is_visible(const void *data, u32 attr, int channel) { - struct i2c_client *client = to_i2c_client(dev); - return sprintf(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC)); + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_min_alarm: + case hwmon_temp_max_alarm: + case hwmon_temp_crit_alarm: + case hwmon_temp_emergency_alarm: + case hwmon_temp_emergency_hyst: + case hwmon_temp_fault: + return S_IRUGO; + case hwmon_temp_min: + case hwmon_temp_max: + case hwmon_temp_crit: + case hwmon_temp_emergency: + case hwmon_temp_offset: + return S_IRUGO | S_IWUSR; + case hwmon_temp_crit_hyst: + if (channel == 0) + return S_IRUGO | S_IWUSR; + return S_IRUGO; + default: + return 0; + } } -static ssize_t set_pec(struct device *dev, struct device_attribute *dummy, - const char *buf, size_t count) +static int lm90_chip_read(struct device *dev, u32 attr, int channel, long *val) { - struct i2c_client *client = to_i2c_client(dev); - long val; + struct lm90_data *data = dev_get_drvdata(dev); int err; - err = kstrtol(buf, 10, &val); - if (err < 0) + mutex_lock(&data->update_lock); + err = lm90_update_device(dev); + mutex_unlock(&data->update_lock); + if (err) return err; - switch (val) { - case 0: - client->flags &= ~I2C_CLIENT_PEC; + switch (attr) { + case hwmon_chip_update_interval: + *val = data->update_interval; break; - case 1: - client->flags |= I2C_CLIENT_PEC; + case hwmon_chip_alarms: + *val = data->alarms; break; default: - return -EINVAL; + return -EOPNOTSUPP; } - return count; + return 0; } -static DEVICE_ATTR(pec, S_IWUSR | S_IRUGO, show_pec, set_pec); +static int lm90_chip_write(struct device *dev, u32 attr, int channel, long val) +{ + struct lm90_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int err; -/* - * Real code - */ + mutex_lock(&data->update_lock); + + err = lm90_update_device(dev); + if (err) + goto error; + + switch (attr) { + case hwmon_chip_update_interval: + err = lm90_set_convrate(client, data, + clamp_val(val, 0, 100000)); + break; + default: + err = -EOPNOTSUPP; + break; + } +error: + mutex_unlock(&data->update_lock); + + return err; +} + +static umode_t lm90_chip_is_visible(const void *data, u32 attr, int channel) +{ + switch (attr) { + case hwmon_chip_update_interval: + return S_IRUGO | S_IWUSR; + case hwmon_chip_alarms: + return S_IRUGO; + default: + return 0; + } +} + +static int lm90_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_chip: + return lm90_chip_read(dev, attr, channel, val); + case hwmon_temp: + return lm90_temp_read(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int lm90_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_chip: + return lm90_chip_write(dev, attr, channel, val); + case hwmon_temp: + return lm90_temp_write(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static umode_t lm90_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_chip: + return lm90_chip_is_visible(data, attr, channel); + case hwmon_temp: + return lm90_temp_is_visible(data, attr, channel); + default: + return 0; + } +} /* Return 0 if detection is successful, -ENODEV otherwise */ static int lm90_detect(struct i2c_client *client, @@ -1617,15 +1626,32 @@ static void lm90_regulator_disable(void *regulator) regulator_disable(regulator); } +static const u32 lm90_chip_config[] = { + HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL | HWMON_C_ALARMS, + 0 +}; + +static const struct hwmon_channel_info lm90_chip_info = { + .type = hwmon_chip, + .config = lm90_chip_config, +}; + + +static const struct hwmon_ops lm90_ops = { + .is_visible = lm90_is_visible, + .read = lm90_read, + .write = lm90_write, +}; + static int lm90_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; struct i2c_adapter *adapter = to_i2c_adapter(dev->parent); - struct lm90_data *data; + struct hwmon_channel_info *info; struct regulator *regulator; struct device *hwmon_dev; - int groups = 0; + struct lm90_data *data; int err; regulator = devm_regulator_get(dev, "vcc"); @@ -1665,6 +1691,49 @@ static int lm90_probe(struct i2c_client *client, /* Set chip capabilities */ data->flags = lm90_params[data->kind].flags; + + data->chip.ops = &lm90_ops; + data->chip.info = data->info; + + data->info[0] = &lm90_chip_info; + data->info[1] = &data->temp_info; + + info = &data->temp_info; + info->type = hwmon_temp; + info->config = data->channel_config; + + data->channel_config[0] = HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM; + data->channel_config[1] = HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_FAULT; + + if (data->flags & LM90_HAVE_OFFSET) + data->channel_config[1] |= HWMON_T_OFFSET; + + if (data->flags & LM90_HAVE_EMERGENCY) { + data->channel_config[0] |= HWMON_T_EMERGENCY | + HWMON_T_EMERGENCY_HYST; + data->channel_config[1] |= HWMON_T_EMERGENCY | + HWMON_T_EMERGENCY_HYST; + } + + if (data->flags & LM90_HAVE_EMERGENCY_ALARM) { + data->channel_config[0] |= HWMON_T_EMERGENCY_ALARM; + data->channel_config[1] |= HWMON_T_EMERGENCY_ALARM; + } + + if (data->flags & LM90_HAVE_TEMP3) { + data->channel_config[2] = HWMON_T_INPUT | + HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | + HWMON_T_EMERGENCY | HWMON_T_EMERGENCY_HYST | + HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | + HWMON_T_CRIT_ALARM | HWMON_T_EMERGENCY_ALARM | + HWMON_T_FAULT; + } + data->reg_local_ext = lm90_params[data->kind].reg_local_ext; /* Set maximum conversion rate */ @@ -1677,21 +1746,10 @@ static int lm90_probe(struct i2c_client *client, return err; } - /* Register sysfs hooks */ - data->groups[groups++] = &lm90_group; - - if (data->flags & LM90_HAVE_OFFSET) - data->groups[groups++] = &lm90_temp2_offset_group; - - if (data->flags & LM90_HAVE_EMERGENCY) - data->groups[groups++] = &lm90_emergency_group; - - if (data->flags & LM90_HAVE_EMERGENCY_ALARM) - data->groups[groups++] = &lm90_emergency_alarm_group; - - if (data->flags & LM90_HAVE_TEMP3) - data->groups[groups++] = &lm90_temp3_group; - + /* + * The 'pec' attribute is attached to the i2c device and thus created + * separately. + */ if (client->flags & I2C_CLIENT_PEC) { err = device_create_file(dev, &dev_attr_pec); if (err) @@ -1701,8 +1759,9 @@ static int lm90_probe(struct i2c_client *client, return err; } - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, data->groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, &data->chip, + NULL); if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index cdf19adaec79..8c573e6e9726 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -15,22 +15,17 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <linux/module.h> +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/i2c.h> #include <linux/init.h> -#include <linux/slab.h> #include <linux/jiffies.h> -#include <linux/i2c.h> #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> -#include <linux/err.h> +#include <linux/module.h> #include <linux/mutex.h> -#include <linux/sysfs.h> +#include <linux/slab.h> #define DEVNAME "lm95241" @@ -54,26 +49,25 @@ static const unsigned short normal_i2c[] = { #define LM95241_REG_RW_REMOTE_MODEL 0x30 /* LM95241 specific bitfields */ -#define CFG_STOP 0x40 -#define CFG_CR0076 0x00 -#define CFG_CR0182 0x10 -#define CFG_CR1000 0x20 -#define CFG_CR2700 0x30 -#define R1MS_SHIFT 0 -#define R2MS_SHIFT 2 -#define R1MS_MASK (0x01 << (R1MS_SHIFT)) -#define R2MS_MASK (0x01 << (R2MS_SHIFT)) -#define R1DF_SHIFT 1 -#define R2DF_SHIFT 2 -#define R1DF_MASK (0x01 << (R1DF_SHIFT)) -#define R2DF_MASK (0x01 << (R2DF_SHIFT)) -#define R1FE_MASK 0x01 -#define R2FE_MASK 0x05 -#define TT1_SHIFT 0 -#define TT2_SHIFT 4 -#define TT_OFF 0 -#define TT_ON 1 -#define TT_MASK 7 +#define CFG_STOP BIT(6) +#define CFG_CR0076 0x00 +#define CFG_CR0182 BIT(4) +#define CFG_CR1000 BIT(5) +#define CFG_CR2700 (BIT(4) | BIT(5)) +#define CFG_CRMASK (BIT(4) | BIT(5)) +#define R1MS_MASK BIT(0) +#define R2MS_MASK BIT(2) +#define R1DF_MASK BIT(1) +#define R2DF_MASK BIT(2) +#define R1FE_MASK BIT(0) +#define R2FE_MASK BIT(2) +#define R1DM BIT(0) +#define R2DM BIT(1) +#define TT1_SHIFT 0 +#define TT2_SHIFT 4 +#define TT_OFF 0 +#define TT_ON 1 +#define TT_MASK 7 #define NATSEMI_MAN_ID 0x01 #define LM95231_CHIP_ID 0xA1 #define LM95241_CHIP_ID 0xA4 @@ -91,11 +85,12 @@ static const u8 lm95241_reg_address[] = { struct lm95241_data { struct i2c_client *client; struct mutex update_lock; - unsigned long last_updated, interval; /* in jiffies */ + unsigned long last_updated; /* in jiffies */ + unsigned long interval; /* in milli-seconds */ char valid; /* zero until following fields are valid */ /* registers values */ u8 temp[ARRAY_SIZE(lm95241_reg_address)]; - u8 config, model, trutherm; + u8 status, config, model, trutherm; }; /* Conversions */ @@ -118,7 +113,8 @@ static struct lm95241_data *lm95241_update_device(struct device *dev) mutex_lock(&data->update_lock); - if (time_after(jiffies, data->last_updated + data->interval) || + if (time_after(jiffies, data->last_updated + + msecs_to_jiffies(data->interval)) || !data->valid) { int i; @@ -127,6 +123,9 @@ static struct lm95241_data *lm95241_update_device(struct device *dev) data->temp[i] = i2c_smbus_read_byte_data(client, lm95241_reg_address[i]); + + data->status = i2c_smbus_read_byte_data(client, + LM95241_REG_R_STATUS); data->last_updated = jiffies; data->valid = 1; } @@ -136,197 +135,241 @@ static struct lm95241_data *lm95241_update_device(struct device *dev) return data; } -/* Sysfs stuff */ -static ssize_t show_input(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct lm95241_data *data = lm95241_update_device(dev); - int index = to_sensor_dev_attr(attr)->index; - - return snprintf(buf, PAGE_SIZE - 1, "%d\n", - index == 0 || (data->config & (1 << (index / 2))) ? - temp_from_reg_signed(data->temp[index], data->temp[index + 1]) : - temp_from_reg_unsigned(data->temp[index], - data->temp[index + 1])); -} - -static ssize_t show_type(struct device *dev, struct device_attribute *attr, - char *buf) +static int lm95241_read_chip(struct device *dev, u32 attr, int channel, + long *val) { struct lm95241_data *data = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE - 1, - data->model & to_sensor_dev_attr(attr)->index ? "1\n" : "2\n"); + switch (attr) { + case hwmon_chip_update_interval: + *val = data->interval; + return 0; + default: + return -EOPNOTSUPP; + } } -static ssize_t set_type(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static int lm95241_read_temp(struct device *dev, u32 attr, int channel, + long *val) { - struct lm95241_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - unsigned long val; - int shift; - u8 mask = to_sensor_dev_attr(attr)->index; - - if (kstrtoul(buf, 10, &val) < 0) - return -EINVAL; - if (val != 1 && val != 2) - return -EINVAL; - - shift = mask == R1MS_MASK ? TT1_SHIFT : TT2_SHIFT; - - mutex_lock(&data->update_lock); + struct lm95241_data *data = lm95241_update_device(dev); - data->trutherm &= ~(TT_MASK << shift); - if (val == 1) { - data->model |= mask; - data->trutherm |= (TT_ON << shift); - } else { - data->model &= ~mask; - data->trutherm |= (TT_OFF << shift); + switch (attr) { + case hwmon_temp_input: + if (!channel || (data->config & BIT(channel - 1))) + *val = temp_from_reg_signed(data->temp[channel * 2], + data->temp[channel * 2 + 1]); + else + *val = temp_from_reg_unsigned(data->temp[channel * 2], + data->temp[channel * 2 + 1]); + return 0; + case hwmon_temp_min: + if (channel == 1) + *val = (data->config & R1DF_MASK) ? -128000 : 0; + else + *val = (data->config & R2DF_MASK) ? -128000 : 0; + return 0; + case hwmon_temp_max: + if (channel == 1) + *val = (data->config & R1DF_MASK) ? 127875 : 255875; + else + *val = (data->config & R2DF_MASK) ? 127875 : 255875; + return 0; + case hwmon_temp_type: + if (channel == 1) + *val = (data->model & R1MS_MASK) ? 1 : 2; + else + *val = (data->model & R2MS_MASK) ? 1 : 2; + return 0; + case hwmon_temp_fault: + if (channel == 1) + *val = !!(data->status & R1DM); + else + *val = !!(data->status & R2DM); + return 0; + default: + return -EOPNOTSUPP; } - data->valid = 0; - - i2c_smbus_write_byte_data(client, LM95241_REG_RW_REMOTE_MODEL, - data->model); - i2c_smbus_write_byte_data(client, LM95241_REG_RW_TRUTHERM, - data->trutherm); - - mutex_unlock(&data->update_lock); - - return count; } -static ssize_t show_min(struct device *dev, struct device_attribute *attr, - char *buf) +static int lm95241_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) { - struct lm95241_data *data = dev_get_drvdata(dev); - - return snprintf(buf, PAGE_SIZE - 1, - data->config & to_sensor_dev_attr(attr)->index ? - "-127000\n" : "0\n"); + switch (type) { + case hwmon_chip: + return lm95241_read_chip(dev, attr, channel, val); + case hwmon_temp: + return lm95241_read_temp(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } } -static ssize_t set_min(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static int lm95241_write_chip(struct device *dev, u32 attr, int channel, + long val) { struct lm95241_data *data = dev_get_drvdata(dev); - long val; - - if (kstrtol(buf, 10, &val) < 0) - return -EINVAL; - if (val < -128000) - return -EINVAL; + int convrate; + u8 config; + int ret; mutex_lock(&data->update_lock); - if (val < 0) - data->config |= to_sensor_dev_attr(attr)->index; - else - data->config &= ~to_sensor_dev_attr(attr)->index; - data->valid = 0; - - i2c_smbus_write_byte_data(data->client, LM95241_REG_RW_CONFIG, - data->config); - + switch (attr) { + case hwmon_chip_update_interval: + config = data->config & ~CFG_CRMASK; + if (val < 130) { + convrate = 76; + config |= CFG_CR0076; + } else if (val < 590) { + convrate = 182; + config |= CFG_CR0182; + } else if (val < 1850) { + convrate = 1000; + config |= CFG_CR1000; + } else { + convrate = 2700; + config |= CFG_CR2700; + } + data->interval = convrate; + data->config = config; + ret = i2c_smbus_write_byte_data(data->client, + LM95241_REG_RW_CONFIG, config); + break; + default: + ret = -EOPNOTSUPP; + break; + } mutex_unlock(&data->update_lock); - - return count; + return ret; } -static ssize_t show_max(struct device *dev, struct device_attribute *attr, - char *buf) +static int lm95241_write_temp(struct device *dev, u32 attr, int channel, + long val) { struct lm95241_data *data = dev_get_drvdata(dev); - - return snprintf(buf, PAGE_SIZE - 1, - data->config & to_sensor_dev_attr(attr)->index ? - "127000\n" : "255000\n"); -} - -static ssize_t set_max(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct lm95241_data *data = dev_get_drvdata(dev); - long val; - - if (kstrtol(buf, 10, &val) < 0) - return -EINVAL; - if (val >= 256000) - return -EINVAL; + struct i2c_client *client = data->client; + int ret; mutex_lock(&data->update_lock); - if (val <= 127000) - data->config |= to_sensor_dev_attr(attr)->index; - else - data->config &= ~to_sensor_dev_attr(attr)->index; - data->valid = 0; - - i2c_smbus_write_byte_data(data->client, LM95241_REG_RW_CONFIG, - data->config); + switch (attr) { + case hwmon_temp_min: + if (channel == 1) { + if (val < 0) + data->config |= R1DF_MASK; + else + data->config &= ~R1DF_MASK; + } else { + if (val < 0) + data->config |= R2DF_MASK; + else + data->config &= ~R2DF_MASK; + } + data->valid = 0; + ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, + data->config); + break; + case hwmon_temp_max: + if (channel == 1) { + if (val <= 127875) + data->config |= R1DF_MASK; + else + data->config &= ~R1DF_MASK; + } else { + if (val <= 127875) + data->config |= R2DF_MASK; + else + data->config &= ~R2DF_MASK; + } + data->valid = 0; + ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, + data->config); + break; + case hwmon_temp_type: + if (val != 1 && val != 2) { + ret = -EINVAL; + break; + } + if (channel == 1) { + data->trutherm &= ~(TT_MASK << TT1_SHIFT); + if (val == 1) { + data->model |= R1MS_MASK; + data->trutherm |= (TT_ON << TT1_SHIFT); + } else { + data->model &= ~R1MS_MASK; + data->trutherm |= (TT_OFF << TT1_SHIFT); + } + } else { + data->trutherm &= ~(TT_MASK << TT2_SHIFT); + if (val == 1) { + data->model |= R2MS_MASK; + data->trutherm |= (TT_ON << TT2_SHIFT); + } else { + data->model &= ~R2MS_MASK; + data->trutherm |= (TT_OFF << TT2_SHIFT); + } + } + ret = i2c_smbus_write_byte_data(client, + LM95241_REG_RW_REMOTE_MODEL, + data->model); + if (ret < 0) + break; + ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_TRUTHERM, + data->trutherm); + break; + default: + ret = -EOPNOTSUPP; + break; + } mutex_unlock(&data->update_lock); - return count; + return ret; } -static ssize_t show_interval(struct device *dev, struct device_attribute *attr, - char *buf) +static int lm95241_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) { - struct lm95241_data *data = lm95241_update_device(dev); - - return snprintf(buf, PAGE_SIZE - 1, "%lu\n", 1000 * data->interval - / HZ); + switch (type) { + case hwmon_chip: + return lm95241_write_chip(dev, attr, channel, val); + case hwmon_temp: + return lm95241_write_temp(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } } -static ssize_t set_interval(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static umode_t lm95241_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) { - struct lm95241_data *data = dev_get_drvdata(dev); - unsigned long val; - - if (kstrtoul(buf, 10, &val) < 0) - return -EINVAL; - - data->interval = val * HZ / 1000; - - return count; + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + return S_IRUGO | S_IWUSR; + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + return S_IRUGO; + case hwmon_temp_fault: + return S_IRUGO; + case hwmon_temp_min: + case hwmon_temp_max: + case hwmon_temp_type: + return S_IRUGO | S_IWUSR; + } + break; + default: + break; + } + return 0; } -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0); -static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2); -static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_input, NULL, 4); -static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, set_type, - R1MS_MASK); -static SENSOR_DEVICE_ATTR(temp3_type, S_IWUSR | S_IRUGO, show_type, set_type, - R2MS_MASK); -static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_min, set_min, - R1DF_MASK); -static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_min, set_min, - R2DF_MASK); -static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_max, set_max, - R1DF_MASK); -static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_max, set_max, - R2DF_MASK); -static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval, - set_interval); - -static struct attribute *lm95241_attrs[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp2_type.dev_attr.attr, - &sensor_dev_attr_temp3_type.dev_attr.attr, - &sensor_dev_attr_temp2_min.dev_attr.attr, - &sensor_dev_attr_temp3_min.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp3_max.dev_attr.attr, - &dev_attr_update_interval.attr, - NULL -}; -ATTRIBUTE_GROUPS(lm95241); - /* Return 0 if detection is successful, -ENODEV otherwise */ static int lm95241_detect(struct i2c_client *new_client, struct i2c_board_info *info) @@ -362,8 +405,8 @@ static int lm95241_detect(struct i2c_client *new_client, static void lm95241_init_client(struct i2c_client *client, struct lm95241_data *data) { - data->interval = HZ; /* 1 sec default */ - data->config = CFG_CR0076; + data->interval = 1000; + data->config = CFG_CR1000; data->trutherm = (TT_OFF << TT1_SHIFT) | (TT_OFF << TT2_SHIFT); i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, data->config); @@ -375,6 +418,47 @@ static void lm95241_init_client(struct i2c_client *client, data->model); } +static const u32 lm95241_chip_config[] = { + HWMON_C_UPDATE_INTERVAL, + 0 +}; + +static const struct hwmon_channel_info lm95241_chip = { + .type = hwmon_chip, + .config = lm95241_chip_config, +}; + +static const u32 lm95241_temp_config[] = { + HWMON_T_INPUT, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | HWMON_T_TYPE | + HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | HWMON_T_TYPE | + HWMON_T_FAULT, + 0 +}; + +static const struct hwmon_channel_info lm95241_temp = { + .type = hwmon_temp, + .config = lm95241_temp_config, +}; + +static const struct hwmon_channel_info *lm95241_info[] = { + &lm95241_chip, + &lm95241_temp, + NULL +}; + +static const struct hwmon_ops lm95241_hwmon_ops = { + .is_visible = lm95241_is_visible, + .read = lm95241_read, + .write = lm95241_write, +}; + +static const struct hwmon_chip_info lm95241_chip_info = { + .ops = &lm95241_hwmon_ops, + .info = lm95241_info, +}; + static int lm95241_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -392,9 +476,10 @@ static int lm95241_probe(struct i2c_client *client, /* Initialize the LM95241 chip */ lm95241_init_client(client, data); - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, - lm95241_groups); + &lm95241_chip_info, + NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } @@ -420,5 +505,5 @@ static struct i2c_driver lm95241_driver = { module_i2c_driver(lm95241_driver); MODULE_AUTHOR("Davide Rizzo <elpa.rizzo@gmail.com>"); -MODULE_DESCRIPTION("LM95241 sensor driver"); +MODULE_DESCRIPTION("LM95231/LM95241 sensor driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c index e7aef4561c83..a3bfd88752ca 100644 --- a/drivers/hwmon/lm95245.c +++ b/drivers/hwmon/lm95245.c @@ -15,22 +15,16 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <linux/module.h> +#include <linux/err.h> #include <linux/init.h> -#include <linux/slab.h> -#include <linux/jiffies.h> -#include <linux/i2c.h> #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> -#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> #include <linux/mutex.h> -#include <linux/sysfs.h> +#include <linux/regmap.h> +#include <linux/slab.h> static const unsigned short normal_i2c[] = { 0x18, 0x19, 0x29, 0x4c, 0x4d, I2C_CLIENT_END }; @@ -89,6 +83,7 @@ static const unsigned short normal_i2c[] = { #define RATE_CR1000 0x02 #define RATE_CR2500 0x03 +#define STATUS1_ROS 0x10 #define STATUS1_DIODE_FAULT 0x04 #define STATUS1_RTCRIT 0x02 #define STATUS1_LOC 0x01 @@ -112,14 +107,9 @@ static const u8 lm95245_reg_address[] = { /* Client data (each client gets its own) */ struct lm95245_data { - struct i2c_client *client; + struct regmap *regmap; struct mutex update_lock; - unsigned long last_updated; /* in jiffies */ - unsigned long interval; /* in msecs */ - bool valid; /* zero until following fields are valid */ - /* registers values */ - u8 regs[ARRAY_SIZE(lm95245_reg_address)]; - u8 config1, config2; + int interval; /* in msecs */ }; /* Conversions */ @@ -135,60 +125,36 @@ static int temp_from_reg_signed(u8 val_h, u8 val_l) return temp_from_reg_unsigned(val_h, val_l); } -static struct lm95245_data *lm95245_update_device(struct device *dev) +static int lm95245_read_conversion_rate(struct lm95245_data *data) { - struct lm95245_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; + unsigned int rate; + int ret; - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated - + msecs_to_jiffies(data->interval)) || !data->valid) { - int i; - - for (i = 0; i < ARRAY_SIZE(lm95245_reg_address); i++) - data->regs[i] - = i2c_smbus_read_byte_data(client, - lm95245_reg_address[i]); - data->last_updated = jiffies; - data->valid = 1; - } - - mutex_unlock(&data->update_lock); - - return data; -} - -static unsigned long lm95245_read_conversion_rate(struct i2c_client *client) -{ - int rate; - unsigned long interval; - - rate = i2c_smbus_read_byte_data(client, LM95245_REG_RW_CONVERS_RATE); + ret = regmap_read(data->regmap, LM95245_REG_RW_CONVERS_RATE, &rate); + if (ret < 0) + return ret; switch (rate) { case RATE_CR0063: - interval = 63; + data->interval = 63; break; case RATE_CR0364: - interval = 364; + data->interval = 364; break; case RATE_CR1000: - interval = 1000; + data->interval = 1000; break; case RATE_CR2500: default: - interval = 2500; + data->interval = 2500; break; } - - return interval; + return 0; } -static unsigned long lm95245_set_conversion_rate(struct i2c_client *client, - unsigned long interval) +static int lm95245_set_conversion_rate(struct lm95245_data *data, long interval) { - int rate; + int ret, rate; if (interval <= 63) { interval = 63; @@ -204,221 +170,289 @@ static unsigned long lm95245_set_conversion_rate(struct i2c_client *client, rate = RATE_CR2500; } - i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONVERS_RATE, rate); + ret = regmap_write(data->regmap, LM95245_REG_RW_CONVERS_RATE, rate); + if (ret < 0) + return ret; - return interval; -} - -/* Sysfs stuff */ -static ssize_t show_input(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct lm95245_data *data = lm95245_update_device(dev); - int temp; - int index = to_sensor_dev_attr(attr)->index; - - /* - * Index 0 (Local temp) is always signed - * Index 2 (Remote temp) has both signed and unsigned data - * use signed calculation for remote if signed bit is set - */ - if (index == 0 || data->regs[index] & 0x80) - temp = temp_from_reg_signed(data->regs[index], - data->regs[index + 1]); - else - temp = temp_from_reg_unsigned(data->regs[index + 2], - data->regs[index + 3]); - - return snprintf(buf, PAGE_SIZE - 1, "%d\n", temp); -} - -static ssize_t show_limit(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct lm95245_data *data = lm95245_update_device(dev); - int index = to_sensor_dev_attr(attr)->index; - - return snprintf(buf, PAGE_SIZE - 1, "%d\n", - data->regs[index] * 1000); + data->interval = interval; + return 0; } -static ssize_t set_limit(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static int lm95245_read_temp(struct device *dev, u32 attr, int channel, + long *val) { struct lm95245_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(attr)->index; - struct i2c_client *client = data->client; - unsigned long val; - - if (kstrtoul(buf, 10, &val) < 0) - return -EINVAL; - - val /= 1000; - - val = clamp_val(val, 0, (index == 6 ? 127 : 255)); - - mutex_lock(&data->update_lock); - - data->valid = 0; - - i2c_smbus_write_byte_data(client, lm95245_reg_address[index], val); - - mutex_unlock(&data->update_lock); - - return count; + struct regmap *regmap = data->regmap; + int ret, regl, regh, regvall, regvalh; + + switch (attr) { + case hwmon_temp_input: + regl = channel ? LM95245_REG_R_REMOTE_TEMPL_S : + LM95245_REG_R_LOCAL_TEMPL_S; + regh = channel ? LM95245_REG_R_REMOTE_TEMPH_S : + LM95245_REG_R_LOCAL_TEMPH_S; + ret = regmap_read(regmap, regl, ®vall); + if (ret < 0) + return ret; + ret = regmap_read(regmap, regh, ®valh); + if (ret < 0) + return ret; + /* + * Local temp is always signed. + * Remote temp has both signed and unsigned data. + * Use signed calculation for remote if signed bit is set + * or if reported temperature is below signed limit. + */ + if (!channel || (regvalh & 0x80) || regvalh < 0x7f) { + *val = temp_from_reg_signed(regvalh, regvall); + return 0; + } + ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPL_U, + ®vall); + if (ret < 0) + return ret; + ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPH_U, + ®valh); + if (ret < 0) + return ret; + *val = temp_from_reg_unsigned(regvalh, regvall); + return 0; + case hwmon_temp_max: + ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT, + ®valh); + if (ret < 0) + return ret; + *val = regvalh * 1000; + return 0; + case hwmon_temp_crit: + regh = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT : + LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT; + ret = regmap_read(regmap, regh, ®valh); + if (ret < 0) + return ret; + *val = regvalh * 1000; + return 0; + case hwmon_temp_max_hyst: + ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT, + ®valh); + if (ret < 0) + return ret; + ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS, + ®vall); + if (ret < 0) + return ret; + *val = (regvalh - regvall) * 1000; + return 0; + case hwmon_temp_crit_hyst: + regh = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT : + LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT; + ret = regmap_read(regmap, regh, ®valh); + if (ret < 0) + return ret; + ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS, + ®vall); + if (ret < 0) + return ret; + *val = (regvalh - regvall) * 1000; + return 0; + case hwmon_temp_type: + ret = regmap_read(regmap, LM95245_REG_RW_CONFIG2, ®valh); + if (ret < 0) + return ret; + *val = (regvalh & CFG2_REMOTE_TT) ? 1 : 2; + return 0; + case hwmon_temp_offset: + ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFL, + ®vall); + if (ret < 0) + return ret; + ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFH, + ®valh); + if (ret < 0) + return ret; + *val = temp_from_reg_signed(regvalh, regvall); + return 0; + case hwmon_temp_max_alarm: + ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®valh); + if (ret < 0) + return ret; + *val = !!(regvalh & STATUS1_ROS); + return 0; + case hwmon_temp_crit_alarm: + ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®valh); + if (ret < 0) + return ret; + *val = !!(regvalh & (channel ? STATUS1_RTCRIT : STATUS1_LOC)); + return 0; + case hwmon_temp_fault: + ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®valh); + if (ret < 0) + return ret; + *val = !!(regvalh & STATUS1_DIODE_FAULT); + return 0; + default: + return -EOPNOTSUPP; + } } -static ssize_t show_crit_hyst(struct device *dev, struct device_attribute *attr, - char *buf) +static int lm95245_write_temp(struct device *dev, u32 attr, int channel, + long val) { - struct lm95245_data *data = lm95245_update_device(dev); - int index = to_sensor_dev_attr(attr)->index; - int hyst = data->regs[index] - data->regs[8]; - - return snprintf(buf, PAGE_SIZE - 1, "%d\n", hyst * 1000); + struct lm95245_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + unsigned int regval; + int ret, reg; + + switch (attr) { + case hwmon_temp_max: + val = clamp_val(val / 1000, 0, 255); + ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT, val); + return ret; + case hwmon_temp_crit: + reg = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT : + LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT; + val = clamp_val(val / 1000, 0, channel ? 255 : 127); + ret = regmap_write(regmap, reg, val); + return ret; + case hwmon_temp_crit_hyst: + mutex_lock(&data->update_lock); + ret = regmap_read(regmap, LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT, + ®val); + if (ret < 0) { + mutex_unlock(&data->update_lock); + return ret; + } + /* Clamp to reasonable range to prevent overflow */ + val = clamp_val(val, -1000000, 1000000); + val = regval - val / 1000; + val = clamp_val(val, 0, 31); + ret = regmap_write(regmap, LM95245_REG_RW_COMMON_HYSTERESIS, + val); + mutex_unlock(&data->update_lock); + return ret; + case hwmon_temp_offset: + val = clamp_val(val, -128000, 127875); + val = val * 256 / 1000; + mutex_lock(&data->update_lock); + ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFL, + val & 0xe0); + if (ret < 0) { + mutex_unlock(&data->update_lock); + return ret; + } + ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFH, + (val >> 8) & 0xff); + mutex_unlock(&data->update_lock); + return ret; + case hwmon_temp_type: + if (val != 1 && val != 2) + return -EINVAL; + ret = regmap_update_bits(regmap, LM95245_REG_RW_CONFIG2, + CFG2_REMOTE_TT, + val == 1 ? CFG2_REMOTE_TT : 0); + return ret; + default: + return -EOPNOTSUPP; + } } -static ssize_t set_crit_hyst(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static int lm95245_read_chip(struct device *dev, u32 attr, int channel, + long *val) { struct lm95245_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(attr)->index; - struct i2c_client *client = data->client; - unsigned long val; - int hyst, limit; - - if (kstrtoul(buf, 10, &val) < 0) - return -EINVAL; - - mutex_lock(&data->update_lock); - - limit = i2c_smbus_read_byte_data(client, lm95245_reg_address[index]); - hyst = limit - val / 1000; - hyst = clamp_val(hyst, 0, 31); - data->regs[8] = hyst; - /* shared crit hysteresis */ - i2c_smbus_write_byte_data(client, LM95245_REG_RW_COMMON_HYSTERESIS, - hyst); - - mutex_unlock(&data->update_lock); - - return count; + switch (attr) { + case hwmon_chip_update_interval: + *val = data->interval; + return 0; + default: + return -EOPNOTSUPP; + } } -static ssize_t show_type(struct device *dev, struct device_attribute *attr, - char *buf) +static int lm95245_write_chip(struct device *dev, u32 attr, int channel, + long val) { struct lm95245_data *data = dev_get_drvdata(dev); - - return snprintf(buf, PAGE_SIZE - 1, - data->config2 & CFG2_REMOTE_TT ? "1\n" : "2\n"); + int ret; + + switch (attr) { + case hwmon_chip_update_interval: + mutex_lock(&data->update_lock); + ret = lm95245_set_conversion_rate(data, val); + mutex_unlock(&data->update_lock); + return ret; + default: + return -EOPNOTSUPP; + } } -static ssize_t set_type(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static int lm95245_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) { - struct lm95245_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - unsigned long val; - - if (kstrtoul(buf, 10, &val) < 0) - return -EINVAL; - if (val != 1 && val != 2) - return -EINVAL; - - mutex_lock(&data->update_lock); - - if (val == 1) - data->config2 |= CFG2_REMOTE_TT; - else - data->config2 &= ~CFG2_REMOTE_TT; - - data->valid = 0; - - i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG2, - data->config2); - - mutex_unlock(&data->update_lock); - - return count; + switch (type) { + case hwmon_chip: + return lm95245_read_chip(dev, attr, channel, val); + case hwmon_temp: + return lm95245_read_temp(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } } -static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, - char *buf) +static int lm95245_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) { - struct lm95245_data *data = lm95245_update_device(dev); - int index = to_sensor_dev_attr(attr)->index; - - return snprintf(buf, PAGE_SIZE - 1, "%d\n", - !!(data->regs[9] & index)); + switch (type) { + case hwmon_chip: + return lm95245_write_chip(dev, attr, channel, val); + case hwmon_temp: + return lm95245_write_temp(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } } -static ssize_t show_interval(struct device *dev, struct device_attribute *attr, - char *buf) +static umode_t lm95245_temp_is_visible(const void *data, u32 attr, int channel) { - struct lm95245_data *data = lm95245_update_device(dev); - - return snprintf(buf, PAGE_SIZE - 1, "%lu\n", data->interval); + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_max_alarm: + case hwmon_temp_max_hyst: + case hwmon_temp_crit_alarm: + case hwmon_temp_fault: + return S_IRUGO; + case hwmon_temp_type: + case hwmon_temp_max: + case hwmon_temp_crit: + case hwmon_temp_offset: + return S_IRUGO | S_IWUSR; + case hwmon_temp_crit_hyst: + return (channel == 0) ? S_IRUGO | S_IWUSR : S_IRUGO; + default: + return 0; + } } -static ssize_t set_interval(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static umode_t lm95245_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) { - struct lm95245_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - unsigned long val; - - if (kstrtoul(buf, 10, &val) < 0) - return -EINVAL; - - mutex_lock(&data->update_lock); - - data->interval = lm95245_set_conversion_rate(client, val); - - mutex_unlock(&data->update_lock); - - return count; + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + return S_IRUGO | S_IWUSR; + default: + return 0; + } + case hwmon_temp: + return lm95245_temp_is_visible(data, attr, channel); + default: + return 0; + } } -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0); -static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_limit, - set_limit, 6); -static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_crit_hyst, - set_crit_hyst, 6); -static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, - STATUS1_LOC); - -static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2); -static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_limit, - set_limit, 7); -static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_crit_hyst, NULL, 7); -static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, - STATUS1_RTCRIT); -static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, - set_type, 0); -static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, - STATUS1_DIODE_FAULT); - -static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval, - set_interval); - -static struct attribute *lm95245_attrs[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp1_crit.dev_attr.attr, - &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp2_crit.dev_attr.attr, - &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, - &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_type.dev_attr.attr, - &sensor_dev_attr_temp2_fault.dev_attr.attr, - &dev_attr_update_interval.attr, - NULL -}; -ATTRIBUTE_GROUPS(lm95245); - /* Return 0 if detection is successful, -ENODEV otherwise */ static int lm95245_detect(struct i2c_client *new_client, struct i2c_board_info *info) @@ -453,44 +487,130 @@ static int lm95245_detect(struct i2c_client *new_client, return 0; } -static void lm95245_init_client(struct i2c_client *client, - struct lm95245_data *data) +static int lm95245_init_client(struct lm95245_data *data) { - data->interval = lm95245_read_conversion_rate(client); - - data->config1 = i2c_smbus_read_byte_data(client, - LM95245_REG_RW_CONFIG1); - data->config2 = i2c_smbus_read_byte_data(client, - LM95245_REG_RW_CONFIG2); - - if (data->config1 & CFG_STOP) { - /* Clear the standby bit */ - data->config1 &= ~CFG_STOP; - i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG1, - data->config1); + int ret; + + ret = lm95245_read_conversion_rate(data); + if (ret < 0) + return ret; + + return regmap_update_bits(data->regmap, LM95245_REG_RW_CONFIG1, + CFG_STOP, 0); +} + +static bool lm95245_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LM95245_REG_RW_CONFIG1: + case LM95245_REG_RW_CONVERS_RATE: + case LM95245_REG_W_ONE_SHOT: + case LM95245_REG_RW_CONFIG2: + case LM95245_REG_RW_REMOTE_OFFH: + case LM95245_REG_RW_REMOTE_OFFL: + case LM95245_REG_RW_REMOTE_OS_LIMIT: + case LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT: + case LM95245_REG_RW_REMOTE_TCRIT_LIMIT: + case LM95245_REG_RW_COMMON_HYSTERESIS: + return true; + default: + return false; + } +} + +static bool lm95245_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LM95245_REG_R_STATUS1: + case LM95245_REG_R_STATUS2: + case LM95245_REG_R_LOCAL_TEMPH_S: + case LM95245_REG_R_LOCAL_TEMPL_S: + case LM95245_REG_R_REMOTE_TEMPH_S: + case LM95245_REG_R_REMOTE_TEMPL_S: + case LM95245_REG_R_REMOTE_TEMPH_U: + case LM95245_REG_R_REMOTE_TEMPL_U: + return true; + default: + return false; } } +static const struct regmap_config lm95245_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = lm95245_is_writeable_reg, + .volatile_reg = lm95245_is_volatile_reg, + .cache_type = REGCACHE_RBTREE, + .use_single_rw = true, +}; + +static const u32 lm95245_chip_config[] = { + HWMON_C_UPDATE_INTERVAL, + 0 +}; + +static const struct hwmon_channel_info lm95245_chip = { + .type = hwmon_chip, + .config = lm95245_chip_config, +}; + +static const u32 lm95245_temp_config[] = { + HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_CRIT_ALARM, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_CRIT | + HWMON_T_CRIT_HYST | HWMON_T_FAULT | HWMON_T_MAX_ALARM | + HWMON_T_CRIT_ALARM | HWMON_T_TYPE | HWMON_T_OFFSET, + 0 +}; + +static const struct hwmon_channel_info lm95245_temp = { + .type = hwmon_temp, + .config = lm95245_temp_config, +}; + +static const struct hwmon_channel_info *lm95245_info[] = { + &lm95245_chip, + &lm95245_temp, + NULL +}; + +static const struct hwmon_ops lm95245_hwmon_ops = { + .is_visible = lm95245_is_visible, + .read = lm95245_read, + .write = lm95245_write, +}; + +static const struct hwmon_chip_info lm95245_chip_info = { + .ops = &lm95245_hwmon_ops, + .info = lm95245_info, +}; + static int lm95245_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; struct lm95245_data *data; struct device *hwmon_dev; + int ret; data = devm_kzalloc(dev, sizeof(struct lm95245_data), GFP_KERNEL); if (!data) return -ENOMEM; - data->client = client; + data->regmap = devm_regmap_init_i2c(client, &lm95245_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + mutex_init(&data->update_lock); /* Initialize the LM95245 chip */ - lm95245_init_client(client, data); - - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, - lm95245_groups); + ret = lm95245_init_client(data); + if (ret < 0) + return ret; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, + &lm95245_chip_info, + NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } diff --git a/drivers/hwmon/ltc4151.c b/drivers/hwmon/ltc4151.c index c86a18402496..8445c9fd946b 100644 --- a/drivers/hwmon/ltc4151.c +++ b/drivers/hwmon/ltc4151.c @@ -30,6 +30,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/init.h> #include <linux/err.h> #include <linux/slab.h> @@ -52,6 +53,7 @@ struct ltc4151_data { struct mutex update_lock; bool valid; unsigned long last_updated; /* in jiffies */ + unsigned int shunt; /* in micro ohms */ /* Registers */ u8 regs[6]; @@ -111,9 +113,9 @@ static int ltc4151_get_value(struct ltc4151_data *data, u8 reg) case LTC4151_SENSE_H: /* * 20uV resolution. Convert to current as measured with - * an 1 mOhm sense resistor, in mA. + * a given sense resistor, in mA. */ - val = val * 20; + val = val * 20 * 1000 / data->shunt; break; case LTC4151_VIN_H: /* 25 mV per increment */ @@ -176,6 +178,7 @@ static int ltc4151_probe(struct i2c_client *client, struct device *dev = &client->dev; struct ltc4151_data *data; struct device *hwmon_dev; + u32 shunt; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; @@ -184,6 +187,15 @@ static int ltc4151_probe(struct i2c_client *client, if (!data) return -ENOMEM; + if (of_property_read_u32(client->dev.of_node, + "shunt-resistor-micro-ohms", &shunt)) + shunt = 1000; /* 1 mOhm if not set via DT */ + + if (shunt == 0) + return -EINVAL; + + data->shunt = shunt; + data->client = client; mutex_init(&data->update_lock); @@ -199,10 +211,16 @@ static const struct i2c_device_id ltc4151_id[] = { }; MODULE_DEVICE_TABLE(i2c, ltc4151_id); +static const struct of_device_id ltc4151_match[] = { + { .compatible = "lltc,ltc4151" }, + {}, +}; + /* This is the driver that will be inserted */ static struct i2c_driver ltc4151_driver = { .driver = { .name = "ltc4151", + .of_match_table = of_match_ptr(ltc4151_match), }, .probe = ltc4151_probe, .id_table = ltc4151_id, diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c index 681b5b7b3c3b..4680d89556ce 100644 --- a/drivers/hwmon/ltc4245.c +++ b/drivers/hwmon/ltc4245.c @@ -16,6 +16,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/bitops.h> #include <linux/err.h> #include <linux/slab.h> #include <linux/i2c.h> @@ -53,8 +54,6 @@ enum ltc4245_cmd { struct ltc4245_data { struct i2c_client *client; - const struct attribute_group *groups[3]; - struct mutex update_lock; bool valid; unsigned long last_updated; /* in jiffies */ @@ -162,7 +161,7 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev) ltc4245_update_gpios(dev); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -256,213 +255,204 @@ static unsigned int ltc4245_get_current(struct device *dev, u8 reg) return curr; } -static ssize_t ltc4245_show_voltage(struct device *dev, - struct device_attribute *da, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - const int voltage = ltc4245_get_voltage(dev, attr->index); +/* Map from voltage channel index to voltage register */ - return snprintf(buf, PAGE_SIZE, "%d\n", voltage); -} +static const s8 ltc4245_in_regs[] = { + LTC4245_12VIN, LTC4245_5VIN, LTC4245_3VIN, LTC4245_VEEIN, + LTC4245_12VOUT, LTC4245_5VOUT, LTC4245_3VOUT, LTC4245_VEEOUT, +}; + +/* Map from current channel index to current register */ -static ssize_t ltc4245_show_current(struct device *dev, - struct device_attribute *da, - char *buf) +static const s8 ltc4245_curr_regs[] = { + LTC4245_12VSENSE, LTC4245_5VSENSE, LTC4245_3VSENSE, LTC4245_VEESENSE, +}; + +static int ltc4245_read_curr(struct device *dev, u32 attr, int channel, + long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - const unsigned int curr = ltc4245_get_current(dev, attr->index); + struct ltc4245_data *data = ltc4245_update_device(dev); - return snprintf(buf, PAGE_SIZE, "%u\n", curr); + switch (attr) { + case hwmon_curr_input: + *val = ltc4245_get_current(dev, ltc4245_curr_regs[channel]); + return 0; + case hwmon_curr_max_alarm: + *val = !!(data->cregs[LTC4245_FAULT1] & BIT(channel + 4)); + return 0; + default: + return -EOPNOTSUPP; + } } -static ssize_t ltc4245_show_power(struct device *dev, - struct device_attribute *da, - char *buf) +static int ltc4245_read_in(struct device *dev, u32 attr, int channel, long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - const unsigned int curr = ltc4245_get_current(dev, attr->index); - const int output_voltage = ltc4245_get_voltage(dev, attr->index+1); + struct ltc4245_data *data = ltc4245_update_device(dev); - /* current in mA * voltage in mV == power in uW */ - const unsigned int power = abs(output_voltage * curr); + switch (attr) { + case hwmon_in_input: + if (channel < 8) { + *val = ltc4245_get_voltage(dev, + ltc4245_in_regs[channel]); + } else { + int regval = data->gpios[channel - 8]; + + if (regval < 0) + return regval; + *val = regval * 10; + } + return 0; + case hwmon_in_min_alarm: + if (channel < 4) + *val = !!(data->cregs[LTC4245_FAULT1] & BIT(channel)); + else + *val = !!(data->cregs[LTC4245_FAULT2] & + BIT(channel - 4)); + return 0; + default: + return -EOPNOTSUPP; + } +} - return snprintf(buf, PAGE_SIZE, "%u\n", power); +static int ltc4245_read_power(struct device *dev, u32 attr, int channel, + long *val) +{ + unsigned long curr; + long voltage; + + switch (attr) { + case hwmon_power_input: + (void)ltc4245_update_device(dev); + curr = ltc4245_get_current(dev, ltc4245_curr_regs[channel]); + voltage = ltc4245_get_voltage(dev, ltc4245_in_regs[channel]); + *val = abs(curr * voltage); + return 0; + default: + return -EOPNOTSUPP; + } } -static ssize_t ltc4245_show_alarm(struct device *dev, - struct device_attribute *da, - char *buf) +static int ltc4245_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) { - struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da); - struct ltc4245_data *data = ltc4245_update_device(dev); - const u8 reg = data->cregs[attr->index]; - const u32 mask = attr->nr; - return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0); + switch (type) { + case hwmon_curr: + return ltc4245_read_curr(dev, attr, channel, val); + case hwmon_power: + return ltc4245_read_power(dev, attr, channel, val); + case hwmon_in: + return ltc4245_read_in(dev, attr, channel - 1, val); + default: + return -EOPNOTSUPP; + } } -static ssize_t ltc4245_show_gpio(struct device *dev, - struct device_attribute *da, - char *buf) +static umode_t ltc4245_is_visible(const void *_data, + enum hwmon_sensor_types type, + u32 attr, int channel) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct ltc4245_data *data = ltc4245_update_device(dev); - int val = data->gpios[attr->index]; + const struct ltc4245_data *data = _data; + + switch (type) { + case hwmon_in: + if (channel == 0) + return 0; + switch (attr) { + case hwmon_in_input: + if (channel > 9 && !data->use_extra_gpios) + return 0; + return S_IRUGO; + case hwmon_in_min_alarm: + if (channel > 8) + return 0; + return S_IRUGO; + default: + return 0; + } + case hwmon_curr: + switch (attr) { + case hwmon_curr_input: + case hwmon_curr_max_alarm: + return S_IRUGO; + default: + return 0; + } + case hwmon_power: + switch (attr) { + case hwmon_power_input: + return S_IRUGO; + default: + return 0; + } + default: + return 0; + } +} - /* handle stale GPIO's */ - if (val < 0) - return val; +static const u32 ltc4245_in_config[] = { + HWMON_I_INPUT, /* dummy, skipped in is_visible */ + HWMON_I_INPUT | HWMON_I_MIN_ALARM, + HWMON_I_INPUT | HWMON_I_MIN_ALARM, + HWMON_I_INPUT | HWMON_I_MIN_ALARM, + HWMON_I_INPUT | HWMON_I_MIN_ALARM, + HWMON_I_INPUT | HWMON_I_MIN_ALARM, + HWMON_I_INPUT | HWMON_I_MIN_ALARM, + HWMON_I_INPUT | HWMON_I_MIN_ALARM, + HWMON_I_INPUT | HWMON_I_MIN_ALARM, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + 0 +}; - /* Convert to millivolts and print */ - return snprintf(buf, PAGE_SIZE, "%u\n", val * 10); -} +static const struct hwmon_channel_info ltc4245_in = { + .type = hwmon_in, + .config = ltc4245_in_config, +}; -/* Construct a sensor_device_attribute structure for each register */ - -/* Input voltages */ -static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4245_show_voltage, NULL, - LTC4245_12VIN); -static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4245_show_voltage, NULL, - LTC4245_5VIN); -static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc4245_show_voltage, NULL, - LTC4245_3VIN); -static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc4245_show_voltage, NULL, - LTC4245_VEEIN); - -/* Input undervoltage alarms */ -static SENSOR_DEVICE_ATTR_2(in1_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 0, LTC4245_FAULT1); -static SENSOR_DEVICE_ATTR_2(in2_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 1, LTC4245_FAULT1); -static SENSOR_DEVICE_ATTR_2(in3_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 2, LTC4245_FAULT1); -static SENSOR_DEVICE_ATTR_2(in4_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 3, LTC4245_FAULT1); - -/* Currents (via sense resistor) */ -static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4245_show_current, NULL, - LTC4245_12VSENSE); -static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc4245_show_current, NULL, - LTC4245_5VSENSE); -static SENSOR_DEVICE_ATTR(curr3_input, S_IRUGO, ltc4245_show_current, NULL, - LTC4245_3VSENSE); -static SENSOR_DEVICE_ATTR(curr4_input, S_IRUGO, ltc4245_show_current, NULL, - LTC4245_VEESENSE); - -/* Overcurrent alarms */ -static SENSOR_DEVICE_ATTR_2(curr1_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 4, LTC4245_FAULT1); -static SENSOR_DEVICE_ATTR_2(curr2_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 5, LTC4245_FAULT1); -static SENSOR_DEVICE_ATTR_2(curr3_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 6, LTC4245_FAULT1); -static SENSOR_DEVICE_ATTR_2(curr4_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 7, LTC4245_FAULT1); - -/* Output voltages */ -static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, ltc4245_show_voltage, NULL, - LTC4245_12VOUT); -static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, ltc4245_show_voltage, NULL, - LTC4245_5VOUT); -static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, ltc4245_show_voltage, NULL, - LTC4245_3VOUT); -static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, ltc4245_show_voltage, NULL, - LTC4245_VEEOUT); - -/* Power Bad alarms */ -static SENSOR_DEVICE_ATTR_2(in5_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 0, LTC4245_FAULT2); -static SENSOR_DEVICE_ATTR_2(in6_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 1, LTC4245_FAULT2); -static SENSOR_DEVICE_ATTR_2(in7_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 2, LTC4245_FAULT2); -static SENSOR_DEVICE_ATTR_2(in8_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 3, LTC4245_FAULT2); - -/* GPIO voltages */ -static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, ltc4245_show_gpio, NULL, 0); -static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, ltc4245_show_gpio, NULL, 1); -static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, ltc4245_show_gpio, NULL, 2); - -/* Power Consumption (virtual) */ -static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ltc4245_show_power, NULL, - LTC4245_12VSENSE); -static SENSOR_DEVICE_ATTR(power2_input, S_IRUGO, ltc4245_show_power, NULL, - LTC4245_5VSENSE); -static SENSOR_DEVICE_ATTR(power3_input, S_IRUGO, ltc4245_show_power, NULL, - LTC4245_3VSENSE); -static SENSOR_DEVICE_ATTR(power4_input, S_IRUGO, ltc4245_show_power, NULL, - LTC4245_VEESENSE); +static const u32 ltc4245_curr_config[] = { + HWMON_C_INPUT | HWMON_C_MAX_ALARM, + HWMON_C_INPUT | HWMON_C_MAX_ALARM, + HWMON_C_INPUT | HWMON_C_MAX_ALARM, + HWMON_C_INPUT | HWMON_C_MAX_ALARM, + 0 +}; -/* - * Finally, construct an array of pointers to members of the above objects, - * as required for sysfs_create_group() - */ -static struct attribute *ltc4245_std_attributes[] = { - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_in2_input.dev_attr.attr, - &sensor_dev_attr_in3_input.dev_attr.attr, - &sensor_dev_attr_in4_input.dev_attr.attr, - - &sensor_dev_attr_in1_min_alarm.dev_attr.attr, - &sensor_dev_attr_in2_min_alarm.dev_attr.attr, - &sensor_dev_attr_in3_min_alarm.dev_attr.attr, - &sensor_dev_attr_in4_min_alarm.dev_attr.attr, - - &sensor_dev_attr_curr1_input.dev_attr.attr, - &sensor_dev_attr_curr2_input.dev_attr.attr, - &sensor_dev_attr_curr3_input.dev_attr.attr, - &sensor_dev_attr_curr4_input.dev_attr.attr, - - &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, - &sensor_dev_attr_curr2_max_alarm.dev_attr.attr, - &sensor_dev_attr_curr3_max_alarm.dev_attr.attr, - &sensor_dev_attr_curr4_max_alarm.dev_attr.attr, - - &sensor_dev_attr_in5_input.dev_attr.attr, - &sensor_dev_attr_in6_input.dev_attr.attr, - &sensor_dev_attr_in7_input.dev_attr.attr, - &sensor_dev_attr_in8_input.dev_attr.attr, - - &sensor_dev_attr_in5_min_alarm.dev_attr.attr, - &sensor_dev_attr_in6_min_alarm.dev_attr.attr, - &sensor_dev_attr_in7_min_alarm.dev_attr.attr, - &sensor_dev_attr_in8_min_alarm.dev_attr.attr, - - &sensor_dev_attr_in9_input.dev_attr.attr, - - &sensor_dev_attr_power1_input.dev_attr.attr, - &sensor_dev_attr_power2_input.dev_attr.attr, - &sensor_dev_attr_power3_input.dev_attr.attr, - &sensor_dev_attr_power4_input.dev_attr.attr, - - NULL, +static const struct hwmon_channel_info ltc4245_curr = { + .type = hwmon_curr, + .config = ltc4245_curr_config, }; -static struct attribute *ltc4245_gpio_attributes[] = { - &sensor_dev_attr_in10_input.dev_attr.attr, - &sensor_dev_attr_in11_input.dev_attr.attr, - NULL, +static const u32 ltc4245_power_config[] = { + HWMON_P_INPUT, + HWMON_P_INPUT, + HWMON_P_INPUT, + HWMON_P_INPUT, + 0 }; -static const struct attribute_group ltc4245_std_group = { - .attrs = ltc4245_std_attributes, +static const struct hwmon_channel_info ltc4245_power = { + .type = hwmon_power, + .config = ltc4245_power_config, }; -static const struct attribute_group ltc4245_gpio_group = { - .attrs = ltc4245_gpio_attributes, +static const struct hwmon_channel_info *ltc4245_info[] = { + <c4245_in, + <c4245_curr, + <c4245_power, + NULL }; -static void ltc4245_sysfs_add_groups(struct ltc4245_data *data) -{ - /* standard sysfs attributes */ - data->groups[0] = <c4245_std_group; +static const struct hwmon_ops ltc4245_hwmon_ops = { + .is_visible = ltc4245_is_visible, + .read = ltc4245_read, +}; - /* if we're using the extra gpio support, register it's attributes */ - if (data->use_extra_gpios) - data->groups[1] = <c4245_gpio_group; -} +static const struct hwmon_chip_info ltc4245_chip_info = { + .ops = <c4245_hwmon_ops, + .info = ltc4245_info, +}; static bool ltc4245_use_extra_gpios(struct i2c_client *client) { @@ -502,12 +492,10 @@ static int ltc4245_probe(struct i2c_client *client, i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00); i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00); - /* Add sysfs hooks */ - ltc4245_sysfs_add_groups(data); - - hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, - client->name, data, - data->groups); + hwmon_dev = devm_hwmon_device_register_with_info(&client->dev, + client->name, data, + <c4245_chip_info, + NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c index 69c0ac80a946..bef84e085973 100644 --- a/drivers/hwmon/max31790.c +++ b/drivers/hwmon/max31790.c @@ -17,7 +17,6 @@ #include <linux/err.h> #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/jiffies.h> @@ -169,362 +168,288 @@ static u8 bits_for_tach_period(int rpm) return bits; } -static ssize_t get_fan(struct device *dev, - struct device_attribute *devattr, char *buf) +static int max31790_read_fan(struct device *dev, u32 attr, int channel, + long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct max31790_data *data = max31790_update_device(dev); int sr, rpm; if (IS_ERR(data)) return PTR_ERR(data); - sr = get_tach_period(data->fan_dynamics[attr->index]); - rpm = RPM_FROM_REG(data->tach[attr->index], sr); - - return sprintf(buf, "%d\n", rpm); -} - -static ssize_t get_fan_target(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct max31790_data *data = max31790_update_device(dev); - int sr, rpm; - - if (IS_ERR(data)) - return PTR_ERR(data); - - sr = get_tach_period(data->fan_dynamics[attr->index]); - rpm = RPM_FROM_REG(data->target_count[attr->index], sr); - - return sprintf(buf, "%d\n", rpm); + switch (attr) { + case hwmon_fan_input: + sr = get_tach_period(data->fan_dynamics[channel]); + rpm = RPM_FROM_REG(data->tach[channel], sr); + *val = rpm; + return 0; + case hwmon_fan_target: + sr = get_tach_period(data->fan_dynamics[channel]); + rpm = RPM_FROM_REG(data->target_count[channel], sr); + *val = rpm; + return 0; + case hwmon_fan_fault: + *val = !!(data->fault_status & (1 << channel)); + return 0; + default: + return -EOPNOTSUPP; + } } -static ssize_t set_fan_target(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) +static int max31790_write_fan(struct device *dev, u32 attr, int channel, + long val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct max31790_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; + int target_count; + int err = 0; u8 bits; int sr; - int target_count; - unsigned long rpm; - int err; - - err = kstrtoul(buf, 10, &rpm); - if (err) - return err; mutex_lock(&data->update_lock); - rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX); - bits = bits_for_tach_period(rpm); - data->fan_dynamics[attr->index] = - ((data->fan_dynamics[attr->index] - & ~MAX31790_FAN_DYN_SR_MASK) - | (bits << MAX31790_FAN_DYN_SR_SHIFT)); - err = i2c_smbus_write_byte_data(client, - MAX31790_REG_FAN_DYNAMICS(attr->index), - data->fan_dynamics[attr->index]); - - if (err < 0) { - mutex_unlock(&data->update_lock); - return err; + switch (attr) { + case hwmon_fan_target: + val = clamp_val(val, FAN_RPM_MIN, FAN_RPM_MAX); + bits = bits_for_tach_period(val); + data->fan_dynamics[channel] = + ((data->fan_dynamics[channel] & + ~MAX31790_FAN_DYN_SR_MASK) | + (bits << MAX31790_FAN_DYN_SR_SHIFT)); + err = i2c_smbus_write_byte_data(client, + MAX31790_REG_FAN_DYNAMICS(channel), + data->fan_dynamics[channel]); + if (err < 0) + break; + + sr = get_tach_period(data->fan_dynamics[channel]); + target_count = RPM_TO_REG(val, sr); + target_count = clamp_val(target_count, 0x1, 0x7FF); + + data->target_count[channel] = target_count << 5; + + err = i2c_smbus_write_word_swapped(client, + MAX31790_REG_TARGET_COUNT(channel), + data->target_count[channel]); + break; + default: + err = -EOPNOTSUPP; + break; } - sr = get_tach_period(data->fan_dynamics[attr->index]); - target_count = RPM_TO_REG(rpm, sr); - target_count = clamp_val(target_count, 0x1, 0x7FF); - - data->target_count[attr->index] = target_count << 5; - - err = i2c_smbus_write_word_swapped(client, - MAX31790_REG_TARGET_COUNT(attr->index), - data->target_count[attr->index]); - mutex_unlock(&data->update_lock); - if (err < 0) - return err; + return err; +} - return count; +static umode_t max31790_fan_is_visible(const void *_data, u32 attr, int channel) +{ + const struct max31790_data *data = _data; + u8 fan_config = data->fan_config[channel % NR_CHANNEL]; + + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_fault: + if (channel < NR_CHANNEL || + (fan_config & MAX31790_FAN_CFG_TACH_INPUT)) + return S_IRUGO; + return 0; + case hwmon_fan_target: + if (channel < NR_CHANNEL && + !(fan_config & MAX31790_FAN_CFG_TACH_INPUT)) + return S_IRUGO | S_IWUSR; + return 0; + default: + return 0; + } } -static ssize_t get_pwm(struct device *dev, - struct device_attribute *devattr, char *buf) +static int max31790_read_pwm(struct device *dev, u32 attr, int channel, + long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct max31790_data *data = max31790_update_device(dev); - int pwm; + u8 fan_config = data->fan_config[channel]; if (IS_ERR(data)) return PTR_ERR(data); - pwm = data->pwm[attr->index] >> 8; - - return sprintf(buf, "%d\n", pwm); + switch (attr) { + case hwmon_pwm_input: + *val = data->pwm[channel] >> 8; + return 0; + case hwmon_pwm_enable: + if (fan_config & MAX31790_FAN_CFG_RPM_MODE) + *val = 2; + else if (fan_config & MAX31790_FAN_CFG_TACH_INPUT_EN) + *val = 1; + else + *val = 0; + return 0; + default: + return -EOPNOTSUPP; + } } -static ssize_t set_pwm(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) +static int max31790_write_pwm(struct device *dev, u32 attr, int channel, + long val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct max31790_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; - unsigned long pwm; - int err; - - err = kstrtoul(buf, 10, &pwm); - if (err) - return err; - - if (pwm > 255) - return -EINVAL; + u8 fan_config; + int err = 0; mutex_lock(&data->update_lock); - data->pwm[attr->index] = pwm << 8; - err = i2c_smbus_write_word_swapped(client, - MAX31790_REG_PWMOUT(attr->index), - data->pwm[attr->index]); + switch (attr) { + case hwmon_pwm_input: + if (val < 0 || val > 255) { + err = -EINVAL; + break; + } + data->pwm[channel] = val << 8; + err = i2c_smbus_write_word_swapped(client, + MAX31790_REG_PWMOUT(channel), + val); + break; + case hwmon_pwm_enable: + fan_config = data->fan_config[channel]; + if (val == 0) { + fan_config &= ~(MAX31790_FAN_CFG_TACH_INPUT_EN | + MAX31790_FAN_CFG_RPM_MODE); + } else if (val == 1) { + fan_config = (fan_config | + MAX31790_FAN_CFG_TACH_INPUT_EN) & + ~MAX31790_FAN_CFG_RPM_MODE; + } else if (val == 2) { + fan_config |= MAX31790_FAN_CFG_TACH_INPUT_EN | + MAX31790_FAN_CFG_RPM_MODE; + } else { + err = -EINVAL; + break; + } + data->fan_config[channel] = fan_config; + err = i2c_smbus_write_byte_data(client, + MAX31790_REG_FAN_CONFIG(channel), + fan_config); + break; + default: + err = -EOPNOTSUPP; + break; + } mutex_unlock(&data->update_lock); - if (err < 0) - return err; - - return count; + return err; } -static ssize_t get_pwm_enable(struct device *dev, - struct device_attribute *devattr, char *buf) +static umode_t max31790_pwm_is_visible(const void *_data, u32 attr, int channel) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct max31790_data *data = max31790_update_device(dev); - int mode; - - if (IS_ERR(data)) - return PTR_ERR(data); - - if (data->fan_config[attr->index] & MAX31790_FAN_CFG_RPM_MODE) - mode = 2; - else if (data->fan_config[attr->index] & MAX31790_FAN_CFG_TACH_INPUT_EN) - mode = 1; - else - mode = 0; - - return sprintf(buf, "%d\n", mode); + const struct max31790_data *data = _data; + u8 fan_config = data->fan_config[channel]; + + switch (attr) { + case hwmon_pwm_input: + case hwmon_pwm_enable: + if (!(fan_config & MAX31790_FAN_CFG_TACH_INPUT)) + return S_IRUGO | S_IWUSR; + return 0; + default: + return 0; + } } -static ssize_t set_pwm_enable(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) +static int max31790_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct max31790_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - unsigned long mode; - int err; - - err = kstrtoul(buf, 10, &mode); - if (err) - return err; - - switch (mode) { - case 0: - data->fan_config[attr->index] = - data->fan_config[attr->index] - & ~(MAX31790_FAN_CFG_TACH_INPUT_EN - | MAX31790_FAN_CFG_RPM_MODE); - break; - case 1: - data->fan_config[attr->index] = - (data->fan_config[attr->index] - | MAX31790_FAN_CFG_TACH_INPUT_EN) - & ~MAX31790_FAN_CFG_RPM_MODE; - break; - case 2: - data->fan_config[attr->index] = - data->fan_config[attr->index] - | MAX31790_FAN_CFG_TACH_INPUT_EN - | MAX31790_FAN_CFG_RPM_MODE; - break; + switch (type) { + case hwmon_fan: + return max31790_read_fan(dev, attr, channel, val); + case hwmon_pwm: + return max31790_read_pwm(dev, attr, channel, val); default: - return -EINVAL; + return -EOPNOTSUPP; } - - mutex_lock(&data->update_lock); - - err = i2c_smbus_write_byte_data(client, - MAX31790_REG_FAN_CONFIG(attr->index), - data->fan_config[attr->index]); - - mutex_unlock(&data->update_lock); - - if (err < 0) - return err; - - return count; } -static ssize_t get_fan_fault(struct device *dev, - struct device_attribute *devattr, char *buf) +static int max31790_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct max31790_data *data = max31790_update_device(dev); - int fault; - - if (IS_ERR(data)) - return PTR_ERR(data); - - fault = !!(data->fault_status & (1 << attr->index)); + switch (type) { + case hwmon_fan: + return max31790_write_fan(dev, attr, channel, val); + case hwmon_pwm: + return max31790_write_pwm(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} - return sprintf(buf, "%d\n", fault); +static umode_t max31790_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_fan: + return max31790_fan_is_visible(data, attr, channel); + case hwmon_pwm: + return max31790_pwm_is_visible(data, attr, channel); + default: + return 0; + } } -static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, 0); -static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1); -static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2); -static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, get_fan, NULL, 3); -static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, get_fan, NULL, 4); -static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, get_fan, NULL, 5); - -static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, get_fan_fault, NULL, 0); -static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, get_fan_fault, NULL, 1); -static SENSOR_DEVICE_ATTR(fan3_fault, S_IRUGO, get_fan_fault, NULL, 2); -static SENSOR_DEVICE_ATTR(fan4_fault, S_IRUGO, get_fan_fault, NULL, 3); -static SENSOR_DEVICE_ATTR(fan5_fault, S_IRUGO, get_fan_fault, NULL, 4); -static SENSOR_DEVICE_ATTR(fan6_fault, S_IRUGO, get_fan_fault, NULL, 5); - -static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, get_fan, NULL, 6); -static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, get_fan, NULL, 7); -static SENSOR_DEVICE_ATTR(fan9_input, S_IRUGO, get_fan, NULL, 8); -static SENSOR_DEVICE_ATTR(fan10_input, S_IRUGO, get_fan, NULL, 9); -static SENSOR_DEVICE_ATTR(fan11_input, S_IRUGO, get_fan, NULL, 10); -static SENSOR_DEVICE_ATTR(fan12_input, S_IRUGO, get_fan, NULL, 11); - -static SENSOR_DEVICE_ATTR(fan7_fault, S_IRUGO, get_fan_fault, NULL, 6); -static SENSOR_DEVICE_ATTR(fan8_fault, S_IRUGO, get_fan_fault, NULL, 7); -static SENSOR_DEVICE_ATTR(fan9_fault, S_IRUGO, get_fan_fault, NULL, 8); -static SENSOR_DEVICE_ATTR(fan10_fault, S_IRUGO, get_fan_fault, NULL, 9); -static SENSOR_DEVICE_ATTR(fan11_fault, S_IRUGO, get_fan_fault, NULL, 10); -static SENSOR_DEVICE_ATTR(fan12_fault, S_IRUGO, get_fan_fault, NULL, 11); - -static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, - get_fan_target, set_fan_target, 0); -static SENSOR_DEVICE_ATTR(fan2_target, S_IWUSR | S_IRUGO, - get_fan_target, set_fan_target, 1); -static SENSOR_DEVICE_ATTR(fan3_target, S_IWUSR | S_IRUGO, - get_fan_target, set_fan_target, 2); -static SENSOR_DEVICE_ATTR(fan4_target, S_IWUSR | S_IRUGO, - get_fan_target, set_fan_target, 3); -static SENSOR_DEVICE_ATTR(fan5_target, S_IWUSR | S_IRUGO, - get_fan_target, set_fan_target, 4); -static SENSOR_DEVICE_ATTR(fan6_target, S_IWUSR | S_IRUGO, - get_fan_target, set_fan_target, 5); - -static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 0); -static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 1); -static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 2); -static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 3); -static SENSOR_DEVICE_ATTR(pwm5, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 4); -static SENSOR_DEVICE_ATTR(pwm6, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 5); - -static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, - get_pwm_enable, set_pwm_enable, 0); -static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, - get_pwm_enable, set_pwm_enable, 1); -static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, - get_pwm_enable, set_pwm_enable, 2); -static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, - get_pwm_enable, set_pwm_enable, 3); -static SENSOR_DEVICE_ATTR(pwm5_enable, S_IWUSR | S_IRUGO, - get_pwm_enable, set_pwm_enable, 4); -static SENSOR_DEVICE_ATTR(pwm6_enable, S_IWUSR | S_IRUGO, - get_pwm_enable, set_pwm_enable, 5); - -static struct attribute *max31790_attrs[] = { - &sensor_dev_attr_fan1_input.dev_attr.attr, - &sensor_dev_attr_fan2_input.dev_attr.attr, - &sensor_dev_attr_fan3_input.dev_attr.attr, - &sensor_dev_attr_fan4_input.dev_attr.attr, - &sensor_dev_attr_fan5_input.dev_attr.attr, - &sensor_dev_attr_fan6_input.dev_attr.attr, - - &sensor_dev_attr_fan1_fault.dev_attr.attr, - &sensor_dev_attr_fan2_fault.dev_attr.attr, - &sensor_dev_attr_fan3_fault.dev_attr.attr, - &sensor_dev_attr_fan4_fault.dev_attr.attr, - &sensor_dev_attr_fan5_fault.dev_attr.attr, - &sensor_dev_attr_fan6_fault.dev_attr.attr, - - &sensor_dev_attr_fan7_input.dev_attr.attr, - &sensor_dev_attr_fan8_input.dev_attr.attr, - &sensor_dev_attr_fan9_input.dev_attr.attr, - &sensor_dev_attr_fan10_input.dev_attr.attr, - &sensor_dev_attr_fan11_input.dev_attr.attr, - &sensor_dev_attr_fan12_input.dev_attr.attr, - - &sensor_dev_attr_fan7_fault.dev_attr.attr, - &sensor_dev_attr_fan8_fault.dev_attr.attr, - &sensor_dev_attr_fan9_fault.dev_attr.attr, - &sensor_dev_attr_fan10_fault.dev_attr.attr, - &sensor_dev_attr_fan11_fault.dev_attr.attr, - &sensor_dev_attr_fan12_fault.dev_attr.attr, - - &sensor_dev_attr_fan1_target.dev_attr.attr, - &sensor_dev_attr_fan2_target.dev_attr.attr, - &sensor_dev_attr_fan3_target.dev_attr.attr, - &sensor_dev_attr_fan4_target.dev_attr.attr, - &sensor_dev_attr_fan5_target.dev_attr.attr, - &sensor_dev_attr_fan6_target.dev_attr.attr, - - &sensor_dev_attr_pwm1.dev_attr.attr, - &sensor_dev_attr_pwm2.dev_attr.attr, - &sensor_dev_attr_pwm3.dev_attr.attr, - &sensor_dev_attr_pwm4.dev_attr.attr, - &sensor_dev_attr_pwm5.dev_attr.attr, - &sensor_dev_attr_pwm6.dev_attr.attr, - - &sensor_dev_attr_pwm1_enable.dev_attr.attr, - &sensor_dev_attr_pwm2_enable.dev_attr.attr, - &sensor_dev_attr_pwm3_enable.dev_attr.attr, - &sensor_dev_attr_pwm4_enable.dev_attr.attr, - &sensor_dev_attr_pwm5_enable.dev_attr.attr, - &sensor_dev_attr_pwm6_enable.dev_attr.attr, - NULL +static const u32 max31790_fan_config[] = { + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT, + 0 }; -static umode_t max31790_attrs_visible(struct kobject *kobj, - struct attribute *a, int n) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct max31790_data *data = dev_get_drvdata(dev); - struct device_attribute *devattr = - container_of(a, struct device_attribute, attr); - int index = to_sensor_dev_attr(devattr)->index % NR_CHANNEL; - u8 fan_config; +static const struct hwmon_channel_info max31790_fan = { + .type = hwmon_fan, + .config = max31790_fan_config, +}; - fan_config = data->fan_config[index]; +static const u32 max31790_pwm_config[] = { + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + 0 +}; - if (n >= NR_CHANNEL * 2 && n < NR_CHANNEL * 4 && - !(fan_config & MAX31790_FAN_CFG_TACH_INPUT)) - return 0; - if (n >= NR_CHANNEL * 4 && (fan_config & MAX31790_FAN_CFG_TACH_INPUT)) - return 0; +static const struct hwmon_channel_info max31790_pwm = { + .type = hwmon_pwm, + .config = max31790_pwm_config, +}; - return a->mode; -} +static const struct hwmon_channel_info *max31790_info[] = { + &max31790_fan, + &max31790_pwm, + NULL +}; + +static const struct hwmon_ops max31790_hwmon_ops = { + .is_visible = max31790_is_visible, + .read = max31790_read, + .write = max31790_write, +}; -static const struct attribute_group max31790_group = { - .attrs = max31790_attrs, - .is_visible = max31790_attrs_visible, +static const struct hwmon_chip_info max31790_chip_info = { + .ops = &max31790_hwmon_ops, + .info = max31790_info, }; -__ATTRIBUTE_GROUPS(max31790); static int max31790_init_client(struct i2c_client *client, struct max31790_data *data) @@ -575,8 +500,10 @@ static int max31790_probe(struct i2c_client *client, if (err) return err; - hwmon_dev = devm_hwmon_device_register_with_groups(dev, - client->name, data, max31790_groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, + &max31790_chip_info, + NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index 162a520f4bd6..a993b44ed538 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -39,6 +39,7 @@ #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> #include <linux/err.h> +#include <linux/of_device.h> /* * Insmod parameters @@ -48,7 +49,7 @@ static int fan_voltage; /* prescaler: Possible values are 1, 2, 4, 8, 16 or 0 for don't change */ static int prescaler; -/* clock: The clock frequency of the chip the driver should assume */ +/* clock: The clock frequency of the chip (max6651 can be clocked externally) */ static int clock = 254000; module_param(fan_voltage, int, S_IRUGO); @@ -133,6 +134,19 @@ static const u8 tach_reg[] = { MAX6650_REG_TACH3, }; +static const struct of_device_id max6650_dt_match[] = { + { + .compatible = "maxim,max6650", + .data = (void *)1 + }, + { + .compatible = "maxim,max6651", + .data = (void *)4 + }, + { }, +}; +MODULE_DEVICE_TABLE(of, max6650_dt_match); + static struct max6650_data *max6650_update_device(struct device *dev) { struct max6650_data *data = dev_get_drvdata(dev); @@ -171,6 +185,30 @@ static struct max6650_data *max6650_update_device(struct device *dev) return data; } +/* + * Change the operating mode of the chip (if needed). + * mode is one of the MAX6650_CFG_MODE_* values. + */ +static int max6650_set_operating_mode(struct max6650_data *data, u8 mode) +{ + int result; + u8 config = data->config; + + if (mode == (config & MAX6650_CFG_MODE_MASK)) + return 0; + + config = (config & ~MAX6650_CFG_MODE_MASK) | mode; + + result = i2c_smbus_write_byte_data(data->client, MAX6650_REG_CONFIG, + config); + if (result < 0) + return result; + + data->config = config; + + return 0; +} + static ssize_t get_fan(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -252,18 +290,12 @@ static ssize_t get_target(struct device *dev, struct device_attribute *devattr, return sprintf(buf, "%d\n", rpm); } -static ssize_t set_target(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +static int max6650_set_target(struct max6650_data *data, unsigned long rpm) { - struct max6650_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; int kscale, ktach; - unsigned long rpm; - int err; - err = kstrtoul(buf, 10, &rpm); - if (err) - return err; + if (rpm == 0) + return max6650_set_operating_mode(data, MAX6650_CFG_MODE_OFF); rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX); @@ -274,8 +306,6 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr, * KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1 */ - mutex_lock(&data->update_lock); - kscale = DIV_FROM_REG(data->config); ktach = ((clock * kscale) / (256 * rpm / 60)) - 1; if (ktach < 0) @@ -284,10 +314,30 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr, ktach = 255; data->speed = ktach; - i2c_smbus_write_byte_data(client, MAX6650_REG_SPEED, data->speed); + return i2c_smbus_write_byte_data(data->client, MAX6650_REG_SPEED, + data->speed); +} + +static ssize_t set_target(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct max6650_data *data = dev_get_drvdata(dev); + unsigned long rpm; + int err; + + err = kstrtoul(buf, 10, &rpm); + if (err) + return err; + + mutex_lock(&data->update_lock); + + err = max6650_set_target(data, rpm); mutex_unlock(&data->update_lock); + if (err < 0) + return err; + return count; } @@ -341,12 +391,11 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, data->dac = 180 - (180 * pwm)/255; else data->dac = 76 - (76 * pwm)/255; - - i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, data->dac); + err = i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, data->dac); mutex_unlock(&data->update_lock); - return count; + return err < 0 ? err : count; } /* @@ -355,14 +404,14 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, * 0 = Fan always on * 1 = Open loop, Voltage is set according to speed, not regulated. * 2 = Closed loop, RPM for all fans regulated by fan1 tachometer + * 3 = Fan off */ - static ssize_t get_enable(struct device *dev, struct device_attribute *devattr, char *buf) { struct max6650_data *data = max6650_update_device(dev); int mode = (data->config & MAX6650_CFG_MODE_MASK) >> 4; - int sysfs_modes[4] = {0, 1, 2, 1}; + int sysfs_modes[4] = {0, 3, 2, 1}; return sprintf(buf, "%d\n", sysfs_modes[mode]); } @@ -371,25 +420,25 @@ static ssize_t set_enable(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct max6650_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int max6650_modes[3] = {0, 3, 2}; unsigned long mode; int err; + const u8 max6650_modes[] = { + MAX6650_CFG_MODE_ON, + MAX6650_CFG_MODE_OPEN_LOOP, + MAX6650_CFG_MODE_CLOSED_LOOP, + MAX6650_CFG_MODE_OFF, + }; err = kstrtoul(buf, 10, &mode); if (err) return err; - if (mode > 2) + if (mode >= ARRAY_SIZE(max6650_modes)) return -EINVAL; mutex_lock(&data->update_lock); - data->config = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG); - data->config = (data->config & ~MAX6650_CFG_MODE_MASK) - | (max6650_modes[mode] << 4); - - i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, data->config); + max6650_set_operating_mode(data, max6650_modes[mode]); mutex_unlock(&data->update_lock); @@ -566,6 +615,18 @@ static int max6650_init_client(struct max6650_data *data, struct device *dev = &client->dev; int config; int err = -EIO; + u32 voltage; + u32 prescale; + u32 target_rpm; + + if (of_property_read_u32(dev->of_node, "maxim,fan-microvolt", + &voltage)) + voltage = fan_voltage; + else + voltage /= 1000000; /* Microvolts to volts */ + if (of_property_read_u32(dev->of_node, "maxim,fan-prescale", + &prescale)) + prescale = prescaler; config = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG); @@ -574,7 +635,7 @@ static int max6650_init_client(struct max6650_data *data, return err; } - switch (fan_voltage) { + switch (voltage) { case 0: break; case 5: @@ -584,14 +645,10 @@ static int max6650_init_client(struct max6650_data *data, config |= MAX6650_CFG_V12; break; default: - dev_err(dev, "illegal value for fan_voltage (%d)\n", - fan_voltage); + dev_err(dev, "illegal value for fan_voltage (%d)\n", voltage); } - dev_info(dev, "Fan voltage is set to %dV.\n", - (config & MAX6650_CFG_V12) ? 12 : 5); - - switch (prescaler) { + switch (prescale) { case 0: break; case 1: @@ -614,28 +671,13 @@ static int max6650_init_client(struct max6650_data *data, | MAX6650_CFG_PRESCALER_16; break; default: - dev_err(dev, "illegal value for prescaler (%d)\n", prescaler); + dev_err(dev, "illegal value for prescaler (%d)\n", prescale); } - dev_info(dev, "Prescaler is set to %d.\n", + dev_info(dev, "Fan voltage: %dV, prescaler: %d.\n", + (config & MAX6650_CFG_V12) ? 12 : 5, 1 << (config & MAX6650_CFG_PRESCALER_MASK)); - /* - * If mode is set to "full off", we change it to "open loop" and - * set DAC to 255, which has the same effect. We do this because - * there's no "full off" mode defined in hwmon specifications. - */ - - if ((config & MAX6650_CFG_MODE_MASK) == MAX6650_CFG_MODE_OFF) { - dev_dbg(dev, "Change mode to open loop, full off.\n"); - config = (config & ~MAX6650_CFG_MODE_MASK) - | MAX6650_CFG_MODE_OPEN_LOOP; - if (i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, 255)) { - dev_err(dev, "DAC write error, aborting.\n"); - return err; - } - } - if (i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, config)) { dev_err(dev, "Config write error, aborting.\n"); return err; @@ -644,6 +686,12 @@ static int max6650_init_client(struct max6650_data *data, data->config = config; data->count = i2c_smbus_read_byte_data(client, MAX6650_REG_COUNT); + if (!of_property_read_u32(client->dev.of_node, "maxim,fan-target-rpm", + &target_rpm)) { + max6650_set_target(data, target_rpm); + max6650_set_operating_mode(data, MAX6650_CFG_MODE_CLOSED_LOOP); + } + return 0; } @@ -651,6 +699,8 @@ static int max6650_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; + const struct of_device_id *of_id = + of_match_device(of_match_ptr(max6650_dt_match), dev); struct max6650_data *data; struct device *hwmon_dev; int err; @@ -661,7 +711,7 @@ static int max6650_probe(struct i2c_client *client, data->client = client; mutex_init(&data->update_lock); - data->nr_fans = id->driver_data; + data->nr_fans = of_id ? (int)(uintptr_t)of_id->data : id->driver_data; /* * Initialize the max6650 chip @@ -691,6 +741,7 @@ MODULE_DEVICE_TABLE(i2c, max6650_id); static struct i2c_driver max6650_driver = { .driver = { .name = "max6650", + .of_match_table = of_match_ptr(max6650_dt_match), }, .probe = max6650_probe, .id_table = max6650_id, diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index d087a8e00cf5..ce75dd4db7eb 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -195,6 +195,8 @@ superio_exit(int ioreg) #define NUM_FAN 6 +#define TEMP_SOURCE_VIRTUAL 0x1f + /* Common and NCT6775 specific data */ /* Voltage min/max registers for nr=7..14 are in bank 5 */ @@ -3940,7 +3942,7 @@ static int nct6775_probe(struct platform_device *pdev) continue; src = nct6775_read_value(data, data->REG_TEMP_SEL[i]) & 0x1f; - if (!src || (mask & (1 << src))) + if (!src) continue; if (src >= data->temp_label_num || @@ -3952,7 +3954,16 @@ static int nct6775_probe(struct platform_device *pdev) continue; } - mask |= 1 << src; + /* + * For virtual temperature sources, the 'virtual' temperature + * for each fan reflects a different temperature, and there + * are no duplicates. + */ + if (src != TEMP_SOURCE_VIRTUAL) { + if (mask & (1 << src)) + continue; + mask |= 1 << src; + } /* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */ if (src <= data->temp_fixed_num) { @@ -4232,11 +4243,11 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) if (err) return err; - if (force_id) + val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8) | + superio_inb(sioaddr, SIO_REG_DEVID + 1); + if (force_id && val != 0xffff) val = force_id; - else - val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8) - | superio_inb(sioaddr, SIO_REG_DEVID + 1); + switch (val & SIO_ID_MASK) { case SIO_NCT6106_ID: sio_data->kind = nct6106; diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c index 08ff89d222e5..95a68ab175c7 100644 --- a/drivers/hwmon/nct7904.c +++ b/drivers/hwmon/nct7904.c @@ -21,7 +21,6 @@ #include <linux/i2c.h> #include <linux/mutex.h> #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> #define VENDOR_ID_REG 0x7A /* Any bank */ #define NUVOTON_ID 0x50 @@ -153,341 +152,230 @@ static int nct7904_write_reg(struct nct7904_data *data, return ret; } -/* FANIN ATTR */ -static ssize_t show_fan(struct device *dev, - struct device_attribute *devattr, char *buf) +static int nct7904_read_fan(struct device *dev, u32 attr, int channel, + long *val) { - int index = to_sensor_dev_attr(devattr)->index; struct nct7904_data *data = dev_get_drvdata(dev); + unsigned int cnt, rpm; int ret; - unsigned cnt, rpm; - ret = nct7904_read_reg16(data, BANK_0, FANIN1_HV_REG + index * 2); - if (ret < 0) - return ret; - cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f); - if (cnt == 0x1fff) - rpm = 0; - else - rpm = 1350000 / cnt; - return sprintf(buf, "%u\n", rpm); + switch(attr) { + case hwmon_fan_input: + ret = nct7904_read_reg16(data, BANK_0, + FANIN1_HV_REG + channel * 2); + if (ret < 0) + return ret; + cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f); + if (cnt == 0x1fff) + rpm = 0; + else + rpm = 1350000 / cnt; + *val = rpm; + return 0; + default: + return -EOPNOTSUPP; + } } -static umode_t nct7904_fanin_is_visible(struct kobject *kobj, - struct attribute *a, int n) +static umode_t nct7904_fan_is_visible(const void *_data, u32 attr, int channel) { - struct device *dev = container_of(kobj, struct device, kobj); - struct nct7904_data *data = dev_get_drvdata(dev); + const struct nct7904_data *data = _data; - if (data->fanin_mask & (1 << n)) - return a->mode; + if (attr == hwmon_fan_input && data->fanin_mask & (1 << channel)) + return S_IRUGO; return 0; } -static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); -static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); -static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2); -static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3); -static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4); -static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan, NULL, 5); -static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan, NULL, 6); -static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, show_fan, NULL, 7); -static SENSOR_DEVICE_ATTR(fan9_input, S_IRUGO, show_fan, NULL, 8); -static SENSOR_DEVICE_ATTR(fan10_input, S_IRUGO, show_fan, NULL, 9); -static SENSOR_DEVICE_ATTR(fan11_input, S_IRUGO, show_fan, NULL, 10); -static SENSOR_DEVICE_ATTR(fan12_input, S_IRUGO, show_fan, NULL, 11); - -static struct attribute *nct7904_fanin_attrs[] = { - &sensor_dev_attr_fan1_input.dev_attr.attr, - &sensor_dev_attr_fan2_input.dev_attr.attr, - &sensor_dev_attr_fan3_input.dev_attr.attr, - &sensor_dev_attr_fan4_input.dev_attr.attr, - &sensor_dev_attr_fan5_input.dev_attr.attr, - &sensor_dev_attr_fan6_input.dev_attr.attr, - &sensor_dev_attr_fan7_input.dev_attr.attr, - &sensor_dev_attr_fan8_input.dev_attr.attr, - &sensor_dev_attr_fan9_input.dev_attr.attr, - &sensor_dev_attr_fan10_input.dev_attr.attr, - &sensor_dev_attr_fan11_input.dev_attr.attr, - &sensor_dev_attr_fan12_input.dev_attr.attr, - NULL -}; - -static const struct attribute_group nct7904_fanin_group = { - .attrs = nct7904_fanin_attrs, - .is_visible = nct7904_fanin_is_visible, +static u8 nct7904_chan_to_index[] = { + 0, /* Not used */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 18, 19, 20, 16 }; -/* VSEN ATTR */ -static ssize_t show_voltage(struct device *dev, - struct device_attribute *devattr, char *buf) +static int nct7904_read_in(struct device *dev, u32 attr, int channel, + long *val) { - int index = to_sensor_dev_attr(devattr)->index; struct nct7904_data *data = dev_get_drvdata(dev); - int ret; - int volt; + int ret, volt, index; - ret = nct7904_read_reg16(data, BANK_0, VSEN1_HV_REG + index * 2); - if (ret < 0) - return ret; - volt = ((ret & 0xff00) >> 5) | (ret & 0x7); - if (index < 14) - volt *= 2; /* 0.002V scale */ - else - volt *= 6; /* 0.006V scale */ + index = nct7904_chan_to_index[channel]; - return sprintf(buf, "%d\n", volt); + switch(attr) { + case hwmon_in_input: + ret = nct7904_read_reg16(data, BANK_0, + VSEN1_HV_REG + index * 2); + if (ret < 0) + return ret; + volt = ((ret & 0xff00) >> 5) | (ret & 0x7); + if (index < 14) + volt *= 2; /* 0.002V scale */ + else + volt *= 6; /* 0.006V scale */ + *val = volt; + return 0; + default: + return -EOPNOTSUPP; + } } -static ssize_t show_ltemp(struct device *dev, - struct device_attribute *devattr, char *buf) +static umode_t nct7904_in_is_visible(const void *_data, u32 attr, int channel) { - struct nct7904_data *data = dev_get_drvdata(dev); - int ret; - int temp; + const struct nct7904_data *data = _data; + int index = nct7904_chan_to_index[channel]; - ret = nct7904_read_reg16(data, BANK_0, LTD_HV_REG); - if (ret < 0) - return ret; - temp = ((ret & 0xff00) >> 5) | (ret & 0x7); - temp = sign_extend32(temp, 10) * 125; - - return sprintf(buf, "%d\n", temp); -} - -static umode_t nct7904_vsen_is_visible(struct kobject *kobj, - struct attribute *a, int n) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct nct7904_data *data = dev_get_drvdata(dev); + if (channel > 0 && attr == hwmon_in_input && + (data->vsen_mask & BIT(index))) + return S_IRUGO; - if (data->vsen_mask & (1 << n)) - return a->mode; return 0; } -static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_voltage, NULL, 0); -static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_voltage, NULL, 1); -static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_voltage, NULL, 2); -static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_voltage, NULL, 3); -static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_voltage, NULL, 4); -static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_voltage, NULL, 5); -static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_voltage, NULL, 6); -static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_voltage, NULL, 7); -static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, show_voltage, NULL, 8); -static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, show_voltage, NULL, 9); -static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, show_voltage, NULL, 10); -static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, show_voltage, NULL, 11); -static SENSOR_DEVICE_ATTR(in13_input, S_IRUGO, show_voltage, NULL, 12); -static SENSOR_DEVICE_ATTR(in14_input, S_IRUGO, show_voltage, NULL, 13); -/* - * Next 3 voltage sensors have specific names in the Nuvoton doc - * (3VDD, VBAT, 3VSB) but we use vacant numbers for them. - */ -static SENSOR_DEVICE_ATTR(in15_input, S_IRUGO, show_voltage, NULL, 14); -static SENSOR_DEVICE_ATTR(in16_input, S_IRUGO, show_voltage, NULL, 15); -static SENSOR_DEVICE_ATTR(in20_input, S_IRUGO, show_voltage, NULL, 16); -/* This is not a voltage, but a local temperature sensor. */ -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_ltemp, NULL, 0); -static SENSOR_DEVICE_ATTR(in17_input, S_IRUGO, show_voltage, NULL, 18); -static SENSOR_DEVICE_ATTR(in18_input, S_IRUGO, show_voltage, NULL, 19); -static SENSOR_DEVICE_ATTR(in19_input, S_IRUGO, show_voltage, NULL, 20); - -static struct attribute *nct7904_vsen_attrs[] = { - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_in2_input.dev_attr.attr, - &sensor_dev_attr_in3_input.dev_attr.attr, - &sensor_dev_attr_in4_input.dev_attr.attr, - &sensor_dev_attr_in5_input.dev_attr.attr, - &sensor_dev_attr_in6_input.dev_attr.attr, - &sensor_dev_attr_in7_input.dev_attr.attr, - &sensor_dev_attr_in8_input.dev_attr.attr, - &sensor_dev_attr_in9_input.dev_attr.attr, - &sensor_dev_attr_in10_input.dev_attr.attr, - &sensor_dev_attr_in11_input.dev_attr.attr, - &sensor_dev_attr_in12_input.dev_attr.attr, - &sensor_dev_attr_in13_input.dev_attr.attr, - &sensor_dev_attr_in14_input.dev_attr.attr, - &sensor_dev_attr_in15_input.dev_attr.attr, - &sensor_dev_attr_in16_input.dev_attr.attr, - &sensor_dev_attr_in20_input.dev_attr.attr, - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_in17_input.dev_attr.attr, - &sensor_dev_attr_in18_input.dev_attr.attr, - &sensor_dev_attr_in19_input.dev_attr.attr, - NULL -}; - -static const struct attribute_group nct7904_vsen_group = { - .attrs = nct7904_vsen_attrs, - .is_visible = nct7904_vsen_is_visible, -}; - -/* CPU_TEMP ATTR */ -static ssize_t show_tcpu(struct device *dev, - struct device_attribute *devattr, char *buf) +static int nct7904_read_temp(struct device *dev, u32 attr, int channel, + long *val) { - int index = to_sensor_dev_attr(devattr)->index; struct nct7904_data *data = dev_get_drvdata(dev); - int ret; - int temp; - - ret = nct7904_read_reg16(data, BANK_0, T_CPU1_HV_REG + index * 2); - if (ret < 0) - return ret; - - temp = ((ret & 0xff00) >> 5) | (ret & 0x7); - temp = sign_extend32(temp, 10) * 125; - return sprintf(buf, "%d\n", temp); + int ret, temp; + + switch(attr) { + case hwmon_temp_input: + if (channel == 0) + ret = nct7904_read_reg16(data, BANK_0, LTD_HV_REG); + else + ret = nct7904_read_reg16(data, BANK_0, + T_CPU1_HV_REG + (channel - 1) * 2); + if (ret < 0) + return ret; + temp = ((ret & 0xff00) >> 5) | (ret & 0x7); + *val = sign_extend32(temp, 10) * 125; + return 0; + default: + return -EOPNOTSUPP; + } } -static umode_t nct7904_tcpu_is_visible(struct kobject *kobj, - struct attribute *a, int n) +static umode_t nct7904_temp_is_visible(const void *_data, u32 attr, int channel) { - struct device *dev = container_of(kobj, struct device, kobj); - struct nct7904_data *data = dev_get_drvdata(dev); + const struct nct7904_data *data = _data; + + if (attr == hwmon_temp_input) { + if (channel == 0) { + if (data->vsen_mask & BIT(17)) + return S_IRUGO; + } else { + if (data->tcpu_mask & BIT(channel - 1)) + return S_IRUGO; + } + } - if (data->tcpu_mask & (1 << n)) - return a->mode; return 0; } -/* "temp1_input" reserved for local temp */ -static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_tcpu, NULL, 0); -static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_tcpu, NULL, 1); -static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_tcpu, NULL, 2); -static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_tcpu, NULL, 3); -static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_tcpu, NULL, 4); -static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, show_tcpu, NULL, 5); -static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, show_tcpu, NULL, 6); -static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO, show_tcpu, NULL, 7); - -static struct attribute *nct7904_tcpu_attrs[] = { - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp4_input.dev_attr.attr, - &sensor_dev_attr_temp5_input.dev_attr.attr, - &sensor_dev_attr_temp6_input.dev_attr.attr, - &sensor_dev_attr_temp7_input.dev_attr.attr, - &sensor_dev_attr_temp8_input.dev_attr.attr, - &sensor_dev_attr_temp9_input.dev_attr.attr, - NULL -}; - -static const struct attribute_group nct7904_tcpu_group = { - .attrs = nct7904_tcpu_attrs, - .is_visible = nct7904_tcpu_is_visible, -}; - -/* PWM ATTR */ -static ssize_t store_pwm(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +static int nct7904_read_pwm(struct device *dev, u32 attr, int channel, + long *val) { - int index = to_sensor_dev_attr(devattr)->index; struct nct7904_data *data = dev_get_drvdata(dev); - unsigned long val; int ret; - if (kstrtoul(buf, 10, &val) < 0) - return -EINVAL; - if (val > 255) - return -EINVAL; - - ret = nct7904_write_reg(data, BANK_3, FANCTL1_OUT_REG + index, val); + switch(attr) { + case hwmon_pwm_input: + ret = nct7904_read_reg(data, BANK_3, FANCTL1_OUT_REG + channel); + if (ret < 0) + return ret; + *val = ret; + return 0; + case hwmon_pwm_enable: + ret = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + channel); + if (ret < 0) + return ret; - return ret ? ret : count; + *val = ret ? 2 : 1; + return 0; + default: + return -EOPNOTSUPP; + } } -static ssize_t show_pwm(struct device *dev, - struct device_attribute *devattr, char *buf) +static int nct7904_write_pwm(struct device *dev, u32 attr, int channel, + long val) { - int index = to_sensor_dev_attr(devattr)->index; struct nct7904_data *data = dev_get_drvdata(dev); - int val; - - val = nct7904_read_reg(data, BANK_3, FANCTL1_OUT_REG + index); - if (val < 0) - return val; + int ret; - return sprintf(buf, "%d\n", val); + switch(attr) { + case hwmon_pwm_input: + if (val < 0 || val > 255) + return -EINVAL; + ret = nct7904_write_reg(data, BANK_3, FANCTL1_OUT_REG + channel, + val); + return ret; + case hwmon_pwm_enable: + if (val < 1 || val > 2 || + (val == 2 && !data->fan_mode[channel])) + return -EINVAL; + ret = nct7904_write_reg(data, BANK_3, FANCTL1_FMR_REG + channel, + val == 2 ? data->fan_mode[channel] : 0); + return ret; + default: + return -EOPNOTSUPP; + } } -static ssize_t store_enable(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) +static umode_t nct7904_pwm_is_visible(const void *_data, u32 attr, int channel) { - int index = to_sensor_dev_attr(devattr)->index; - struct nct7904_data *data = dev_get_drvdata(dev); - unsigned long val; - int ret; - - if (kstrtoul(buf, 10, &val) < 0) - return -EINVAL; - if (val < 1 || val > 2 || (val == 2 && !data->fan_mode[index])) - return -EINVAL; - - ret = nct7904_write_reg(data, BANK_3, FANCTL1_FMR_REG + index, - val == 2 ? data->fan_mode[index] : 0); - - return ret ? ret : count; + switch(attr) { + case hwmon_pwm_input: + case hwmon_pwm_enable: + return S_IRUGO | S_IWUSR; + default: + return 0; + } } -/* Return 1 for manual mode or 2 for SmartFan mode */ -static ssize_t show_enable(struct device *dev, - struct device_attribute *devattr, char *buf) +static int nct7904_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) { - int index = to_sensor_dev_attr(devattr)->index; - struct nct7904_data *data = dev_get_drvdata(dev); - int val; - - val = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + index); - if (val < 0) - return val; - - return sprintf(buf, "%d\n", val ? 2 : 1); + switch (type) { + case hwmon_in: + return nct7904_read_in(dev, attr, channel, val); + case hwmon_fan: + return nct7904_read_fan(dev, attr, channel, val); + case hwmon_pwm: + return nct7904_read_pwm(dev, attr, channel, val); + case hwmon_temp: + return nct7904_read_temp(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } } -/* 2 attributes per channel: pwm and mode */ -static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, - show_pwm, store_pwm, 0); -static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, - show_enable, store_enable, 0); -static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, - show_pwm, store_pwm, 1); -static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR, - show_enable, store_enable, 1); -static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR, - show_pwm, store_pwm, 2); -static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR, - show_enable, store_enable, 2); -static SENSOR_DEVICE_ATTR(pwm4, S_IRUGO | S_IWUSR, - show_pwm, store_pwm, 3); -static SENSOR_DEVICE_ATTR(pwm4_enable, S_IRUGO | S_IWUSR, - show_enable, store_enable, 3); - -static struct attribute *nct7904_fanctl_attrs[] = { - &sensor_dev_attr_pwm1.dev_attr.attr, - &sensor_dev_attr_pwm1_enable.dev_attr.attr, - &sensor_dev_attr_pwm2.dev_attr.attr, - &sensor_dev_attr_pwm2_enable.dev_attr.attr, - &sensor_dev_attr_pwm3.dev_attr.attr, - &sensor_dev_attr_pwm3_enable.dev_attr.attr, - &sensor_dev_attr_pwm4.dev_attr.attr, - &sensor_dev_attr_pwm4_enable.dev_attr.attr, - NULL -}; - -static const struct attribute_group nct7904_fanctl_group = { - .attrs = nct7904_fanctl_attrs, -}; +static int nct7904_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_pwm: + return nct7904_write_pwm(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} -static const struct attribute_group *nct7904_groups[] = { - &nct7904_fanin_group, - &nct7904_vsen_group, - &nct7904_tcpu_group, - &nct7904_fanctl_group, - NULL -}; +static umode_t nct7904_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_in: + return nct7904_in_is_visible(data, attr, channel); + case hwmon_fan: + return nct7904_fan_is_visible(data, attr, channel); + case hwmon_pwm: + return nct7904_pwm_is_visible(data, attr, channel); + case hwmon_temp: + return nct7904_temp_is_visible(data, attr, channel); + default: + return 0; + } +} /* Return 0 if detection is successful, -ENODEV otherwise */ static int nct7904_detect(struct i2c_client *client, @@ -512,6 +400,103 @@ static int nct7904_detect(struct i2c_client *client, return 0; } +static const u32 nct7904_in_config[] = { + HWMON_I_INPUT, /* dummy, skipped in is_visible */ + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + 0 +}; + +static const struct hwmon_channel_info nct7904_in = { + .type = hwmon_in, + .config = nct7904_in_config, +}; + +static const u32 nct7904_fan_config[] = { + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + HWMON_F_INPUT, + 0 +}; + +static const struct hwmon_channel_info nct7904_fan = { + .type = hwmon_fan, + .config = nct7904_fan_config, +}; + +static const u32 nct7904_pwm_config[] = { + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + 0 +}; + +static const struct hwmon_channel_info nct7904_pwm = { + .type = hwmon_pwm, + .config = nct7904_pwm_config, +}; + +static const u32 nct7904_temp_config[] = { + HWMON_T_INPUT, + HWMON_T_INPUT, + HWMON_T_INPUT, + HWMON_T_INPUT, + HWMON_T_INPUT, + HWMON_T_INPUT, + HWMON_T_INPUT, + HWMON_T_INPUT, + HWMON_T_INPUT, + 0 +}; + +static const struct hwmon_channel_info nct7904_temp = { + .type = hwmon_temp, + .config = nct7904_temp_config, +}; + +static const struct hwmon_channel_info *nct7904_info[] = { + &nct7904_in, + &nct7904_fan, + &nct7904_pwm, + &nct7904_temp, + NULL +}; + +static const struct hwmon_ops nct7904_hwmon_ops = { + .is_visible = nct7904_is_visible, + .read = nct7904_read, + .write = nct7904_write, +}; + +static const struct hwmon_chip_info nct7904_chip_info = { + .ops = &nct7904_hwmon_ops, + .info = nct7904_info, +}; + static int nct7904_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -566,8 +551,8 @@ static int nct7904_probe(struct i2c_client *client, } hwmon_dev = - devm_hwmon_device_register_with_groups(dev, client->name, data, - nct7904_groups); + devm_hwmon_device_register_with_info(dev, client->name, data, + &nct7904_chip_info, NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index 8ef7b713cb1a..c52d07c6b49f 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -253,12 +253,9 @@ static const struct ntc_compensation b57330v2103[] = { }; struct ntc_data { - struct device *hwmon_dev; struct ntc_thermistor_platform_data *pdata; const struct ntc_compensation *comp; - struct device *dev; int n_comp; - char name[PLATFORM_NAME_SIZE]; }; #if defined(CONFIG_OF) && IS_ENABLED(CONFIG_IIO) @@ -316,22 +313,22 @@ static const struct of_device_id ntc_match[] = { MODULE_DEVICE_TABLE(of, ntc_match); static struct ntc_thermistor_platform_data * -ntc_thermistor_parse_dt(struct platform_device *pdev) +ntc_thermistor_parse_dt(struct device *dev) { struct iio_channel *chan; enum iio_chan_type type; - struct device_node *np = pdev->dev.of_node; + struct device_node *np = dev->of_node; struct ntc_thermistor_platform_data *pdata; int ret; if (!np) return NULL; - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return ERR_PTR(-ENOMEM); - chan = iio_channel_get(&pdev->dev, NULL); + chan = devm_iio_channel_get(dev, NULL); if (IS_ERR(chan)) return ERR_CAST(chan); @@ -359,22 +356,15 @@ ntc_thermistor_parse_dt(struct platform_device *pdev) return pdata; } -static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata) -{ - if (pdata->chan) - iio_channel_release(pdata->chan); -} #else static struct ntc_thermistor_platform_data * -ntc_thermistor_parse_dt(struct platform_device *pdev) +ntc_thermistor_parse_dt(struct device *dev) { return NULL; } #define ntc_match NULL -static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata) -{ } #endif static inline u64 div64_u64_safe(u64 dividend, u64 divisor) @@ -516,9 +506,8 @@ static int ntc_thermistor_get_ohm(struct ntc_data *data) return -EINVAL; } -static int ntc_read_temp(void *dev, int *temp) +static int ntc_read_temp(void *data, int *temp) { - struct ntc_data *data = dev_get_drvdata(dev); int ohm; ohm = ntc_thermistor_get_ohm(data); @@ -530,14 +519,6 @@ static int ntc_read_temp(void *dev, int *temp) return 0; } -static ssize_t ntc_show_name(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct ntc_data *data = dev_get_drvdata(dev); - - return sprintf(buf, "%s\n", data->name); -} - static ssize_t ntc_show_type(struct device *dev, struct device_attribute *attr, char *buf) { @@ -559,18 +540,13 @@ static ssize_t ntc_show_temp(struct device *dev, static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ntc_show_temp, NULL, 0); -static DEVICE_ATTR(name, S_IRUGO, ntc_show_name, NULL); -static struct attribute *ntc_attributes[] = { - &dev_attr_name.attr, +static struct attribute *ntc_attrs[] = { &sensor_dev_attr_temp1_type.dev_attr.attr, &sensor_dev_attr_temp1_input.dev_attr.attr, NULL, }; - -static const struct attribute_group ntc_attr_group = { - .attrs = ntc_attributes, -}; +ATTRIBUTE_GROUPS(ntc); static const struct thermal_zone_of_device_ops ntc_of_thermal_ops = { .get_temp = ntc_read_temp, @@ -579,33 +555,34 @@ static const struct thermal_zone_of_device_ops ntc_of_thermal_ops = { static int ntc_thermistor_probe(struct platform_device *pdev) { struct thermal_zone_device *tz; + struct device *dev = &pdev->dev; const struct of_device_id *of_id = - of_match_device(of_match_ptr(ntc_match), &pdev->dev); + of_match_device(of_match_ptr(ntc_match), dev); const struct platform_device_id *pdev_id; struct ntc_thermistor_platform_data *pdata; + struct device *hwmon_dev; struct ntc_data *data; - int ret; - pdata = ntc_thermistor_parse_dt(pdev); + pdata = ntc_thermistor_parse_dt(dev); if (IS_ERR(pdata)) return PTR_ERR(pdata); else if (pdata == NULL) - pdata = dev_get_platdata(&pdev->dev); + pdata = dev_get_platdata(dev); if (!pdata) { - dev_err(&pdev->dev, "No platform init data supplied.\n"); + dev_err(dev, "No platform init data supplied.\n"); return -ENODEV; } /* Either one of the two is required. */ if (!pdata->read_uv && !pdata->read_ohm) { - dev_err(&pdev->dev, + dev_err(dev, "Both read_uv and read_ohm missing. Need either one of the two.\n"); return -EINVAL; } if (pdata->read_uv && pdata->read_ohm) { - dev_warn(&pdev->dev, + dev_warn(dev, "Only one of read_uv and read_ohm is needed; ignoring read_uv.\n"); pdata->read_uv = NULL; } @@ -617,20 +594,17 @@ static int ntc_thermistor_probe(struct platform_device *pdev) NTC_CONNECTED_POSITIVE) || (pdata->connect != NTC_CONNECTED_POSITIVE && pdata->connect != NTC_CONNECTED_GROUND))) { - dev_err(&pdev->dev, - "Required data to use read_uv not supplied.\n"); + dev_err(dev, "Required data to use read_uv not supplied.\n"); return -EINVAL; } - data = devm_kzalloc(&pdev->dev, sizeof(struct ntc_data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct ntc_data), GFP_KERNEL); if (!data) return -ENOMEM; pdev_id = of_id ? of_id->data : platform_get_device_id(pdev); - data->dev = &pdev->dev; data->pdata = pdata; - strlcpy(data->name, pdev_id->name, sizeof(data->name)); switch (pdev_id->driver_data) { case TYPE_NCPXXWB473: @@ -654,49 +628,25 @@ static int ntc_thermistor_probe(struct platform_device *pdev) data->n_comp = ARRAY_SIZE(ncpXXxh103); break; default: - dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n", + dev_err(dev, "Unknown device type: %lu(%s)\n", pdev_id->driver_data, pdev_id->name); return -EINVAL; } - platform_set_drvdata(pdev, data); - - ret = sysfs_create_group(&data->dev->kobj, &ntc_attr_group); - if (ret) { - dev_err(data->dev, "unable to create sysfs files\n"); - return ret; - } - - data->hwmon_dev = hwmon_device_register(data->dev); - if (IS_ERR(data->hwmon_dev)) { - dev_err(data->dev, "unable to register as hwmon device.\n"); - ret = PTR_ERR(data->hwmon_dev); - goto err_after_sysfs; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, pdev_id->name, + data, ntc_groups); + if (IS_ERR(hwmon_dev)) { + dev_err(dev, "unable to register as hwmon device.\n"); + return PTR_ERR(hwmon_dev); } - dev_info(&pdev->dev, "Thermistor type: %s successfully probed.\n", - pdev_id->name); + dev_info(dev, "Thermistor type: %s successfully probed.\n", + pdev_id->name); - tz = devm_thermal_zone_of_sensor_register(data->dev, 0, data->dev, + tz = devm_thermal_zone_of_sensor_register(dev, 0, data, &ntc_of_thermal_ops); if (IS_ERR(tz)) - dev_dbg(&pdev->dev, "Failed to register to thermal fw.\n"); - - return 0; -err_after_sysfs: - sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); - ntc_iio_channel_release(pdata); - return ret; -} - -static int ntc_thermistor_remove(struct platform_device *pdev) -{ - struct ntc_data *data = platform_get_drvdata(pdev); - struct ntc_thermistor_platform_data *pdata = data->pdata; - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); - ntc_iio_channel_release(pdata); + dev_dbg(dev, "Failed to register to thermal fw.\n"); return 0; } @@ -707,7 +657,6 @@ static struct platform_driver ntc_thermistor_driver = { .of_match_table = of_match_ptr(ntc_match), }, .probe = ntc_thermistor_probe, - .remove = ntc_thermistor_remove, .id_table = ntc_thermistor_id, }; diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 054d3d863802..cad1229b7e17 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -126,12 +126,12 @@ config SENSORS_TPS40422 be called tps40422. config SENSORS_UCD9000 - tristate "TI UCD90120, UCD90124, UCD9090, UCD90910" + tristate "TI UCD90120, UCD90124, UCD90160, UCD9090, UCD90910" default n help If you say yes here you get hardware monitoring support for TI - UCD90120, UCD90124, UCD9090, UCD90910 Sequencer and System Health - Controllers. + UCD90120, UCD90124, UCD90160, UCD9090, UCD90910, Sequencer and System + Health Controllers. This driver can also be built as a module. If so, the module will be called ucd9000. diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index 0a74991a60f0..44ca8a94873d 100644 --- a/drivers/hwmon/pmbus/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c @@ -25,6 +25,7 @@ #include <linux/slab.h> #include <linux/mutex.h> #include <linux/i2c.h> +#include <linux/i2c/pmbus.h> #include "pmbus.h" /* @@ -167,14 +168,26 @@ static int pmbus_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct pmbus_driver_info *info; + struct pmbus_platform_data *pdata = NULL; + struct device *dev = &client->dev; - info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info), - GFP_KERNEL); + info = devm_kzalloc(dev, sizeof(struct pmbus_driver_info), GFP_KERNEL); if (!info) return -ENOMEM; + if (!strcmp(id->name, "dps460") || !strcmp(id->name, "dps800") || + !strcmp(id->name, "sgd009")) { + pdata = devm_kzalloc(dev, sizeof(struct pmbus_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->flags = PMBUS_SKIP_STATUS_CHECK; + } + info->pages = id->driver_data; info->identify = pmbus_identify; + dev->platform_data = pdata; return pmbus_do_probe(client, id, info); } @@ -186,6 +199,8 @@ static const struct i2c_device_id pmbus_id[] = { {"adp4000", 1}, {"bmr453", 1}, {"bmr454", 1}, + {"dps460", 1}, + {"dps800", 1}, {"mdt040", 1}, {"ncp4200", 1}, {"ncp4208", 1}, @@ -193,6 +208,7 @@ static const struct i2c_device_id pmbus_id[] = { {"pdt006", 1}, {"pdt012", 1}, {"pmbus", 0}, + {"sgd009", 1}, {"tps40400", 1}, {"tps544b20", 1}, {"tps544b25", 1}, diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index fbb1479d3ad4..3e3aa950277f 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -28,7 +28,7 @@ #include <linux/i2c/pmbus.h> #include "pmbus.h" -enum chips { ucd9000, ucd90120, ucd90124, ucd9090, ucd90910 }; +enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 }; #define UCD9000_MONITOR_CONFIG 0xd5 #define UCD9000_NUM_PAGES 0xd6 @@ -112,6 +112,7 @@ static const struct i2c_device_id ucd9000_id[] = { {"ucd9000", ucd9000}, {"ucd90120", ucd90120}, {"ucd90124", ucd90124}, + {"ucd90160", ucd90160}, {"ucd9090", ucd9090}, {"ucd90910", ucd90910}, {} diff --git a/drivers/hwmon/scpi-hwmon.c b/drivers/hwmon/scpi-hwmon.c index 25b44e68926d..559a3dcd64d8 100644 --- a/drivers/hwmon/scpi-hwmon.c +++ b/drivers/hwmon/scpi-hwmon.c @@ -255,7 +255,6 @@ static const struct of_device_id scpi_of_match[] = { static struct platform_driver scpi_hwmon_platdrv = { .driver = { .name = "scpi-hwmon", - .owner = THIS_MODULE, .of_match_table = scpi_of_match, }, .probe = scpi_hwmon_probe, diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c index 8479ac5eb853..36bba2a816a4 100644 --- a/drivers/hwmon/tmp102.c +++ b/drivers/hwmon/tmp102.c @@ -25,7 +25,6 @@ #include <linux/device.h> #include <linux/jiffies.h> #include <linux/regmap.h> -#include <linux/thermal.h> #include <linux/of.h> #define DRIVER_NAME "tmp102" @@ -79,84 +78,113 @@ static inline u16 tmp102_mC_to_reg(int val) return (val * 128) / 1000; } -static int tmp102_read_temp(void *dev, int *temp) +static int tmp102_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *temp) { struct tmp102 *tmp102 = dev_get_drvdata(dev); - unsigned int reg; - int ret; - - if (time_before(jiffies, tmp102->ready_time)) { - dev_dbg(dev, "%s: Conversion not ready yet..\n", __func__); - return -EAGAIN; + unsigned int regval; + int err, reg; + + switch (attr) { + case hwmon_temp_input: + /* Is it too early to return a conversion ? */ + if (time_before(jiffies, tmp102->ready_time)) { + dev_dbg(dev, "%s: Conversion not ready yet..\n", __func__); + return -EAGAIN; + } + reg = TMP102_TEMP_REG; + break; + case hwmon_temp_max_hyst: + reg = TMP102_TLOW_REG; + break; + case hwmon_temp_max: + reg = TMP102_THIGH_REG; + break; + default: + return -EOPNOTSUPP; } - ret = regmap_read(tmp102->regmap, TMP102_TEMP_REG, ®); - if (ret < 0) - return ret; - - *temp = tmp102_reg_to_mC(reg); + err = regmap_read(tmp102->regmap, reg, ®val); + if (err < 0) + return err; + *temp = tmp102_reg_to_mC(regval); return 0; } -static ssize_t tmp102_show_temp(struct device *dev, - struct device_attribute *attr, - char *buf) +static int tmp102_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long temp) { - struct sensor_device_attribute *sda = to_sensor_dev_attr(attr); struct tmp102 *tmp102 = dev_get_drvdata(dev); - int regaddr = sda->index; - unsigned int reg; - int err; - - if (regaddr == TMP102_TEMP_REG && - time_before(jiffies, tmp102->ready_time)) - return -EAGAIN; - - err = regmap_read(tmp102->regmap, regaddr, ®); - if (err < 0) - return err; + int reg; + + switch (attr) { + case hwmon_temp_max_hyst: + reg = TMP102_TLOW_REG; + break; + case hwmon_temp_max: + reg = TMP102_THIGH_REG; + break; + default: + return -EOPNOTSUPP; + } - return sprintf(buf, "%d\n", tmp102_reg_to_mC(reg)); + temp = clamp_val(temp, -256000, 255000); + return regmap_write(tmp102->regmap, reg, tmp102_mC_to_reg(temp)); } -static ssize_t tmp102_set_temp(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static umode_t tmp102_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) { - struct sensor_device_attribute *sda = to_sensor_dev_attr(attr); - struct tmp102 *tmp102 = dev_get_drvdata(dev); - int reg = sda->index; - long val; - int err; - - if (kstrtol(buf, 10, &val) < 0) - return -EINVAL; - val = clamp_val(val, -256000, 255000); - - err = regmap_write(tmp102->regmap, reg, tmp102_mC_to_reg(val)); - return err ? : count; + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: + return S_IRUGO; + case hwmon_temp_max_hyst: + case hwmon_temp_max: + return S_IRUGO | S_IWUSR; + default: + return 0; + } } -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, tmp102_show_temp, NULL, - TMP102_TEMP_REG); +static u32 tmp102_chip_config[] = { + HWMON_C_REGISTER_TZ, + 0 +}; + +static const struct hwmon_channel_info tmp102_chip = { + .type = hwmon_chip, + .config = tmp102_chip_config, +}; -static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, tmp102_show_temp, - tmp102_set_temp, TMP102_TLOW_REG); +static u32 tmp102_temp_config[] = { + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST, + 0 +}; -static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, tmp102_show_temp, - tmp102_set_temp, TMP102_THIGH_REG); +static const struct hwmon_channel_info tmp102_temp = { + .type = hwmon_temp, + .config = tmp102_temp_config, +}; -static struct attribute *tmp102_attrs[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, +static const struct hwmon_channel_info *tmp102_info[] = { + &tmp102_chip, + &tmp102_temp, NULL }; -ATTRIBUTE_GROUPS(tmp102); -static const struct thermal_zone_of_device_ops tmp102_of_thermal_ops = { - .get_temp = tmp102_read_temp, +static const struct hwmon_ops tmp102_hwmon_ops = { + .is_visible = tmp102_is_visible, + .read = tmp102_read, + .write = tmp102_write, +}; + +static const struct hwmon_chip_info tmp102_chip_info = { + .ops = &tmp102_hwmon_ops, + .info = tmp102_info, }; static void tmp102_restore_config(void *data) @@ -188,7 +216,7 @@ static const struct regmap_config tmp102_regmap_config = { }; static int tmp102_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct device *dev = &client->dev; struct device *hwmon_dev; @@ -249,16 +277,14 @@ static int tmp102_probe(struct i2c_client *client, tmp102->ready_time += msecs_to_jiffies(CONVERSION_TIME_MS); } - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - tmp102, - tmp102_groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + tmp102, + &tmp102_chip_info, + NULL); if (IS_ERR(hwmon_dev)) { dev_dbg(dev, "unable to register hwmon device\n"); return PTR_ERR(hwmon_dev); } - devm_thermal_zone_of_sensor_register(hwmon_dev, 0, hwmon_dev, - &tmp102_of_thermal_ops); - dev_info(dev, "initialized\n"); return 0; diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c index 85d48d80822a..bfb98b96c781 100644 --- a/drivers/hwmon/tmp421.c +++ b/drivers/hwmon/tmp421.c @@ -72,6 +72,10 @@ MODULE_DEVICE_TABLE(i2c, tmp421_id); struct tmp421_data { struct i2c_client *client; struct mutex update_lock; + u32 temp_config[5]; + struct hwmon_channel_info temp_info; + const struct hwmon_channel_info *info[2]; + struct hwmon_chip_info chip; char valid; unsigned long last_updated; int channels; @@ -125,85 +129,46 @@ static struct tmp421_data *tmp421_update_device(struct device *dev) return data; } -static ssize_t show_temp_value(struct device *dev, - struct device_attribute *devattr, char *buf) +static int tmp421_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) { - int index = to_sensor_dev_attr(devattr)->index; - struct tmp421_data *data = tmp421_update_device(dev); - int temp; - - mutex_lock(&data->update_lock); - if (data->config & TMP421_CONFIG_RANGE) - temp = temp_from_u16(data->temp[index]); - else - temp = temp_from_s16(data->temp[index]); - mutex_unlock(&data->update_lock); - - return sprintf(buf, "%d\n", temp); -} + struct tmp421_data *tmp421 = tmp421_update_device(dev); + + switch (attr) { + case hwmon_temp_input: + if (tmp421->config & TMP421_CONFIG_RANGE) + *val = temp_from_u16(tmp421->temp[channel]); + else + *val = temp_from_s16(tmp421->temp[channel]); + return 0; + case hwmon_temp_fault: + /* + * The OPEN bit signals a fault. This is bit 0 of the temperature + * register (low byte). + */ + *val = tmp421->temp[channel] & 0x01; + return 0; + default: + return -EOPNOTSUPP; + } -static ssize_t show_fault(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct tmp421_data *data = tmp421_update_device(dev); - - /* - * The OPEN bit signals a fault. This is bit 0 of the temperature - * register (low byte). - */ - if (data->temp[index] & 0x01) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); } -static umode_t tmp421_is_visible(struct kobject *kobj, struct attribute *a, - int n) +static umode_t tmp421_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) { - struct device *dev = container_of(kobj, struct device, kobj); - struct tmp421_data *data = dev_get_drvdata(dev); - struct device_attribute *devattr; - unsigned int index; - - devattr = container_of(a, struct device_attribute, attr); - index = to_sensor_dev_attr(devattr)->index; - - if (index < data->channels) - return a->mode; - - return 0; + switch (attr) { + case hwmon_temp_fault: + if (channel == 0) + return 0; + return S_IRUGO; + case hwmon_temp_input: + return S_IRUGO; + default: + return 0; + } } -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_value, NULL, 0); -static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_value, NULL, 1); -static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, 1); -static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp_value, NULL, 2); -static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, 2); -static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp_value, NULL, 3); -static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_fault, NULL, 3); - -static struct attribute *tmp421_attr[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp2_fault.dev_attr.attr, - &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp3_fault.dev_attr.attr, - &sensor_dev_attr_temp4_input.dev_attr.attr, - &sensor_dev_attr_temp4_fault.dev_attr.attr, - NULL -}; - -static const struct attribute_group tmp421_group = { - .attrs = tmp421_attr, - .is_visible = tmp421_is_visible, -}; - -static const struct attribute_group *tmp421_groups[] = { - &tmp421_group, - NULL -}; - static int tmp421_init_client(struct i2c_client *client) { int config, config_orig; @@ -289,13 +254,18 @@ static int tmp421_detect(struct i2c_client *client, return 0; } +static const struct hwmon_ops tmp421_ops = { + .is_visible = tmp421_is_visible, + .read = tmp421_read, +}; + static int tmp421_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; struct device *hwmon_dev; struct tmp421_data *data; - int err; + int i, err; data = devm_kzalloc(dev, sizeof(struct tmp421_data), GFP_KERNEL); if (!data) @@ -309,8 +279,21 @@ static int tmp421_probe(struct i2c_client *client, if (err) return err; - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, tmp421_groups); + for (i = 0; i < data->channels; i++) + data->temp_config[i] = HWMON_T_INPUT | HWMON_T_FAULT; + + data->chip.ops = &tmp421_ops; + data->chip.info = data->info; + + data->info[0] = &data->temp_info; + + data->temp_info.type = hwmon_temp; + data->temp_info.config = data->temp_config; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, + &data->chip, + NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c new file mode 100644 index 000000000000..9c0dbb8191ad --- /dev/null +++ b/drivers/hwmon/xgene-hwmon.c @@ -0,0 +1,787 @@ +/* + * APM X-Gene SoC Hardware Monitoring Driver + * + * Copyright (c) 2016, Applied Micro Circuits Corporation + * Author: Loc Ho <lho@apm.com> + * Hoan Tran <hotran@apm.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * This driver provides the following features: + * - Retrieve CPU total power (uW) + * - Retrieve IO total power (uW) + * - Retrieve SoC temperature (milli-degree C) and alarm + */ +#include <linux/acpi.h> +#include <linux/dma-mapping.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/mailbox_controller.h> +#include <linux/mailbox_client.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include <acpi/pcc.h> + +/* SLIMpro message defines */ +#define MSG_TYPE_DBG 0 +#define MSG_TYPE_ERR 7 +#define MSG_TYPE_PWRMGMT 9 + +#define MSG_TYPE(v) (((v) & 0xF0000000) >> 28) +#define MSG_TYPE_SET(v) (((v) << 28) & 0xF0000000) +#define MSG_SUBTYPE(v) (((v) & 0x0F000000) >> 24) +#define MSG_SUBTYPE_SET(v) (((v) << 24) & 0x0F000000) + +#define DBG_SUBTYPE_SENSOR_READ 4 +#define SENSOR_RD_MSG 0x04FFE902 +#define SENSOR_RD_EN_ADDR(a) ((a) & 0x000FFFFF) +#define PMD_PWR_REG 0x20 +#define PMD_PWR_MW_REG 0x26 +#define SOC_PWR_REG 0x21 +#define SOC_PWR_MW_REG 0x27 +#define SOC_TEMP_REG 0x10 + +#define TEMP_NEGATIVE_BIT 8 +#define SENSOR_INVALID_DATA BIT(15) + +#define PWRMGMT_SUBTYPE_TPC 1 +#define TPC_ALARM 2 +#define TPC_GET_ALARM 3 +#define TPC_CMD(v) (((v) & 0x00FF0000) >> 16) +#define TPC_CMD_SET(v) (((v) << 16) & 0x00FF0000) +#define TPC_EN_MSG(hndl, cmd, type) \ + (MSG_TYPE_SET(MSG_TYPE_PWRMGMT) | \ + MSG_SUBTYPE_SET(hndl) | TPC_CMD_SET(cmd) | type) + +/* PCC defines */ +#define PCC_SIGNATURE_MASK 0x50424300 +#define PCCC_GENERATE_DB_INT BIT(15) +#define PCCS_CMD_COMPLETE BIT(0) +#define PCCS_SCI_DOORBEL BIT(1) +#define PCCS_PLATFORM_NOTIFICATION BIT(3) +/* + * Arbitrary retries in case the remote processor is slow to respond + * to PCC commands + */ +#define PCC_NUM_RETRIES 500 + +#define ASYNC_MSG_FIFO_SIZE 16 +#define MBOX_OP_TIMEOUTMS 1000 + +#define WATT_TO_mWATT(x) ((x) * 1000) +#define mWATT_TO_uWATT(x) ((x) * 1000) +#define CELSIUS_TO_mCELSIUS(x) ((x) * 1000) + +#define to_xgene_hwmon_dev(cl) \ + container_of(cl, struct xgene_hwmon_dev, mbox_client) + +struct slimpro_resp_msg { + u32 msg; + u32 param1; + u32 param2; +} __packed; + +struct xgene_hwmon_dev { + struct device *dev; + struct mbox_chan *mbox_chan; + struct mbox_client mbox_client; + int mbox_idx; + + spinlock_t kfifo_lock; + struct mutex rd_mutex; + struct completion rd_complete; + int resp_pending; + struct slimpro_resp_msg sync_msg; + + struct work_struct workq; + struct kfifo_rec_ptr_1 async_msg_fifo; + + struct device *hwmon_dev; + bool temp_critical_alarm; + + phys_addr_t comm_base_addr; + void *pcc_comm_addr; + u64 usecs_lat; +}; + +/* + * This function tests and clears a bitmask then returns its old value + */ +static u16 xgene_word_tst_and_clr(u16 *addr, u16 mask) +{ + u16 ret, val; + + val = le16_to_cpu(READ_ONCE(*addr)); + ret = val & mask; + val &= ~mask; + WRITE_ONCE(*addr, cpu_to_le16(val)); + + return ret; +} + +static int xgene_hwmon_pcc_rd(struct xgene_hwmon_dev *ctx, u32 *msg) +{ + struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr; + u32 *ptr = (void *)(generic_comm_base + 1); + int rc, i; + u16 val; + + mutex_lock(&ctx->rd_mutex); + init_completion(&ctx->rd_complete); + ctx->resp_pending = true; + + /* Write signature for subspace */ + WRITE_ONCE(generic_comm_base->signature, + cpu_to_le32(PCC_SIGNATURE_MASK | ctx->mbox_idx)); + + /* Write to the shared command region */ + WRITE_ONCE(generic_comm_base->command, + cpu_to_le16(MSG_TYPE(msg[0]) | PCCC_GENERATE_DB_INT)); + + /* Flip CMD COMPLETE bit */ + val = le16_to_cpu(READ_ONCE(generic_comm_base->status)); + val &= ~PCCS_CMD_COMPLETE; + WRITE_ONCE(generic_comm_base->status, cpu_to_le16(val)); + + /* Copy the message to the PCC comm space */ + for (i = 0; i < sizeof(struct slimpro_resp_msg) / 4; i++) + WRITE_ONCE(ptr[i], cpu_to_le32(msg[i])); + + /* Ring the doorbell */ + rc = mbox_send_message(ctx->mbox_chan, msg); + if (rc < 0) { + dev_err(ctx->dev, "Mailbox send error %d\n", rc); + goto err; + } + if (!wait_for_completion_timeout(&ctx->rd_complete, + usecs_to_jiffies(ctx->usecs_lat))) { + dev_err(ctx->dev, "Mailbox operation timed out\n"); + rc = -ETIMEDOUT; + goto err; + } + + /* Check for error message */ + if (MSG_TYPE(ctx->sync_msg.msg) == MSG_TYPE_ERR) { + rc = -EINVAL; + goto err; + } + + msg[0] = ctx->sync_msg.msg; + msg[1] = ctx->sync_msg.param1; + msg[2] = ctx->sync_msg.param2; + +err: + mbox_chan_txdone(ctx->mbox_chan, 0); + ctx->resp_pending = false; + mutex_unlock(&ctx->rd_mutex); + return rc; +} + +static int xgene_hwmon_rd(struct xgene_hwmon_dev *ctx, u32 *msg) +{ + int rc; + + mutex_lock(&ctx->rd_mutex); + init_completion(&ctx->rd_complete); + ctx->resp_pending = true; + + rc = mbox_send_message(ctx->mbox_chan, msg); + if (rc < 0) { + dev_err(ctx->dev, "Mailbox send error %d\n", rc); + goto err; + } + + if (!wait_for_completion_timeout(&ctx->rd_complete, + msecs_to_jiffies(MBOX_OP_TIMEOUTMS))) { + dev_err(ctx->dev, "Mailbox operation timed out\n"); + rc = -ETIMEDOUT; + goto err; + } + + /* Check for error message */ + if (MSG_TYPE(ctx->sync_msg.msg) == MSG_TYPE_ERR) { + rc = -EINVAL; + goto err; + } + + msg[0] = ctx->sync_msg.msg; + msg[1] = ctx->sync_msg.param1; + msg[2] = ctx->sync_msg.param2; + +err: + ctx->resp_pending = false; + mutex_unlock(&ctx->rd_mutex); + return rc; +} + +static int xgene_hwmon_reg_map_rd(struct xgene_hwmon_dev *ctx, u32 addr, + u32 *data) +{ + u32 msg[3]; + int rc; + + msg[0] = SENSOR_RD_MSG; + msg[1] = SENSOR_RD_EN_ADDR(addr); + msg[2] = 0; + + if (acpi_disabled) + rc = xgene_hwmon_rd(ctx, msg); + else + rc = xgene_hwmon_pcc_rd(ctx, msg); + + if (rc < 0) + return rc; + + /* + * Check if sensor data is valid. + */ + if (msg[1] & SENSOR_INVALID_DATA) + return -ENODATA; + + *data = msg[1]; + + return rc; +} + +static int xgene_hwmon_get_notification_msg(struct xgene_hwmon_dev *ctx, + u32 *amsg) +{ + u32 msg[3]; + int rc; + + msg[0] = TPC_EN_MSG(PWRMGMT_SUBTYPE_TPC, TPC_GET_ALARM, 0); + msg[1] = 0; + msg[2] = 0; + + rc = xgene_hwmon_pcc_rd(ctx, msg); + if (rc < 0) + return rc; + + amsg[0] = msg[0]; + amsg[1] = msg[1]; + amsg[2] = msg[2]; + + return rc; +} + +static int xgene_hwmon_get_cpu_pwr(struct xgene_hwmon_dev *ctx, u32 *val) +{ + u32 watt, mwatt; + int rc; + + rc = xgene_hwmon_reg_map_rd(ctx, PMD_PWR_REG, &watt); + if (rc < 0) + return rc; + + rc = xgene_hwmon_reg_map_rd(ctx, PMD_PWR_MW_REG, &mwatt); + if (rc < 0) + return rc; + + *val = WATT_TO_mWATT(watt) + mwatt; + return 0; +} + +static int xgene_hwmon_get_io_pwr(struct xgene_hwmon_dev *ctx, u32 *val) +{ + u32 watt, mwatt; + int rc; + + rc = xgene_hwmon_reg_map_rd(ctx, SOC_PWR_REG, &watt); + if (rc < 0) + return rc; + + rc = xgene_hwmon_reg_map_rd(ctx, SOC_PWR_MW_REG, &mwatt); + if (rc < 0) + return rc; + + *val = WATT_TO_mWATT(watt) + mwatt; + return 0; +} + +static int xgene_hwmon_get_temp(struct xgene_hwmon_dev *ctx, u32 *val) +{ + return xgene_hwmon_reg_map_rd(ctx, SOC_TEMP_REG, val); +} + +/* + * Sensor temperature/power functions + */ +static ssize_t temp1_input_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev); + int rc, temp; + u32 val; + + rc = xgene_hwmon_get_temp(ctx, &val); + if (rc < 0) + return rc; + + temp = sign_extend32(val, TEMP_NEGATIVE_BIT); + + return snprintf(buf, PAGE_SIZE, "%d\n", CELSIUS_TO_mCELSIUS(temp)); +} + +static ssize_t temp1_label_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "SoC Temperature\n"); +} + +static ssize_t temp1_critical_alarm_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", ctx->temp_critical_alarm); +} + +static ssize_t power1_label_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CPU power\n"); +} + +static ssize_t power2_label_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "IO power\n"); +} + +static ssize_t power1_input_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev); + u32 val; + int rc; + + rc = xgene_hwmon_get_cpu_pwr(ctx, &val); + if (rc < 0) + return rc; + + return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val)); +} + +static ssize_t power2_input_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev); + u32 val; + int rc; + + rc = xgene_hwmon_get_io_pwr(ctx, &val); + if (rc < 0) + return rc; + + return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val)); +} + +static DEVICE_ATTR_RO(temp1_label); +static DEVICE_ATTR_RO(temp1_input); +static DEVICE_ATTR_RO(temp1_critical_alarm); +static DEVICE_ATTR_RO(power1_label); +static DEVICE_ATTR_RO(power1_input); +static DEVICE_ATTR_RO(power2_label); +static DEVICE_ATTR_RO(power2_input); + +static struct attribute *xgene_hwmon_attrs[] = { + &dev_attr_temp1_label.attr, + &dev_attr_temp1_input.attr, + &dev_attr_temp1_critical_alarm.attr, + &dev_attr_power1_label.attr, + &dev_attr_power1_input.attr, + &dev_attr_power2_label.attr, + &dev_attr_power2_input.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(xgene_hwmon); + +static int xgene_hwmon_tpc_alarm(struct xgene_hwmon_dev *ctx, + struct slimpro_resp_msg *amsg) +{ + ctx->temp_critical_alarm = !!amsg->param2; + sysfs_notify(&ctx->dev->kobj, NULL, "temp1_critical_alarm"); + + return 0; +} + +static void xgene_hwmon_process_pwrmsg(struct xgene_hwmon_dev *ctx, + struct slimpro_resp_msg *amsg) +{ + if ((MSG_SUBTYPE(amsg->msg) == PWRMGMT_SUBTYPE_TPC) && + (TPC_CMD(amsg->msg) == TPC_ALARM)) + xgene_hwmon_tpc_alarm(ctx, amsg); +} + +/* + * This function is called to process async work queue + */ +static void xgene_hwmon_evt_work(struct work_struct *work) +{ + struct slimpro_resp_msg amsg; + struct xgene_hwmon_dev *ctx; + int ret; + + ctx = container_of(work, struct xgene_hwmon_dev, workq); + while (kfifo_out_spinlocked(&ctx->async_msg_fifo, &amsg, + sizeof(struct slimpro_resp_msg), + &ctx->kfifo_lock)) { + /* + * If PCC, send a consumer command to Platform to get info + * If Slimpro Mailbox, get message from specific FIFO + */ + if (!acpi_disabled) { + ret = xgene_hwmon_get_notification_msg(ctx, + (u32 *)&amsg); + if (ret < 0) + continue; + } + + if (MSG_TYPE(amsg.msg) == MSG_TYPE_PWRMGMT) + xgene_hwmon_process_pwrmsg(ctx, &amsg); + } +} + +static int xgene_hwmon_rx_ready(struct xgene_hwmon_dev *ctx, void *msg) +{ + if (IS_ERR_OR_NULL(ctx->hwmon_dev) && !ctx->resp_pending) { + /* Enqueue to the FIFO */ + kfifo_in_spinlocked(&ctx->async_msg_fifo, msg, + sizeof(struct slimpro_resp_msg), + &ctx->kfifo_lock); + return -ENODEV; + } + + return 0; +} + +/* + * This function is called when the SLIMpro Mailbox received a message + */ +static void xgene_hwmon_rx_cb(struct mbox_client *cl, void *msg) +{ + struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl); + + /* + * While the driver registers with the mailbox framework, an interrupt + * can be pending before the probe function completes its + * initialization. If such condition occurs, just queue up the message + * as the driver is not ready for servicing the callback. + */ + if (xgene_hwmon_rx_ready(ctx, msg) < 0) + return; + + /* + * Response message format: + * msg[0] is the return code of the operation + * msg[1] is the first parameter word + * msg[2] is the second parameter word + * + * As message only supports dword size, just assign it. + */ + + /* Check for sync query */ + if (ctx->resp_pending && + ((MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_ERR) || + (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_DBG && + MSG_SUBTYPE(((u32 *)msg)[0]) == DBG_SUBTYPE_SENSOR_READ) || + (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_PWRMGMT && + MSG_SUBTYPE(((u32 *)msg)[0]) == PWRMGMT_SUBTYPE_TPC && + TPC_CMD(((u32 *)msg)[0]) == TPC_ALARM))) { + ctx->sync_msg.msg = ((u32 *)msg)[0]; + ctx->sync_msg.param1 = ((u32 *)msg)[1]; + ctx->sync_msg.param2 = ((u32 *)msg)[2]; + + /* Operation waiting for response */ + complete(&ctx->rd_complete); + + return; + } + + /* Enqueue to the FIFO */ + kfifo_in_spinlocked(&ctx->async_msg_fifo, msg, + sizeof(struct slimpro_resp_msg), &ctx->kfifo_lock); + /* Schedule the bottom handler */ + schedule_work(&ctx->workq); +} + +/* + * This function is called when the PCC Mailbox received a message + */ +static void xgene_hwmon_pcc_rx_cb(struct mbox_client *cl, void *msg) +{ + struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl); + struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr; + struct slimpro_resp_msg amsg; + + /* + * While the driver registers with the mailbox framework, an interrupt + * can be pending before the probe function completes its + * initialization. If such condition occurs, just queue up the message + * as the driver is not ready for servicing the callback. + */ + if (xgene_hwmon_rx_ready(ctx, &amsg) < 0) + return; + + msg = generic_comm_base + 1; + /* Check if platform sends interrupt */ + if (!xgene_word_tst_and_clr(&generic_comm_base->status, + PCCS_SCI_DOORBEL)) + return; + + /* + * Response message format: + * msg[0] is the return code of the operation + * msg[1] is the first parameter word + * msg[2] is the second parameter word + * + * As message only supports dword size, just assign it. + */ + + /* Check for sync query */ + if (ctx->resp_pending && + ((MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_ERR) || + (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_DBG && + MSG_SUBTYPE(((u32 *)msg)[0]) == DBG_SUBTYPE_SENSOR_READ) || + (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_PWRMGMT && + MSG_SUBTYPE(((u32 *)msg)[0]) == PWRMGMT_SUBTYPE_TPC && + TPC_CMD(((u32 *)msg)[0]) == TPC_ALARM))) { + /* Check if platform completes command */ + if (xgene_word_tst_and_clr(&generic_comm_base->status, + PCCS_CMD_COMPLETE)) { + ctx->sync_msg.msg = ((u32 *)msg)[0]; + ctx->sync_msg.param1 = ((u32 *)msg)[1]; + ctx->sync_msg.param2 = ((u32 *)msg)[2]; + + /* Operation waiting for response */ + complete(&ctx->rd_complete); + + return; + } + } + + /* + * Platform notifies interrupt to OSPM. + * OPSM schedules a consumer command to get this information + * in a workqueue. Platform must wait until OSPM has issued + * a consumer command that serves this notification. + */ + + /* Enqueue to the FIFO */ + kfifo_in_spinlocked(&ctx->async_msg_fifo, &amsg, + sizeof(struct slimpro_resp_msg), &ctx->kfifo_lock); + /* Schedule the bottom handler */ + schedule_work(&ctx->workq); +} + +static void xgene_hwmon_tx_done(struct mbox_client *cl, void *msg, int ret) +{ + if (ret) { + dev_dbg(cl->dev, "TX did not complete: CMD sent:%x, ret:%d\n", + *(u16 *)msg, ret); + } else { + dev_dbg(cl->dev, "TX completed. CMD sent:%x, ret:%d\n", + *(u16 *)msg, ret); + } +} + +static int xgene_hwmon_probe(struct platform_device *pdev) +{ + struct xgene_hwmon_dev *ctx; + struct mbox_client *cl; + int rc; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->dev = &pdev->dev; + platform_set_drvdata(pdev, ctx); + cl = &ctx->mbox_client; + + spin_lock_init(&ctx->kfifo_lock); + mutex_init(&ctx->rd_mutex); + + rc = kfifo_alloc(&ctx->async_msg_fifo, + sizeof(struct slimpro_resp_msg) * ASYNC_MSG_FIFO_SIZE, + GFP_KERNEL); + if (rc) + goto out_mbox_free; + + INIT_WORK(&ctx->workq, xgene_hwmon_evt_work); + + /* Request mailbox channel */ + cl->dev = &pdev->dev; + cl->tx_done = xgene_hwmon_tx_done; + cl->tx_block = false; + cl->tx_tout = MBOX_OP_TIMEOUTMS; + cl->knows_txdone = false; + if (acpi_disabled) { + cl->rx_callback = xgene_hwmon_rx_cb; + ctx->mbox_chan = mbox_request_channel(cl, 0); + if (IS_ERR(ctx->mbox_chan)) { + dev_err(&pdev->dev, + "SLIMpro mailbox channel request failed\n"); + return -ENODEV; + } + } else { + struct acpi_pcct_hw_reduced *cppc_ss; + + if (device_property_read_u32(&pdev->dev, "pcc-channel", + &ctx->mbox_idx)) { + dev_err(&pdev->dev, "no pcc-channel property\n"); + return -ENODEV; + } + + cl->rx_callback = xgene_hwmon_pcc_rx_cb; + ctx->mbox_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx); + if (IS_ERR(ctx->mbox_chan)) { + dev_err(&pdev->dev, + "PPC channel request failed\n"); + return -ENODEV; + } + + /* + * The PCC mailbox controller driver should + * have parsed the PCCT (global table of all + * PCC channels) and stored pointers to the + * subspace communication region in con_priv. + */ + cppc_ss = ctx->mbox_chan->con_priv; + if (!cppc_ss) { + dev_err(&pdev->dev, "PPC subspace not found\n"); + rc = -ENODEV; + goto out_mbox_free; + } + + if (!ctx->mbox_chan->mbox->txdone_irq) { + dev_err(&pdev->dev, "PCC IRQ not supported\n"); + rc = -ENODEV; + goto out_mbox_free; + } + + /* + * This is the shared communication region + * for the OS and Platform to communicate over. + */ + ctx->comm_base_addr = cppc_ss->base_address; + if (ctx->comm_base_addr) { + ctx->pcc_comm_addr = memremap(ctx->comm_base_addr, + cppc_ss->length, + MEMREMAP_WB); + } else { + dev_err(&pdev->dev, "Failed to get PCC comm region\n"); + rc = -ENODEV; + goto out_mbox_free; + } + + if (!ctx->pcc_comm_addr) { + dev_err(&pdev->dev, + "Failed to ioremap PCC comm region\n"); + rc = -ENOMEM; + goto out_mbox_free; + } + + /* + * cppc_ss->latency is just a Nominal value. In reality + * the remote processor could be much slower to reply. + * So add an arbitrary amount of wait on top of Nominal. + */ + ctx->usecs_lat = PCC_NUM_RETRIES * cppc_ss->latency; + } + + ctx->hwmon_dev = hwmon_device_register_with_groups(ctx->dev, + "apm_xgene", + ctx, + xgene_hwmon_groups); + if (IS_ERR(ctx->hwmon_dev)) { + dev_err(&pdev->dev, "Failed to register HW monitor device\n"); + rc = PTR_ERR(ctx->hwmon_dev); + goto out; + } + + /* + * Schedule the bottom handler if there is a pending message. + */ + schedule_work(&ctx->workq); + + dev_info(&pdev->dev, "APM X-Gene SoC HW monitor driver registered\n"); + + return 0; + +out: + if (acpi_disabled) + mbox_free_channel(ctx->mbox_chan); + else + pcc_mbox_free_channel(ctx->mbox_chan); +out_mbox_free: + kfifo_free(&ctx->async_msg_fifo); + + return rc; +} + +static int xgene_hwmon_remove(struct platform_device *pdev) +{ + struct xgene_hwmon_dev *ctx = platform_get_drvdata(pdev); + + hwmon_device_unregister(ctx->hwmon_dev); + kfifo_free(&ctx->async_msg_fifo); + if (acpi_disabled) + mbox_free_channel(ctx->mbox_chan); + else + pcc_mbox_free_channel(ctx->mbox_chan); + + return 0; +} + +#ifdef CONFIG_ACPI +static const struct acpi_device_id xgene_hwmon_acpi_match[] = { + {"APMC0D29", 0}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, xgene_hwmon_acpi_match); +#endif + +static const struct of_device_id xgene_hwmon_of_match[] = { + {.compatible = "apm,xgene-slimpro-hwmon"}, + {} +}; +MODULE_DEVICE_TABLE(of, xgene_hwmon_of_match); + +static struct platform_driver xgene_hwmon_driver __refdata = { + .probe = xgene_hwmon_probe, + .remove = xgene_hwmon_remove, + .driver = { + .name = "xgene-slimpro-hwmon", + .of_match_table = xgene_hwmon_of_match, + .acpi_match_table = ACPI_PTR(xgene_hwmon_acpi_match), + }, +}; +module_platform_driver(xgene_hwmon_driver); + +MODULE_DESCRIPTION("APM X-Gene SoC hardware monitor"); +MODULE_LICENSE("GPL"); |