From f48ccb26e264b112afc087d562fa2d68e2e1174b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 3 Jul 2016 21:46:05 -0700 Subject: hwmon: (lm95241) Fix overflow problems, write conversion rate to chip Writing the update_interval attribute could result in an overflow if a number close to the maximum unsigned long was written. At the same time, even though the chip supports setting the conversion rate, the selected conversion rate was not actually written to the chip. Fix the second problem by selecting valid (supported) conversion rates, and writing the selected conversion rate to the chip. This also fixes the first problem, since arbitrary conversion rates are now converted to actually supported conversion rates. Also, set the default chip conversion rate to 1 second. Previously, the chip was configured for continuous conversion, but readings were only retrieved every seond, which doesn't make much sense. If we only read a value from the chip every second, we can as well save some power and only convert in one-second intervals. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95241.c | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index cdf19adaec79..a8cf666fe661 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -59,6 +59,7 @@ static const unsigned short normal_i2c[] = { #define CFG_CR0182 0x10 #define CFG_CR1000 0x20 #define CFG_CR2700 0x30 +#define CFG_CRMASK 0x30 #define R1MS_SHIFT 0 #define R2MS_SHIFT 2 #define R1MS_MASK (0x01 << (R1MS_SHIFT)) @@ -91,7 +92,8 @@ 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)]; @@ -118,7 +120,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; @@ -276,8 +279,7 @@ static ssize_t show_interval(struct device *dev, struct device_attribute *attr, { struct lm95241_data *data = lm95241_update_device(dev); - return snprintf(buf, PAGE_SIZE - 1, "%lu\n", 1000 * data->interval - / HZ); + return snprintf(buf, PAGE_SIZE - 1, "%lu\n", data->interval); } static ssize_t set_interval(struct device *dev, struct device_attribute *attr, @@ -285,11 +287,35 @@ static ssize_t set_interval(struct device *dev, struct device_attribute *attr, { struct lm95241_data *data = dev_get_drvdata(dev); unsigned long val; + int convrate; + u8 config; if (kstrtoul(buf, 10, &val) < 0) return -EINVAL; - data->interval = val * HZ / 1000; + mutex_lock(&data->update_lock); + + 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; + i2c_smbus_write_byte_data(data->client, LM95241_REG_RW_CONFIG, + config); + mutex_unlock(&data->update_lock); return count; } @@ -362,8 +388,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); -- cgit From 090a7f8efe0be34e837d51ef27f6232356ecabf6 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 4 Jul 2016 06:46:31 -0700 Subject: hwmon: (lm95241) Add support for fault attributes The chip reports if remote diodes are present, which can be used for the fault attrributes. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95241.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index a8cf666fe661..e4e7bf169b07 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -70,6 +70,8 @@ static const unsigned short normal_i2c[] = { #define R2DF_MASK (0x01 << (R2DF_SHIFT)) #define R1FE_MASK 0x01 #define R2FE_MASK 0x05 +#define R1DM 0x01 +#define R2DM 0x02 #define TT1_SHIFT 0 #define TT2_SHIFT 4 #define TT_OFF 0 @@ -97,7 +99,7 @@ struct lm95241_data { 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 */ @@ -130,6 +132,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; } @@ -274,6 +279,15 @@ static ssize_t set_max(struct device *dev, struct device_attribute *attr, return count; } +static ssize_t show_fault(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95241_data *data = lm95241_update_device(dev); + + return snprintf(buf, PAGE_SIZE - 1, "%d", + !!(data->status & to_sensor_dev_attr(attr)->index)); +} + static ssize_t show_interval(struct device *dev, struct device_attribute *attr, char *buf) { @@ -335,6 +349,8 @@ 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 SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, R1DM); +static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, R2DM); static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval, set_interval); @@ -348,6 +364,8 @@ static struct attribute *lm95241_attrs[] = { &sensor_dev_attr_temp3_min.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr, &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, &dev_attr_update_interval.attr, NULL }; -- cgit From 4b2ea08be985cb238ea03f3fd76490de950fd3cc Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 4 Jul 2016 06:48:18 -0700 Subject: hwmon: (lm95241) Order include files alphabetically Simplify detecting duplicate include files and finding the right place for adding new ones. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95241.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index e4e7bf169b07..c2da2b161996 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -21,15 +21,15 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include +#include +#include #include -#include #include -#include #include #include -#include +#include #include +#include #include #define DEVNAME "lm95241" -- cgit From 054f3040e47250ba98958175ff4e7ab61dd7725e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 4 Jul 2016 08:00:12 -0700 Subject: hwmon: (lm95241) Drop FSF address The FSF address may change, and providing it does not add any value. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95241.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index c2da2b161996..507b32b67974 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -15,10 +15,6 @@ * 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 -- cgit From e8172a9381f33b6a4b16871b9149171e0113bfde Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 4 Jul 2016 08:01:04 -0700 Subject: hwmon: (lm95241) Use BIT macro where appropriate Drop some of the SHIFT defines since shift is implied with BIT(). Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95241.c | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index 507b32b67974..df94f486b21c 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -17,6 +17,7 @@ * GNU General Public License for more details. */ +#include #include #include #include @@ -50,29 +51,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 CFG_CRMASK 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 R1DM 0x01 -#define R2DM 0x02 -#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 @@ -148,7 +145,7 @@ static ssize_t show_input(struct device *dev, struct device_attribute *attr, int index = to_sensor_dev_attr(attr)->index; return snprintf(buf, PAGE_SIZE - 1, "%d\n", - index == 0 || (data->config & (1 << (index / 2))) ? + index == 0 || (data->config & BIT(index / 2)) ? temp_from_reg_signed(data->temp[index], data->temp[index + 1]) : temp_from_reg_unsigned(data->temp[index], data->temp[index + 1])); -- cgit From 637ab157d4a94145a6c9b89113222b6c80fab54c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 4 Jul 2016 08:27:38 -0700 Subject: hwmon: (lm95241) Use more accurate limits The lower temperature limit is -128 degrees C. The supported upper limits are 127.875 or 255.875 degrees C. Also, don't fail if a value outside the supported range is provided when setting a temperature limit. Instead, clamp the provided value to the available value range. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95241.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index df94f486b21c..3d96c3fcba9b 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -205,7 +205,7 @@ static ssize_t show_min(struct device *dev, struct device_attribute *attr, return snprintf(buf, PAGE_SIZE - 1, data->config & to_sensor_dev_attr(attr)->index ? - "-127000\n" : "0\n"); + "-128000\n" : "0\n"); } static ssize_t set_min(struct device *dev, struct device_attribute *attr, @@ -216,8 +216,6 @@ static ssize_t set_min(struct device *dev, struct device_attribute *attr, if (kstrtol(buf, 10, &val) < 0) return -EINVAL; - if (val < -128000) - return -EINVAL; mutex_lock(&data->update_lock); @@ -242,7 +240,7 @@ static ssize_t show_max(struct device *dev, struct device_attribute *attr, return snprintf(buf, PAGE_SIZE - 1, data->config & to_sensor_dev_attr(attr)->index ? - "127000\n" : "255000\n"); + "127875\n" : "255875\n"); } static ssize_t set_max(struct device *dev, struct device_attribute *attr, @@ -253,8 +251,6 @@ static ssize_t set_max(struct device *dev, struct device_attribute *attr, if (kstrtol(buf, 10, &val) < 0) return -EINVAL; - if (val >= 256000) - return -EINVAL; mutex_lock(&data->update_lock); -- cgit From a95da110cd6f464e7ea413224add0700051fe9ac Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 25 Jul 2016 11:12:06 +0200 Subject: hwmon: (adt7411) add external thermal diode support If the EXT_TDM bit is set, the chip supports a second temperature sensor instead of two voltage sensors. Signed-off-by: Michael Walle Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7411.c | 51 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 8 deletions(-) (limited to 'drivers/hwmon') 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 @@ -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. -- cgit From cc00decf0e280953e9067e938ad331f93bda8b40 Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Mon, 25 Jul 2016 10:55:53 +0000 Subject: hwmon: (pmbus) Add explicit support for DPS-460, DPS-800, and SGD009 Provide support for PSU DPS-460, DPS-800 from Delta Electronics, INC and for SGD009 from Acbel Polytech, INC. These devices do not support the STATUS_CML register, and reports a communication error in response to this command. For this reason, the status register check is disabled for these controllers. Signed-off-by: Vadim Pasternak Reviewed-by: Jiri Pirko Reviewed-by: Jean Delvare Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') 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 #include #include +#include #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}, -- cgit From 640208567660dbe8ad81f3b1c7c90e56fd352317 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 25 Jul 2016 13:27:49 -0700 Subject: hwmon: (ntc_thermistor) Use devm_iio_channel_get Use devm_iio_channel_get() instead of iio_channel_get to simplify error handling and device removal. Signed-off-by: Guenter Roeck --- drivers/hwmon/ntc_thermistor.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index 8ef7b713cb1a..485701ca3609 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -331,7 +331,7 @@ ntc_thermistor_parse_dt(struct platform_device *pdev) if (!pdata) return ERR_PTR(-ENOMEM); - chan = iio_channel_get(&pdev->dev, NULL); + chan = devm_iio_channel_get(&pdev->dev, NULL); if (IS_ERR(chan)) return ERR_CAST(chan); @@ -359,11 +359,6 @@ 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) @@ -373,8 +368,6 @@ ntc_thermistor_parse_dt(struct platform_device *pdev) #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) @@ -685,18 +678,15 @@ static int ntc_thermistor_probe(struct platform_device *pdev) 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); return 0; } -- cgit From 48001525c94b099058ef0b1fa6a5094082123d2d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 25 Jul 2016 13:37:56 -0700 Subject: hwmon: (ntc_thermistor) Use dev instead of &pdev->dev where possible Instead of repeatedly accessing &pdev->dev, use a local variable dev instead where possible. Also drop 'dev' from private data since it is unnecessary. Signed-off-by: Guenter Roeck --- drivers/hwmon/ntc_thermistor.c | 52 ++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 27 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index 485701ca3609..6b2d5222521f 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -256,7 +256,6 @@ 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]; }; @@ -316,22 +315,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 = devm_iio_channel_get(&pdev->dev, NULL); + chan = devm_iio_channel_get(dev, NULL); if (IS_ERR(chan)) return ERR_CAST(chan); @@ -361,7 +360,7 @@ ntc_thermistor_parse_dt(struct platform_device *pdev) } #else static struct ntc_thermistor_platform_data * -ntc_thermistor_parse_dt(struct platform_device *pdev) +ntc_thermistor_parse_dt(struct device *dev) { return NULL; } @@ -572,33 +571,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 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; } @@ -610,18 +610,16 @@ 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)); @@ -647,37 +645,37 @@ 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); + ret = sysfs_create_group(&dev->kobj, &ntc_attr_group); if (ret) { - dev_err(data->dev, "unable to create sysfs files\n"); + dev_err(dev, "unable to create sysfs files\n"); return ret; } - data->hwmon_dev = hwmon_device_register(data->dev); + data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { - dev_err(data->dev, "unable to register as hwmon device.\n"); + dev_err(dev, "unable to register as hwmon device.\n"); ret = PTR_ERR(data->hwmon_dev); goto err_after_sysfs; } - 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, dev, &ntc_of_thermal_ops); if (IS_ERR(tz)) - dev_dbg(&pdev->dev, "Failed to register to thermal fw.\n"); + dev_dbg(dev, "Failed to register to thermal fw.\n"); return 0; err_after_sysfs: - sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); + sysfs_remove_group(&dev->kobj, &ntc_attr_group); return ret; } @@ -686,7 +684,7 @@ static int ntc_thermistor_remove(struct platform_device *pdev) struct ntc_data *data = platform_get_drvdata(pdev); hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); + sysfs_remove_group(&pdev->dev.kobj, &ntc_attr_group); return 0; } -- cgit From 5e7f5994bdddfb8ddcf08c23638b3ec00c8984bc Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 25 Jul 2016 13:45:30 -0700 Subject: hwmon: (ntc_thermistor) Use devm_hwmon_device_register_with_groups Simplify code, reduce code size, and drop remove function as no longer needed. Signed-off-by: Guenter Roeck --- drivers/hwmon/ntc_thermistor.c | 57 +++++++----------------------------------- 1 file changed, 9 insertions(+), 48 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index 6b2d5222521f..c52d07c6b49f 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -253,11 +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; int n_comp; - char name[PLATFORM_NAME_SIZE]; }; #if defined(CONFIG_OF) && IS_ENABLED(CONFIG_IIO) @@ -508,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); @@ -522,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) { @@ -551,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, @@ -576,8 +560,8 @@ static int ntc_thermistor_probe(struct platform_device *pdev) 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(dev); if (IS_ERR(pdata)) @@ -621,7 +605,6 @@ static int ntc_thermistor_probe(struct platform_device *pdev) pdev_id = of_id ? of_id->data : platform_get_device_id(pdev); data->pdata = pdata; - strlcpy(data->name, pdev_id->name, sizeof(data->name)); switch (pdev_id->driver_data) { case TYPE_NCPXXWB473: @@ -650,42 +633,21 @@ static int ntc_thermistor_probe(struct platform_device *pdev) return -EINVAL; } - platform_set_drvdata(pdev, data); - - ret = sysfs_create_group(&dev->kobj, &ntc_attr_group); - if (ret) { - dev_err(dev, "unable to create sysfs files\n"); - return ret; - } - - data->hwmon_dev = hwmon_device_register(dev); - if (IS_ERR(data->hwmon_dev)) { + 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"); - ret = PTR_ERR(data->hwmon_dev); - goto err_after_sysfs; + return PTR_ERR(hwmon_dev); } dev_info(dev, "Thermistor type: %s successfully probed.\n", pdev_id->name); - tz = devm_thermal_zone_of_sensor_register(dev, 0, dev, + tz = devm_thermal_zone_of_sensor_register(dev, 0, data, &ntc_of_thermal_ops); if (IS_ERR(tz)) dev_dbg(dev, "Failed to register to thermal fw.\n"); - return 0; -err_after_sysfs: - sysfs_remove_group(&dev->kobj, &ntc_attr_group); - return ret; -} - -static int ntc_thermistor_remove(struct platform_device *pdev) -{ - struct ntc_data *data = platform_get_drvdata(pdev); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&pdev->dev.kobj, &ntc_attr_group); - return 0; } @@ -695,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, }; -- cgit From c9ebbe6f23f43f4520d9e3c4fe1384963848088e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 10 Jul 2016 09:43:21 -0700 Subject: hwmon: (core) Order include files alphabetically Ordering include files alphabetically makes it easier to add new ones. Stop including linux/spinlock.h and linux/kdev_t.h since both are not needed. Reviewed-by: Jonathan Cameron Signed-off-by: Guenter Roeck --- drivers/hwmon/hwmon.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index a26c385a435b..649a68d119b4 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -12,16 +12,14 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include #include #include -#include -#include -#include -#include #include -#include +#include +#include +#include #include +#include #include #define HWMON_ID_PREFIX "hwmon" -- cgit From d560168b5d0fb4a70c74b386564072a819d9bf71 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 26 Aug 2015 19:38:11 -0700 Subject: hwmon: (core) New hwmon registration API Up to now, each hwmon driver has to implement its own sysfs attributes. This requires a lot of template code, and distracts from the driver's core function to read and write chip registers. To be able to reduce driver complexity, move sensor attribute handling and thermal zone registration into hwmon core. By using the new API, driver code and data size is typically reduced by 20-70%, depending on driver complexity and the number of sysfs attributes supported. With this patch, the new API only supports thermal sensors. Support for other sensor types will be added with subsequent patches. Acked-by: Punit Agrawal Reviewed-by: Jonathan Cameron Signed-off-by: Guenter Roeck --- drivers/hwmon/hwmon.c | 485 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 458 insertions(+), 27 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 649a68d119b4..3e4cc442a089 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -12,6 +12,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -21,6 +22,7 @@ #include #include #include +#include #define HWMON_ID_PREFIX "hwmon" #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" @@ -28,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) { @@ -78,25 +106,286 @@ 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. - */ -struct device * -hwmon_device_register_with_groups(struct device *dev, const char *name, - void *drvdata, - const struct attribute_group **groups) +/* Thermal zone handling */ + +#if IS_REACHABLE(CONFIG_THERMAL) && defined(CONFIG_THERMAL_OF) +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) && defined(CONFIG_THERMAL_OF) */ + +/* 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_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 *__templates[] = { + [hwmon_chip] = hwmon_chip_attr_templates, + [hwmon_temp] = hwmon_temp_attr_templates, +}; + +static const int __templates_size[] = { + [hwmon_chip] = ARRAY_SIZE(hwmon_chip_attr_templates), + [hwmon_temp] = ARRAY_SIZE(hwmon_temp_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"))) @@ -112,27 +401,127 @@ 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 @@ -211,6 +600,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; -- cgit From 00d616cf872bb552a6853df288efcdb9f937a489 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 20 Jun 2016 11:01:57 -0700 Subject: hwmon: (core) Add voltage attribute support to new API Acked-by: Punit Agrawal Reviewed-by: Jonathan Cameron Signed-off-by: Guenter Roeck --- drivers/hwmon/hwmon.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 3e4cc442a089..7697dfa92558 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -259,6 +259,7 @@ static struct attribute *hwmon_genattr(struct device *dev, 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_update_interval] = "update_interval", [hwmon_chip_alarms] = "alarms", }; @@ -290,14 +291,34 @@ static const char * const hwmon_temp_attr_templates[] = { [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 *__templates[] = { [hwmon_chip] = hwmon_chip_attr_templates, [hwmon_temp] = hwmon_temp_attr_templates, + [hwmon_in] = hwmon_in_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), }; static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info) -- cgit From 9b26947ce5b6a6d5f260d9564195e8971cc9713d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 20 Jun 2016 11:10:33 -0700 Subject: hwmon: (core) Add current attribute support to new API Acked-by: Punit Agrawal Reviewed-by: Jonathan Cameron Signed-off-by: Guenter Roeck --- drivers/hwmon/hwmon.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 7697dfa92558..9229229a99a5 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -260,6 +260,7 @@ static struct attribute *hwmon_genattr(struct device *dev, 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_update_interval] = "update_interval", [hwmon_chip_alarms] = "alarms", }; @@ -309,16 +310,36 @@ static const char * const hwmon_in_attr_templates[] = { [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 *__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, }; 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), }; static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info) -- cgit From b308f5c744522de020da4706718de9076adeada7 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 20 Jun 2016 11:27:36 -0700 Subject: hwmon: (core) Add power attribute support to new API Acked-by: Punit Agrawal Reviewed-by: Jonathan Cameron Signed-off-by: Guenter Roeck --- drivers/hwmon/hwmon.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 9229229a99a5..85f4e27548cb 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -261,6 +261,7 @@ 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", }; @@ -328,11 +329,39 @@ static const char * const hwmon_curr_attr_templates[] = { [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 *__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, }; static const int __templates_size[] = { @@ -340,6 +369,7 @@ static const int __templates_size[] = { [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), }; static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info) -- cgit From 6bfcca44a6e7b0a6d92eab39c4cb830516b9568c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 20 Jun 2016 11:38:37 -0700 Subject: hwmon: (core) Add energy and humidity attribute support to new API Acked-by: Punit Agrawal Reviewed-by: Jonathan Cameron Signed-off-by: Guenter Roeck --- drivers/hwmon/hwmon.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 85f4e27548cb..2dd4e0acfe12 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -356,12 +356,30 @@ static const char * const hwmon_power_attr_templates[] = { [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 *__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, }; static const int __templates_size[] = { @@ -370,6 +388,8 @@ static const int __templates_size[] = { [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), }; static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info) -- cgit From 8faee73f92cd4dd4928e6860001315a0cc834c99 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 25 Jun 2016 19:52:13 -0700 Subject: hwmon: (core) Add fan attribute support to new API Acked-by: Punit Agrawal Reviewed-by: Jonathan Cameron Signed-off-by: Guenter Roeck --- drivers/hwmon/hwmon.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 2dd4e0acfe12..4729d4742ab7 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -372,6 +372,20 @@ static const char * const hwmon_humidity_attr_templates[] = { [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 *__templates[] = { [hwmon_chip] = hwmon_chip_attr_templates, [hwmon_temp] = hwmon_temp_attr_templates, @@ -380,6 +394,7 @@ static const char * const *__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, }; static const int __templates_size[] = { @@ -390,6 +405,7 @@ static const int __templates_size[] = { [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), }; static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info) -- cgit From f9f7bb3a0efafb662a4c639bc62df1df2b7321f9 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 26 Jun 2016 12:20:46 -0700 Subject: hwmon: (core) Add basic pwm attribute support to new API Add basic pwm attribute support (no auto attributes) to new API. Reviewed-by: Jonathan Cameron Signed-off-by: Guenter Roeck --- drivers/hwmon/hwmon.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 4729d4742ab7..fade170977e8 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -386,6 +386,13 @@ static const char * const hwmon_fan_attr_templates[] = { [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, @@ -395,6 +402,7 @@ static const char * const *__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[] = { @@ -406,6 +414,7 @@ static const int __templates_size[] = { [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) -- cgit From fc72af3ad43512bdea27cbd98f7acdb1e0354c2e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 3 Aug 2016 22:07:18 -0700 Subject: hwmon: (nct6775) Do not accept force_id unless chip is found Since commit 698a7c24a544 ("hwmon: (nct6775) Support two SuperIO chips in the same system"), the driver supports two Super-IO chips. This has the undesirable side effect that force_id always detects a second chip at address 0xfff8, even if no chip exists at that address. nct6775: Found NCT6793D or compatible chip at 0x4e:0xfff8 If no chip at all is found at a given SIO address, it does not make sense to instantiate it. Limit force_id to only work if some chip is found, that is if the chip ID returns a value other than 0xffff. Reviewed-by: Jean Delvare Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index d087a8e00cf5..7859a30ce31e 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -4232,11 +4232,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; -- cgit From 30fe976fe7a8de2d29c152cdcae207231503d564 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 1 Aug 2016 12:07:18 +0200 Subject: hwmon: (ltc4151) Make shunt-resistor configurable Allow to specify the resistance of the attached shunt via DT by adding the shunt-resistor property. Fall-back to the previous default (1 mOhm) if unset. Signed-off-by: Daniel Golle [groeck: Fixed 'line over 80 columns' checkpatch warning] Signed-off-by: Guenter Roeck --- drivers/hwmon/ltc4151.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') 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 #include +#include #include #include #include @@ -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, -- cgit From aa18cc911ae2f172429d59169e95726b8e63525c Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Mon, 8 Aug 2016 13:35:45 +1200 Subject: hwmon: (adt7470) Expose PWM frequency to sysfs The ADT7470 supports a variety of PWM frequencies. This patch allows the frequency to be configured and viewed through the sysfs entry pwm1_freq. Signed-off-by: Joshua Scott Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index f5da39a68929..94550d79ca48 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -32,6 +32,7 @@ #include #include #include +#include /* 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; @@ -688,6 +695,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) @@ -1038,6 +1109,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 +1227,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, -- cgit From 86430c1a66fe490fc97b0970575c223e27dee49d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 12 Aug 2016 06:28:15 -0700 Subject: hwmon: (core) Avoid cyclic dependency between hwmon and thermal_sys If both hwmon and thermal_sys are built as modules, and CONFIG_THERMAL_HWMON is enabled, the following cyclic module dependency is reported. depmod: ERROR: Found 2 modules in dependency cycles! depmod: ERROR: Cycle detected: hwmon -> thermal_sys -> hwmon Fixes: e4bce763adb2 ("hwmon: (core) New hwmon registration API") Reported-by: Vignesh R Cc: Keerthy J Signed-off-by: Guenter Roeck --- drivers/hwmon/hwmon.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index fade170977e8..adae6848ffb2 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -108,7 +108,13 @@ static DEFINE_IDA(hwmon_ida); /* Thermal zone handling */ -#if IS_REACHABLE(CONFIG_THERMAL) && defined(CONFIG_THERMAL_OF) +/* + * The complex conditional is necessary to avoid a cyclic dependency + * between hwmon and thermal_sys modules. + */ +#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; @@ -153,7 +159,7 @@ static int hwmon_thermal_add_sensor(struct device *dev, { return 0; } -#endif /* IS_REACHABLE(CONFIG_THERMAL) && defined(CONFIG_THERMAL_OF) */ +#endif /* IS_REACHABLE(CONFIG_THERMAL) && ... */ /* sysfs attribute management */ -- cgit From ed42cfa881e1d8d9603b7cb872199e3c8e0d1b19 Mon Sep 17 00:00:00 2001 From: hotran Date: Thu, 21 Jul 2016 15:37:32 -0700 Subject: hwmon: Add xgene hwmon driver This patch adds hardware temperature and power reading support for APM X-Gene SoC using the mailbox communication interface. Signed-off-by: Hoan Tran Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 7 + drivers/hwmon/Makefile | 1 + drivers/hwmon/xgene-hwmon.c | 755 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 763 insertions(+) create mode 100644 drivers/hwmon/xgene-hwmon.c (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index eaf2f916d48c..655a462f3622 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1823,6 +1823,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/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c new file mode 100644 index 000000000000..bc78a5d10182 --- /dev/null +++ b/drivers/hwmon/xgene-hwmon.c @@ -0,0 +1,755 @@ +/* + * APM X-Gene SoC Hardware Monitoring Driver + * + * Copyright (c) 2016, Applied Micro Circuits Corporation + * Author: Loc Ho + * Hoan Tran + * + * 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 . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 = readw_relaxed(addr); + ret = val & mask; + val &= ~mask; + writew_relaxed(val, addr); + + 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; + void *ptr = 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 */ + writel_relaxed(PCC_SIGNATURE_MASK | ctx->mbox_idx, + &generic_comm_base->signature); + + /* Write to the shared command region */ + writew_relaxed(MSG_TYPE(msg[0]) | PCCC_GENERATE_DB_INT, + &generic_comm_base->command); + + /* Flip CMD COMPLETE bit */ + val = readw_relaxed(&generic_comm_base->status); + val &= ~PCCS_CMD_COMPLETE; + writew_relaxed(val, &generic_comm_base->status); + + /* Copy the message to the PCC comm space */ + for (i = 0; i < sizeof(struct slimpro_resp_msg) / 4; i++) + writel_relaxed(msg[i], ptr + i * 4); + + /* 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); + } +} + +/* + * 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); + struct slimpro_resp_msg amsg; + + /* + * 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; + } + + amsg.msg = ((u32 *)msg)[0]; + amsg.param1 = ((u32 *)msg)[1]; + amsg.param2 = ((u32 *)msg)[2]; + + /* 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); +} + +/* + * 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; + + 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; + + /* 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 = + acpi_os_ioremap(ctx->comm_base_addr, + cppc_ss->length); + } 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; + } + + 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); + + 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; + } + + dev_info(&pdev->dev, "APM X-Gene SoC HW monitor driver registered\n"); + + return 0; + +out: + kfifo_free(&ctx->async_msg_fifo); +out_mbox_free: + if (acpi_disabled) + mbox_free_channel(ctx->mbox_chan); + else + pcc_mbox_free_channel(ctx->mbox_chan); + + 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"); -- cgit From 2d5aee433d9c5bb6075eb42039a21c5b3728d22d Mon Sep 17 00:00:00 2001 From: Thilo Cestonaro Date: Tue, 16 Aug 2016 13:07:53 +0200 Subject: hwmon: (ftsteutates) Add i2c detect functionality Signed-off-by: Thilo Cestonaro Signed-off-by: Guenter Roeck --- drivers/hwmon/ftsteutates.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'drivers/hwmon') 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); -- cgit From 43943ebb9f0cee2774df64d48f4a21171acf9c85 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Tue, 16 Aug 2016 15:17:13 +0200 Subject: hwmon: (scpi) Delete unnecessary assignment for the field "owner" The field "owner" is set by the core. Thus delete an unneeded initialisation. Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Signed-off-by: Markus Elfring Acked-by: Sudeep Holla Signed-off-by: Guenter Roeck --- drivers/hwmon/scpi-hwmon.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/hwmon') 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, -- cgit From 50b2b02c40340c4ac3fa1e12eb6e600a10c95e72 Mon Sep 17 00:00:00 2001 From: Matt Weber Date: Fri, 19 Aug 2016 21:08:45 -0500 Subject: hwmon: (ucd9000) Add support for UCD90160 Power Supply Sequencer The UCD90160 Power Supply Sequencer reuses the existing register layout, so just an id addition was required. Signed-off-by: Matthew Weber Signed-off-by: Ronak Desai [groeck: Updated description, ordered alphabetically, added documentation] Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/Kconfig | 6 +++--- drivers/hwmon/pmbus/ucd9000.c | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') 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/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 #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}, {} -- cgit From d5f3f6c8123388a6f978f2593cfe0e8ba8c30801 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 29 Aug 2016 13:33:29 +0200 Subject: hwmon: (it87) Drop useless comments Remove the index comments at the end of it87_attributes_in. They serve no purpose (as there is no reference to them in it87_in_is_visible) and some of them were obviously wrong. Signed-off-by: Jean Delvare Cc: Martin Blumenstingl Cc: Guenter Roeck Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') 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 }; -- cgit From a6cdeefeca47b834109bb13941c699be92c4ff97 Mon Sep 17 00:00:00 2001 From: Mike Looijmans Date: Mon, 22 Aug 2016 08:47:51 +0200 Subject: hwmon: (max6650) Add devicetree support Parse devicetree parameters for voltage and prescaler setting. This allows using multiple max6550 devices with varying settings, and also makes it possible to instantiate and configure the device using devicetree. Signed-off-by: Mike Looijmans Signed-off-by: Guenter Roeck --- drivers/hwmon/max6650.c | 47 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 11 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index 162a520f4bd6..c87517aee83a 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -39,6 +39,7 @@ #include #include #include +#include /* * 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); @@ -566,6 +580,17 @@ static int max6650_init_client(struct max6650_data *data, struct device *dev = &client->dev; int config; int err = -EIO; + u32 voltage; + u32 prescale; + + 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 +599,7 @@ static int max6650_init_client(struct max6650_data *data, return err; } - switch (fan_voltage) { + switch (voltage) { case 0: break; case 5: @@ -584,14 +609,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,10 +635,11 @@ 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)); /* @@ -651,6 +673,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 +685,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 +715,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, -- cgit From 20005cc58da50035c9b64572d4091c184dfa24c0 Mon Sep 17 00:00:00 2001 From: Mike Looijmans Date: Wed, 24 Aug 2016 10:13:26 +0200 Subject: hwmon: (max6650) Allow fan shutdown and initial rpm target The fan can be stopped by writing "3" to pwm1_enable in sysfs. Add devicetree property for early initialization of the fan controller to prevent overheating, for example when resetting the board while the fan was completely turned off. Also improve error reporting, I2C failures were ignored while writing new values. Signed-off-by: Mike Looijmans Signed-off-by: Guenter Roeck --- drivers/hwmon/max6650.c | 108 ++++++++++++++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 41 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index c87517aee83a..a993b44ed538 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -185,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) { @@ -266,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); @@ -288,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) @@ -298,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; } @@ -355,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; } /* @@ -369,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]); } @@ -385,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); @@ -582,6 +617,7 @@ static int max6650_init_client(struct max6650_data *data, int err = -EIO; u32 voltage; u32 prescale; + u32 target_rpm; if (of_property_read_u32(dev->of_node, "maxim,fan-microvolt", &voltage)) @@ -642,22 +678,6 @@ static int max6650_init_client(struct max6650_data *data, (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; @@ -666,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; } -- cgit From 9417fefe6f6ec2005ff302fba33038f58ed766e3 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Thu, 8 Sep 2016 16:28:35 +0200 Subject: hwmon: (iio_hwmon) defer probe when no channel is found iio_channel_get_all returns -ENODEV when it cannot find either phandles and properties in the Device Tree or channels whose consumer_dev_name matches iio_hwmon in iio_map_list. The iio_map_list is filled in by iio drivers which might be probed after iio_hwmon. It is better to defer the probe of iio_hwmon if such error is returned by iio_channel_get_all in order to let a chance to iio drivers to expose channels in iio_map_list. Signed-off-by: Quentin Schulz Signed-off-by: Guenter Roeck --- drivers/hwmon/iio_hwmon.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/hwmon') 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) { -- cgit From 2ca492e22cb70a001749377506bd22eb06f60ecc Mon Sep 17 00:00:00 2001 From: hotran Date: Thu, 8 Sep 2016 09:33:10 -0700 Subject: hwmon: (xgene) Fix crash when alarm occurs before driver probe The system crashes during probing xgene-hwmon driver when temperature alarm interrupt occurs before. It's because - xgene_hwmon_probe() requests mailbox channel which also enables the mailbox interrupt. - As temperature alarm interrupt is pending, ISR runs and crashes when accesses into invalid resourse as unmapped PCC shared memory. This patch fixes this issue by saving this alarm message and scheduling a bottom handler after xgene_hwmon_probe() finish. Signed-off-by: Hoan Tran Reported-by: Itaru Kitayama Signed-off-by: Guenter Roeck --- drivers/hwmon/xgene-hwmon.c | 69 ++++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 19 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c index bc78a5d10182..aa44579a1e5a 100644 --- a/drivers/hwmon/xgene-hwmon.c +++ b/drivers/hwmon/xgene-hwmon.c @@ -465,13 +465,34 @@ static void xgene_hwmon_evt_work(struct work_struct *work) } } +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); - 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, msg) < 0) + return; /* * Response message format: @@ -500,12 +521,8 @@ static void xgene_hwmon_rx_cb(struct mbox_client *cl, void *msg) return; } - amsg.msg = ((u32 *)msg)[0]; - amsg.param1 = ((u32 *)msg)[1]; - amsg.param2 = ((u32 *)msg)[2]; - /* Enqueue to the FIFO */ - kfifo_in_spinlocked(&ctx->async_msg_fifo, &amsg, + kfifo_in_spinlocked(&ctx->async_msg_fifo, msg, sizeof(struct slimpro_resp_msg), &ctx->kfifo_lock); /* Schedule the bottom handler */ schedule_work(&ctx->workq); @@ -520,6 +537,15 @@ static void xgene_hwmon_pcc_rx_cb(struct mbox_client *cl, void *msg) 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, @@ -596,6 +622,17 @@ static int xgene_hwmon_probe(struct platform_device *pdev) 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; @@ -676,17 +713,6 @@ static int xgene_hwmon_probe(struct platform_device *pdev) ctx->usecs_lat = PCC_NUM_RETRIES * cppc_ss->latency; } - 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); - ctx->hwmon_dev = hwmon_device_register_with_groups(ctx->dev, "apm_xgene", ctx, @@ -697,17 +723,22 @@ static int xgene_hwmon_probe(struct platform_device *pdev) 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: - kfifo_free(&ctx->async_msg_fifo); -out_mbox_free: 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; } -- cgit From 08b024338166abebcc2216f97693336f7ac0bf42 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 19 Jun 2016 19:15:05 -0700 Subject: hwmon: (lm75) Convert to use new hwmon registration API Simplify code and reduce code size by using the new hwmon registration API. Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 1 - drivers/hwmon/lm75.c | 185 +++++++++++++++++++++++++++++++------------------- 2 files changed, 116 insertions(+), 70 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 655a462f3622..fcdaf6773238 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 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 #include #include -#include #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; -- cgit From eb1c8f4325d59e913d561d1fac8c9bc8154eb274 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 18 Jun 2016 19:56:08 -0700 Subject: hwmon: (lm90) Convert to use new hwmon registration API Reduce driver complexity and size by converting it to the new hwmon API. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 811 +++++++++++++++++++++++++++------------------------ 1 file changed, 435 insertions(+), 376 deletions(-) (limited to 'drivers/hwmon') 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 #include #include -#include #include #include #include @@ -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); -- cgit From 0208531d9090af3ca9d985dfdf66d1a71da0e58f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 20 Jun 2016 10:50:27 -0700 Subject: hwmon: (tmp102) Convert to use new hwmon registration API Simplify code and reduce code size by using the new hwmon registration API. Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 1 - drivers/hwmon/tmp102.c | 154 +++++++++++++++++++++++++++++-------------------- 2 files changed, 90 insertions(+), 65 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index fcdaf6773238..6c82ee240c4c 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1571,7 +1571,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 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 #include #include -#include #include #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; -- cgit From bb43cc45dac37475c70b114502f067c535389edc Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 25 Jun 2016 10:41:18 -0700 Subject: hwmon: (tmp421) Convert to use new hwmon registration API Simplify code and reduce code size by using the new hwmon registration API. Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp421.c | 133 +++++++++++++++++++++---------------------------- 1 file changed, 58 insertions(+), 75 deletions(-) (limited to 'drivers/hwmon') 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); } -- cgit From a584287cd26cefba42d72b0cb7050e01b3aa77b8 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 25 Jun 2016 12:14:32 -0700 Subject: hwmon: (ltc4245) Convert to use new hwmon registration API Simplify code and reduce code size by using the new hwmon registration API. Signed-off-by: Guenter Roeck --- drivers/hwmon/ltc4245.c | 362 +++++++++++++++++++++++------------------------- 1 file changed, 175 insertions(+), 187 deletions(-) (limited to 'drivers/hwmon') 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 #include #include +#include #include #include #include @@ -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); } -- cgit From d65a5102a99f5b2f95956b9deff66052b563c996 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 26 Jun 2016 12:22:03 -0700 Subject: hwmon: (nct7904) Convert to use new hwmon registration API Simplify code and reduce code size by using the new hwmon registration API. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct7904.c | 555 +++++++++++++++++++++++------------------------- 1 file changed, 270 insertions(+), 285 deletions(-) (limited to 'drivers/hwmon') 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 #include #include -#include #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); } -- cgit From 54187ff9d766b2138bd83df74b6df7d78d422965 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 1 Jul 2016 21:24:29 -0700 Subject: hwmon: (max31790) Convert to use new hwmon registration API Simplify code and reduce code size by using the new hwmon registration API. Signed-off-by: Guenter Roeck --- drivers/hwmon/max31790.c | 521 ++++++++++++++++++++--------------------------- 1 file changed, 224 insertions(+), 297 deletions(-) (limited to 'drivers/hwmon') 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 #include -#include #include #include #include @@ -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); } -- cgit From fcc448cfe48ed7a74a299709457b32a7e5ca663a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 2 Jul 2016 21:40:19 -0700 Subject: hwmon: (jc42) Convert to use new hwmon registration API Simplify code and reduce code size by using the new hwmon registration API. Signed-off-by: Guenter Roeck --- drivers/hwmon/jc42.c | 293 ++++++++++++++++++++++++++------------------------- 1 file changed, 149 insertions(+), 144 deletions(-) (limited to 'drivers/hwmon') 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 #include #include -#include #include #include #include @@ -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); } -- cgit From 3e9046281bd5deb180e0694b93e6169424582e3c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 4 Jul 2016 19:58:11 -0700 Subject: hwmon: (lm95241) Convert to use new hwmon registration API Simplify code and reduce code size by using the new hwmon registration API. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95241.c | 430 +++++++++++++++++++++++++++--------------------- 1 file changed, 241 insertions(+), 189 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index 3d96c3fcba9b..844c2a0aaba0 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -23,11 +23,9 @@ #include #include #include -#include #include #include #include -#include #define DEVNAME "lm95241" @@ -137,229 +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 & BIT(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 ? - "-128000\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; + 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; -} - -static ssize_t show_max(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct lm95241_data *data = dev_get_drvdata(dev); - - return snprintf(buf, PAGE_SIZE - 1, - data->config & to_sensor_dev_attr(attr)->index ? - "127875\n" : "255875\n"); + return ret; } -static ssize_t set_max(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static int lm95241_write_temp(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; + 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; -} - -static ssize_t show_fault(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct lm95241_data *data = lm95241_update_device(dev); - - return snprintf(buf, PAGE_SIZE - 1, "%d", - !!(data->status & to_sensor_dev_attr(attr)->index)); + 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", data->interval); + 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; - int convrate; - u8 config; - - if (kstrtoul(buf, 10, &val) < 0) - return -EINVAL; - - mutex_lock(&data->update_lock); - - 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; + 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; } - - data->interval = convrate; - data->config = config; - i2c_smbus_write_byte_data(data->client, LM95241_REG_RW_CONFIG, - config); - mutex_unlock(&data->update_lock); - - return count; + 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 SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, R1DM); -static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, R2DM); -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, - &sensor_dev_attr_temp2_fault.dev_attr.attr, - &sensor_dev_attr_temp3_fault.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) @@ -408,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) { @@ -425,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); } -- cgit From c0a4b9ec1b43ebb9d5001e3bf86f58d4ca0ffe18 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 5 Jul 2016 19:10:52 -0700 Subject: hwmon: (lm95245) Use new hwmon registration API Simplify code and reduce code size by using the new hwmon registration API. Other changes: - Convert to use regmap, and drop local caching. This avoids reading registers unnecessarily, and uses regmap for caching of non-volatile registers. - Add support for temp2_max, temp2_max_alarm, temp2_max_hyst, and temp2_offset. - Order include files alphabetically - Drop FSF address - Check errors from register read and write functions and report to userspace. - Accept negative hysteresis values. While unlikely, a maximum limit _can_ be set to a value smaller than 31 degrees C, which makes negative hysteresis values possible. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95245.c | 638 ++++++++++++++++++++++++++++-------------------- 1 file changed, 379 insertions(+), 259 deletions(-) (limited to 'drivers/hwmon') 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 +#include #include -#include -#include -#include #include -#include -#include +#include +#include #include -#include +#include +#include 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); } -- cgit From c7cefce03e691270c0e5e117248e14661e9c9cad Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 9 Sep 2016 22:10:45 +0200 Subject: hwmon: (xgene) access mailbox as RAM The newly added hwmon driver fails to build in an allmodconfig kernel: ERROR: "memblock_is_memory" [drivers/hwmon/xgene-hwmon.ko] undefined! According to comments in the code, the mailbox is a shared memory region, not a set of MMIO registers, so we should use memremap() for mapping it instead of ioremap or acpi_os_ioremap, and pointer dereferences instead of readl/writel. The driver already uses plain kernel pointers, so it's a bit unusual to work with functions that operate on __iomem pointers, and this fixes that part too. I'm using READ_ONCE/WRITE_ONCE here to keep the existing behavior regarding the ordering of the accesses from the CPU, but note that there are no barriers (also unchanged from before). I'm also keeping the endianness behavior, though I'm unsure whether the message data was supposed to be in LE32 format in the first place, it's possible this was meant to be interpreted as a byte stream instead. Signed-off-by: Arnd Bergmann Acked-by: Hoan Tran Tested-by: Hoan Tran Signed-off-by: Guenter Roeck --- drivers/hwmon/xgene-hwmon.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c index aa44579a1e5a..9c0dbb8191ad 100644 --- a/drivers/hwmon/xgene-hwmon.c +++ b/drivers/hwmon/xgene-hwmon.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -34,7 +35,7 @@ #include #include #include -#include + #include /* SLIMpro message defines */ @@ -126,10 +127,10 @@ static u16 xgene_word_tst_and_clr(u16 *addr, u16 mask) { u16 ret, val; - val = readw_relaxed(addr); + val = le16_to_cpu(READ_ONCE(*addr)); ret = val & mask; val &= ~mask; - writew_relaxed(val, addr); + WRITE_ONCE(*addr, cpu_to_le16(val)); return ret; } @@ -137,7 +138,7 @@ static u16 xgene_word_tst_and_clr(u16 *addr, u16 mask) 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; - void *ptr = generic_comm_base + 1; + u32 *ptr = (void *)(generic_comm_base + 1); int rc, i; u16 val; @@ -146,21 +147,21 @@ static int xgene_hwmon_pcc_rd(struct xgene_hwmon_dev *ctx, u32 *msg) ctx->resp_pending = true; /* Write signature for subspace */ - writel_relaxed(PCC_SIGNATURE_MASK | ctx->mbox_idx, - &generic_comm_base->signature); + WRITE_ONCE(generic_comm_base->signature, + cpu_to_le32(PCC_SIGNATURE_MASK | ctx->mbox_idx)); /* Write to the shared command region */ - writew_relaxed(MSG_TYPE(msg[0]) | PCCC_GENERATE_DB_INT, - &generic_comm_base->command); + WRITE_ONCE(generic_comm_base->command, + cpu_to_le16(MSG_TYPE(msg[0]) | PCCC_GENERATE_DB_INT)); /* Flip CMD COMPLETE bit */ - val = readw_relaxed(&generic_comm_base->status); + val = le16_to_cpu(READ_ONCE(generic_comm_base->status)); val &= ~PCCS_CMD_COMPLETE; - writew_relaxed(val, &generic_comm_base->status); + 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++) - writel_relaxed(msg[i], ptr + i * 4); + WRITE_ONCE(ptr[i], cpu_to_le32(msg[i])); /* Ring the doorbell */ rc = mbox_send_message(ctx->mbox_chan, msg); @@ -689,9 +690,9 @@ static int xgene_hwmon_probe(struct platform_device *pdev) */ ctx->comm_base_addr = cppc_ss->base_address; if (ctx->comm_base_addr) { - ctx->pcc_comm_addr = - acpi_os_ioremap(ctx->comm_base_addr, - cppc_ss->length); + 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; -- cgit From feca3132d9fd9822a28f67c25051efd468174de8 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Fri, 9 Sep 2016 17:19:25 +1200 Subject: hwmon: (adt7470) Add write support to alarm_mask Add write support for the alarm_mask. A base of 0 is provided so that either hex or decimal can be used. The hex format when reading alarm_mask is unchanged. Signed-off-by: Joshua Scott Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 94550d79ca48..dbd758be1924 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -545,6 +545,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) @@ -989,7 +1011,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, -- cgit From 93cacfd41f82eea00b420d62e29a493ca3d07b74 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Fri, 9 Sep 2016 17:19:26 +1200 Subject: hwmon: (adt7470) Allow faster removal adt7470_remove will wait for the update thread to complete before returning. This had a worst-case time of up to the user-configurable auto_update_interval. Replace msleep_interruptible with set_current_state and schedule_timeout so that kthread_stop will interrupt the sleep. Signed-off-by: Joshua Scott Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index dbd758be1924..ffe1edc10ef0 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -273,9 +273,12 @@ 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); -- cgit From acf32964de8e8f02839796f1fe512b50a192e141 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Tue, 13 Sep 2016 15:47:41 +1000 Subject: hwmon: (ibmpowernv) Fix label for cores numbers not threads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the label says "Core" but lists the thread numbers. This ends up looking like this: # cat /sys/class/hwmon/hwmon0/temp[1-4]_label Core 0-7 Core 8-15 Core 16-23 Core 24-31 This is misleading as it looks like it's cores 0-7 when it's actually threads 0-7. This changes the print to just give the core number, so the output now looks like this: # cat /sys/class/hwmon/hwmon0/temp[1-4]_label Core 0 Core 8 Core 16 Core 24 Signed-off-by: Michael Neuling Acked-by: Cédric Le Goater Signed-off-by: Guenter Roeck --- drivers/hwmon/ibmpowernv.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/hwmon') 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); -- cgit From ca05ab20f1292fb5f929d40cd923460e15af7b41 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 13 Sep 2016 15:21:59 +0800 Subject: hwmon: (lm95245) Select REGMAP_I2C This driver now uses regmap APIs, so it needs to select REGMAP_I2C. Signed-off-by: Axel Lin Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 6c82ee240c4c..45cef3d2c75c 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1118,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. -- cgit From 0a8784e792a03a04a2ec3bc443183a2f42058e30 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 13 Sep 2016 15:27:48 +0800 Subject: hwmon: (lm95241) Update module description to include LM95231 This driver also supports LM95231. Signed-off-by: Axel Lin Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95241.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index 844c2a0aaba0..8c573e6e9726 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -505,5 +505,5 @@ static struct i2c_driver lm95241_driver = { module_i2c_driver(lm95241_driver); MODULE_AUTHOR("Davide Rizzo "); -MODULE_DESCRIPTION("LM95241 sensor driver"); +MODULE_DESCRIPTION("LM95231/LM95241 sensor driver"); MODULE_LICENSE("GPL"); -- cgit From dc8b73ebe543f53ec78507a9d0d37073773f6157 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 16 Sep 2016 10:07:36 +0200 Subject: hwmon: (adt7470) No need for additional synchronization on kthread_stop() The kthread_stop() waits for the thread to exit. There is no need for an additional synchronization needed to join on the kthread. The completion was added by 89fac11cb3e7 ("adt7470: make automatic fan control really work"). Signed-off-by: Daniel Wagner Cc: Darrick J. Wong Cc: Jean Delvare Cc: Guenter Roeck Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index ffe1edc10ef0..6e60ca53406e 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -177,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; }; @@ -281,7 +280,6 @@ static int adt7470_update_thread(void *p) schedule_timeout(msecs_to_jiffies(data->auto_update_interval)); } - complete_all(&data->auto_update_stop); return 0; } @@ -1356,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)) { @@ -1371,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; } -- cgit From 7ce4190c4ca466ccd609845b97dce665317aee2a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 11 Sep 2016 12:42:52 -0700 Subject: hwmon: (nct6775) Add support for multiple virtual temperature sources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For virtual temperatures, the actual temperature values are written by software, presumably by the BIOS. This functionality is (as of right now) supported on NCT6791D, NCT6792D, and NCT6793D. On those chips, the temperatures are written into registers 0xea..0xef on page 0. This is known to be used on some Asus motherboards, where the actual temperature source can be configured in the BIOS. Report the 'virtual' temperatures for all monotoring sources to address this situation. Example for the resulting output (as seen with the 'sensors' command): nct6791-isa-0290 Adapter: ISA adapter ... Virtual_TEMP: +31.0°C PECI Agent 0: +38.5°C Virtual_TEMP: +32.0°C ... Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 7859a30ce31e..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) { -- cgit