diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-14 14:43:23 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-14 14:43:23 -0700 |
commit | 503698e12d68f6144b408b11156ad315943ffb53 (patch) | |
tree | 378979f76b321a114e910da59e06f1d6c2ff625e /drivers | |
parent | 6391f34e844c71ebf645058b2b643f70fed5b990 (diff) | |
parent | 78cebd0889c7b8b92f43e7667efc16636b858c34 (diff) | |
download | linux-503698e12d68f6144b408b11156ad315943ffb53.tar.bz2 |
Merge tag 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck:
- new driver for Sensirion SHTC1 humidity / temperature sensor
- convert ltc4151 and vexpress drivers to use devm functions
- drop generic chip detection from lm85 driver
- avoid forward declarations in atxp1 driver
- fix sign extensions in ina2xx driver
* tag 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging:
hwmon: vexpress: Use devm helper for hwmon device registration
hwmon: (atxp1) Avoid forward declaration
hwmon: add support for Sensirion SHTC1 sensor
hwmon: (ltc4151) Convert to devm_hwmon_device_register_with_groups
hwmon: (lm85) Drop generic detection
hwmon: (ina2xx) Cast to s16 on shunt and current regs
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hwmon/Kconfig | 10 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/atxp1.c | 42 | ||||
-rw-r--r-- | drivers/hwmon/ina2xx.c | 7 | ||||
-rw-r--r-- | drivers/hwmon/lm85.c | 33 | ||||
-rw-r--r-- | drivers/hwmon/ltc4151.c | 51 | ||||
-rw-r--r-- | drivers/hwmon/shtc1.c | 251 | ||||
-rw-r--r-- | drivers/hwmon/vexpress.c | 82 |
8 files changed, 335 insertions, 142 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 00343166feb1..08531a128f53 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1124,6 +1124,16 @@ config SENSORS_SHT21 This driver can also be built as a module. If so, the module will be called sht21. +config SENSORS_SHTC1 + tristate "Sensiron humidity and temperature sensors. SHTC1 and compat." + depends on I2C + help + If you say yes here you get support for the Sensiron SHTC1 and SHTW1 + humidity and temperature sensors. + + This driver can also be built as a module. If so, the module + will be called shtc1. + config SENSORS_S3C tristate "Samsung built-in ADC" depends on S3C_ADC diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 11798ad7e801..3dc0f02f71d2 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -126,6 +126,7 @@ obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o obj-$(CONFIG_SENSORS_SHT15) += sht15.o obj-$(CONFIG_SENSORS_SHT21) += sht21.o +obj-$(CONFIG_SENSORS_SHTC1) += shtc1.o obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o obj-$(CONFIG_SENSORS_SMM665) += smm665.o obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o diff --git a/drivers/hwmon/atxp1.c b/drivers/hwmon/atxp1.c index 6edce42c61d5..2ae8a304b5ef 100644 --- a/drivers/hwmon/atxp1.c +++ b/drivers/hwmon/atxp1.c @@ -45,30 +45,6 @@ MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>"); static const unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END }; -static int atxp1_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int atxp1_remove(struct i2c_client *client); -static struct atxp1_data *atxp1_update_device(struct device *dev); -static int atxp1_detect(struct i2c_client *client, struct i2c_board_info *info); - -static const struct i2c_device_id atxp1_id[] = { - { "atxp1", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, atxp1_id); - -static struct i2c_driver atxp1_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "atxp1", - }, - .probe = atxp1_probe, - .remove = atxp1_remove, - .id_table = atxp1_id, - .detect = atxp1_detect, - .address_list = normal_i2c, -}; - struct atxp1_data { struct device *hwmon_dev; struct mutex update_lock; @@ -386,4 +362,22 @@ static int atxp1_remove(struct i2c_client *client) return 0; }; +static const struct i2c_device_id atxp1_id[] = { + { "atxp1", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, atxp1_id); + +static struct i2c_driver atxp1_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "atxp1", + }, + .probe = atxp1_probe, + .remove = atxp1_remove, + .id_table = atxp1_id, + .detect = atxp1_detect, + .address_list = normal_i2c, +}; + module_i2c_driver(atxp1_driver); diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 93d26e8af3e2..bfd3f3eeabcd 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -148,7 +148,8 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg) switch (reg) { case INA2XX_SHUNT_VOLTAGE: - val = DIV_ROUND_CLOSEST(data->regs[reg], + /* signed register */ + val = DIV_ROUND_CLOSEST((s16)data->regs[reg], data->config->shunt_div); break; case INA2XX_BUS_VOLTAGE: @@ -160,8 +161,8 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg) val = data->regs[reg] * data->config->power_lsb; break; case INA2XX_CURRENT: - /* LSB=1mA (selected). Is in mA */ - val = data->regs[reg]; + /* signed register, LSB=1mA (selected), in mA */ + val = (s16)data->regs[reg]; break; default: /* programmer goofed */ diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index bed4af358308..b0129a54e1a6 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -5,7 +5,7 @@ * Copyright (c) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com> * Copyright (c) 2003 Margit Schubert-While <margitsw@t-online.de> * Copyright (c) 2004 Justin Thiessen <jthiessen@penguincomputing.com> - * Copyright (C) 2007--2009 Jean Delvare <jdelvare@suse.de> + * Copyright (C) 2007--2014 Jean Delvare <jdelvare@suse.de> * * Chip details at <http://www.national.com/ds/LM/LM85.pdf> * @@ -39,7 +39,7 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; enum chips { - any_chip, lm85b, lm85c, + lm85, adm1027, adt7463, adt7468, emc6d100, emc6d102, emc6d103, emc6d103s }; @@ -75,9 +75,6 @@ enum chips { #define LM85_COMPANY_NATIONAL 0x01 #define LM85_COMPANY_ANALOG_DEV 0x41 #define LM85_COMPANY_SMSC 0x5c -#define LM85_VERSTEP_VMASK 0xf0 -#define LM85_VERSTEP_GENERIC 0x60 -#define LM85_VERSTEP_GENERIC2 0x70 #define LM85_VERSTEP_LM85C 0x60 #define LM85_VERSTEP_LM85B 0x62 #define LM85_VERSTEP_LM96000_1 0x68 @@ -351,9 +348,9 @@ static const struct i2c_device_id lm85_id[] = { { "adm1027", adm1027 }, { "adt7463", adt7463 }, { "adt7468", adt7468 }, - { "lm85", any_chip }, - { "lm85b", lm85b }, - { "lm85c", lm85c }, + { "lm85", lm85 }, + { "lm85b", lm85 }, + { "lm85c", lm85 }, { "emc6d100", emc6d100 }, { "emc6d101", emc6d100 }, { "emc6d102", emc6d102 }, @@ -1281,7 +1278,7 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; int address = client->addr; - const char *type_name; + const char *type_name = NULL; int company, verstep; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { @@ -1297,16 +1294,6 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info) "Detecting device at 0x%02x with COMPANY: 0x%02x and VERSTEP: 0x%02x\n", address, company, verstep); - /* All supported chips have the version in common */ - if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC && - (verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC2) { - dev_dbg(&adapter->dev, - "Autodetection failed: unsupported version\n"); - return -ENODEV; - } - type_name = "lm85"; - - /* Now, refine the detection */ if (company == LM85_COMPANY_NATIONAL) { switch (verstep) { case LM85_VERSTEP_LM85C: @@ -1323,6 +1310,7 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info) "Found Winbond WPCD377I, ignoring\n"); return -ENODEV; } + type_name = "lm85"; break; } } else if (company == LM85_COMPANY_ANALOG_DEV) { @@ -1357,12 +1345,11 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info) type_name = "emc6d103s"; break; } - } else { - dev_dbg(&adapter->dev, - "Autodetection failed: unknown vendor\n"); - return -ENODEV; } + if (!type_name) + return -ENODEV; + strlcpy(info->type, type_name, I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/ltc4151.c b/drivers/hwmon/ltc4151.c index af81be1237c9..c86a18402496 100644 --- a/drivers/hwmon/ltc4151.c +++ b/drivers/hwmon/ltc4151.c @@ -47,7 +47,7 @@ #define LTC4151_ADIN_L 0x05 struct ltc4151_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; bool valid; @@ -59,8 +59,8 @@ struct ltc4151_data { static struct ltc4151_data *ltc4151_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct ltc4151_data *data = i2c_get_clientdata(client); + struct ltc4151_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; struct ltc4151_data *ret = data; mutex_lock(&data->update_lock); @@ -159,7 +159,7 @@ static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4151_show_value, NULL, * Finally, construct an array of pointers to members of the above objects, * as required for sysfs_create_group() */ -static struct attribute *ltc4151_attributes[] = { +static struct attribute *ltc4151_attrs[] = { &sensor_dev_attr_in1_input.dev_attr.attr, &sensor_dev_attr_in2_input.dev_attr.attr, @@ -167,54 +167,30 @@ static struct attribute *ltc4151_attributes[] = { NULL, }; - -static const struct attribute_group ltc4151_group = { - .attrs = ltc4151_attributes, -}; +ATTRIBUTE_GROUPS(ltc4151); static int ltc4151_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; struct ltc4151_data *data; - int ret; + struct device *hwmon_dev; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->update_lock); - /* Register sysfs hooks */ - ret = sysfs_create_group(&client->dev.kobj, <c4151_group); - if (ret) - return ret; - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - ret = PTR_ERR(data->hwmon_dev); - goto out_hwmon_device_register; - } - - return 0; - -out_hwmon_device_register: - sysfs_remove_group(&client->dev.kobj, <c4151_group); - return ret; -} - -static int ltc4151_remove(struct i2c_client *client) -{ - struct ltc4151_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, <c4151_group); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + ltc4151_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct i2c_device_id ltc4151_id[] = { @@ -229,7 +205,6 @@ static struct i2c_driver ltc4151_driver = { .name = "ltc4151", }, .probe = ltc4151_probe, - .remove = ltc4151_remove, .id_table = ltc4151_id, }; diff --git a/drivers/hwmon/shtc1.c b/drivers/hwmon/shtc1.c new file mode 100644 index 000000000000..decd7df995ab --- /dev/null +++ b/drivers/hwmon/shtc1.c @@ -0,0 +1,251 @@ +/* Sensirion SHTC1 humidity and temperature sensor driver + * + * Copyright (C) 2014 Sensirion AG, Switzerland + * Author: Johannes Winkelmann <johannes.winkelmann@sensirion.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/platform_data/shtc1.h> + +/* commands (high precision mode) */ +static const unsigned char shtc1_cmd_measure_blocking_hpm[] = { 0x7C, 0xA2 }; +static const unsigned char shtc1_cmd_measure_nonblocking_hpm[] = { 0x78, 0x66 }; + +/* commands (low precision mode) */ +static const unsigned char shtc1_cmd_measure_blocking_lpm[] = { 0x64, 0x58 }; +static const unsigned char shtc1_cmd_measure_nonblocking_lpm[] = { 0x60, 0x9c }; + +/* command for reading the ID register */ +static const unsigned char shtc1_cmd_read_id_reg[] = { 0xef, 0xc8 }; + +/* constants for reading the ID register */ +#define SHTC1_ID 0x07 +#define SHTC1_ID_REG_MASK 0x1f + +/* delays for non-blocking i2c commands, both in us */ +#define SHTC1_NONBLOCKING_WAIT_TIME_HPM 14400 +#define SHTC1_NONBLOCKING_WAIT_TIME_LPM 1000 + +#define SHTC1_CMD_LENGTH 2 +#define SHTC1_RESPONSE_LENGTH 6 + +struct shtc1_data { + struct i2c_client *client; + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + + const unsigned char *command; + unsigned int nonblocking_wait_time; /* in us */ + + struct shtc1_platform_data setup; + + int temperature; /* 1000 * temperature in dgr C */ + int humidity; /* 1000 * relative humidity in %RH */ +}; + +static int shtc1_update_values(struct i2c_client *client, + struct shtc1_data *data, + char *buf, int bufsize) +{ + int ret = i2c_master_send(client, data->command, SHTC1_CMD_LENGTH); + if (ret != SHTC1_CMD_LENGTH) { + dev_err(&client->dev, "failed to send command: %d\n", ret); + return ret < 0 ? ret : -EIO; + } + + /* + * In blocking mode (clock stretching mode) the I2C bus + * is blocked for other traffic, thus the call to i2c_master_recv() + * will wait until the data is ready. For non blocking mode, we + * have to wait ourselves. + */ + if (!data->setup.blocking_io) + usleep_range(data->nonblocking_wait_time, + data->nonblocking_wait_time + 1000); + + ret = i2c_master_recv(client, buf, bufsize); + if (ret != bufsize) { + dev_err(&client->dev, "failed to read values: %d\n", ret); + return ret < 0 ? ret : -EIO; + } + + return 0; +} + +/* sysfs attributes */ +static struct shtc1_data *shtc1_update_client(struct device *dev) +{ + struct shtc1_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned char buf[SHTC1_RESPONSE_LENGTH]; + int val; + int ret = 0; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ / 10) || !data->valid) { + ret = shtc1_update_values(client, data, buf, sizeof(buf)); + if (ret) + goto out; + + /* + * From datasheet: + * T = -45 + 175 * ST / 2^16 + * RH = 100 * SRH / 2^16 + * + * Adapted for integer fixed point (3 digit) arithmetic. + */ + val = be16_to_cpup((__be16 *)buf); + data->temperature = ((21875 * val) >> 13) - 45000; + val = be16_to_cpup((__be16 *)(buf + 3)); + data->humidity = ((12500 * val) >> 13); + + data->last_updated = jiffies; + data->valid = true; + } + +out: + mutex_unlock(&data->update_lock); + + return ret == 0 ? data : ERR_PTR(ret); +} + +static ssize_t temp1_input_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct shtc1_data *data = shtc1_update_client(dev); + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", data->temperature); +} + +static ssize_t humidity1_input_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct shtc1_data *data = shtc1_update_client(dev); + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", data->humidity); +} + +static DEVICE_ATTR_RO(temp1_input); +static DEVICE_ATTR_RO(humidity1_input); + +static struct attribute *shtc1_attrs[] = { + &dev_attr_temp1_input.attr, + &dev_attr_humidity1_input.attr, + NULL +}; + +ATTRIBUTE_GROUPS(shtc1); + +static void shtc1_select_command(struct shtc1_data *data) +{ + if (data->setup.high_precision) { + data->command = data->setup.blocking_io ? + shtc1_cmd_measure_blocking_hpm : + shtc1_cmd_measure_nonblocking_hpm; + data->nonblocking_wait_time = SHTC1_NONBLOCKING_WAIT_TIME_HPM; + + } else { + data->command = data->setup.blocking_io ? + shtc1_cmd_measure_blocking_lpm : + shtc1_cmd_measure_nonblocking_lpm; + data->nonblocking_wait_time = SHTC1_NONBLOCKING_WAIT_TIME_LPM; + } +} + +static int shtc1_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + char id_reg[2]; + struct shtc1_data *data; + struct device *hwmon_dev; + struct i2c_adapter *adap = client->adapter; + struct device *dev = &client->dev; + + if (!i2c_check_functionality(adap, I2C_FUNC_I2C)) { + dev_err(dev, "plain i2c transactions not supported\n"); + return -ENODEV; + } + + ret = i2c_master_send(client, shtc1_cmd_read_id_reg, SHTC1_CMD_LENGTH); + if (ret != SHTC1_CMD_LENGTH) { + dev_err(dev, "could not send read_id_reg command: %d\n", ret); + return ret < 0 ? ret : -ENODEV; + } + ret = i2c_master_recv(client, id_reg, sizeof(id_reg)); + if (ret != sizeof(id_reg)) { + dev_err(dev, "could not read ID register: %d\n", ret); + return -ENODEV; + } + if ((id_reg[1] & SHTC1_ID_REG_MASK) != SHTC1_ID) { + dev_err(dev, "ID register doesn't match\n"); + return -ENODEV; + } + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->setup.blocking_io = false; + data->setup.high_precision = true; + data->client = client; + + if (client->dev.platform_data) + data->setup = *(struct shtc1_platform_data *)dev->platform_data; + shtc1_select_command(data); + mutex_init(&data->update_lock); + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, + client->name, + data, + shtc1_groups); + if (IS_ERR(hwmon_dev)) + dev_dbg(dev, "unable to register hwmon device\n"); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +/* device ID table */ +static const struct i2c_device_id shtc1_id[] = { + { "shtc1", 0 }, + { "shtw1", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, shtc1_id); + +static struct i2c_driver shtc1_i2c_driver = { + .driver.name = "shtc1", + .probe = shtc1_probe, + .id_table = shtc1_id, +}; + +module_i2c_driver(shtc1_i2c_driver); + +MODULE_AUTHOR("Johannes Winkelmann <johannes.winkelmann@sensirion.com>"); +MODULE_DESCRIPTION("Sensirion SHTC1 humidity and temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/vexpress.c b/drivers/hwmon/vexpress.c index 611f34c7333d..c53619086f33 100644 --- a/drivers/hwmon/vexpress.c +++ b/drivers/hwmon/vexpress.c @@ -27,17 +27,8 @@ struct vexpress_hwmon_data { struct device *hwmon_dev; struct regmap *reg; - const char *name; }; -static ssize_t vexpress_hwmon_name_show(struct device *dev, - struct device_attribute *dev_attr, char *buffer) -{ - struct vexpress_hwmon_data *data = dev_get_drvdata(dev); - - return sprintf(buffer, "%s\n", data->name); -} - static ssize_t vexpress_hwmon_label_show(struct device *dev, struct device_attribute *dev_attr, char *buffer) { @@ -95,16 +86,6 @@ static umode_t vexpress_hwmon_attr_is_visible(struct kobject *kobj, return attr->mode; } -static DEVICE_ATTR(name, S_IRUGO, vexpress_hwmon_name_show, NULL); - -#define VEXPRESS_HWMON_ATTRS(_name, _label_attr, _input_attr) \ -struct attribute *vexpress_hwmon_attrs_##_name[] = { \ - &dev_attr_name.attr, \ - &dev_attr_##_label_attr.attr, \ - &sensor_dev_attr_##_input_attr.dev_attr.attr, \ - NULL \ -} - struct vexpress_hwmon_type { const char *name; const struct attribute_group **attr_groups; @@ -114,7 +95,11 @@ struct vexpress_hwmon_type { static DEVICE_ATTR(in1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, vexpress_hwmon_u32_show, NULL, 1000); -static VEXPRESS_HWMON_ATTRS(volt, in1_label, in1_input); +static struct attribute *vexpress_hwmon_attrs_volt[] = { + &dev_attr_in1_label.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + NULL +}; static struct attribute_group vexpress_hwmon_group_volt = { .is_visible = vexpress_hwmon_attr_is_visible, .attrs = vexpress_hwmon_attrs_volt, @@ -131,7 +116,11 @@ static struct vexpress_hwmon_type vexpress_hwmon_volt = { static DEVICE_ATTR(curr1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, vexpress_hwmon_u32_show, NULL, 1000); -static VEXPRESS_HWMON_ATTRS(amp, curr1_label, curr1_input); +static struct attribute *vexpress_hwmon_attrs_amp[] = { + &dev_attr_curr1_label.attr, + &sensor_dev_attr_curr1_input.dev_attr.attr, + NULL +}; static struct attribute_group vexpress_hwmon_group_amp = { .is_visible = vexpress_hwmon_attr_is_visible, .attrs = vexpress_hwmon_attrs_amp, @@ -147,7 +136,11 @@ static struct vexpress_hwmon_type vexpress_hwmon_amp = { static DEVICE_ATTR(temp1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, vexpress_hwmon_u32_show, NULL, 1000); -static VEXPRESS_HWMON_ATTRS(temp, temp1_label, temp1_input); +static struct attribute *vexpress_hwmon_attrs_temp[] = { + &dev_attr_temp1_label.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL +}; static struct attribute_group vexpress_hwmon_group_temp = { .is_visible = vexpress_hwmon_attr_is_visible, .attrs = vexpress_hwmon_attrs_temp, @@ -163,7 +156,11 @@ static struct vexpress_hwmon_type vexpress_hwmon_temp = { static DEVICE_ATTR(power1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, vexpress_hwmon_u32_show, NULL, 1); -static VEXPRESS_HWMON_ATTRS(power, power1_label, power1_input); +static struct attribute *vexpress_hwmon_attrs_power[] = { + &dev_attr_power1_label.attr, + &sensor_dev_attr_power1_input.dev_attr.attr, + NULL +}; static struct attribute_group vexpress_hwmon_group_power = { .is_visible = vexpress_hwmon_attr_is_visible, .attrs = vexpress_hwmon_attrs_power, @@ -179,7 +176,11 @@ static struct vexpress_hwmon_type vexpress_hwmon_power = { static DEVICE_ATTR(energy1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); static SENSOR_DEVICE_ATTR(energy1_input, S_IRUGO, vexpress_hwmon_u64_show, NULL, 1); -static VEXPRESS_HWMON_ATTRS(energy, energy1_label, energy1_input); +static struct attribute *vexpress_hwmon_attrs_energy[] = { + &dev_attr_energy1_label.attr, + &sensor_dev_attr_energy1_input.dev_attr.attr, + NULL +}; static struct attribute_group vexpress_hwmon_group_energy = { .is_visible = vexpress_hwmon_attr_is_visible, .attrs = vexpress_hwmon_attrs_energy, @@ -218,7 +219,6 @@ MODULE_DEVICE_TABLE(of, vexpress_hwmon_of_match); static int vexpress_hwmon_probe(struct platform_device *pdev) { - int err; const struct of_device_id *match; struct vexpress_hwmon_data *data; const struct vexpress_hwmon_type *type; @@ -232,45 +232,19 @@ static int vexpress_hwmon_probe(struct platform_device *pdev) if (!match) return -ENODEV; type = match->data; - data->name = type->name; data->reg = devm_regmap_init_vexpress_config(&pdev->dev); if (IS_ERR(data->reg)) return PTR_ERR(data->reg); - err = sysfs_create_groups(&pdev->dev.kobj, type->attr_groups); - if (err) - goto error; - - data->hwmon_dev = hwmon_device_register(&pdev->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto error; - } - - return 0; - -error: - sysfs_remove_group(&pdev->dev.kobj, match->data); - return err; -} - -static int vexpress_hwmon_remove(struct platform_device *pdev) -{ - struct vexpress_hwmon_data *data = platform_get_drvdata(pdev); - const struct of_device_id *match; - - hwmon_device_unregister(data->hwmon_dev); - - match = of_match_device(vexpress_hwmon_of_match, &pdev->dev); - sysfs_remove_group(&pdev->dev.kobj, match->data); + data->hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, + type->name, data, type->attr_groups); - return 0; + return PTR_ERR_OR_ZERO(data->hwmon_dev); } static struct platform_driver vexpress_hwmon_driver = { .probe = vexpress_hwmon_probe, - .remove = vexpress_hwmon_remove, .driver = { .name = DRVNAME, .owner = THIS_MODULE, |