From f809621e8c36c15e9caf248dabe441ac25507e62 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 20 Jan 2014 09:25:50 -0800 Subject: [PATCH 01/36] hwmon: (lm95241) Convert to use devm_hwmon_device_register_with_groups Simplify code, reduce code size, and attach hwmon attributes to hwmon device. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95241.c | 91 ++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 61 deletions(-) diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index 4b68fb2a31d7..7d5ce89908c2 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -89,7 +89,7 @@ static const u8 lm95241_reg_address[] = { /* Client data (each client gets its own) */ struct lm95241_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; unsigned long last_updated, interval; /* in jiffies */ char valid; /* zero until following fields are valid */ @@ -113,8 +113,8 @@ static int temp_from_reg_unsigned(u8 val_h, u8 val_l) static struct lm95241_data *lm95241_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95241_data *data = i2c_get_clientdata(client); + struct lm95241_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; mutex_lock(&data->update_lock); @@ -122,7 +122,7 @@ static struct lm95241_data *lm95241_update_device(struct device *dev) !data->valid) { int i; - dev_dbg(&client->dev, "Updating lm95241 data.\n"); + dev_dbg(dev, "Updating lm95241 data.\n"); for (i = 0; i < ARRAY_SIZE(lm95241_reg_address); i++) data->temp[i] = i2c_smbus_read_byte_data(client, @@ -153,8 +153,7 @@ static ssize_t show_input(struct device *dev, struct device_attribute *attr, static ssize_t show_type(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95241_data *data = i2c_get_clientdata(client); + 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"); @@ -163,8 +162,8 @@ static ssize_t show_type(struct device *dev, struct device_attribute *attr, static ssize_t set_type(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95241_data *data = i2c_get_clientdata(client); + 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; @@ -201,8 +200,7 @@ static ssize_t set_type(struct device *dev, struct device_attribute *attr, static ssize_t show_min(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95241_data *data = i2c_get_clientdata(client); + struct lm95241_data *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE - 1, data->config & to_sensor_dev_attr(attr)->index ? @@ -212,8 +210,7 @@ static ssize_t show_min(struct device *dev, struct device_attribute *attr, static ssize_t set_min(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95241_data *data = i2c_get_clientdata(client); + struct lm95241_data *data = dev_get_drvdata(dev); long val; if (kstrtol(buf, 10, &val) < 0) @@ -229,7 +226,8 @@ static ssize_t set_min(struct device *dev, struct device_attribute *attr, data->config &= ~to_sensor_dev_attr(attr)->index; data->valid = 0; - i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, data->config); + i2c_smbus_write_byte_data(data->client, LM95241_REG_RW_CONFIG, + data->config); mutex_unlock(&data->update_lock); @@ -239,8 +237,7 @@ static ssize_t set_min(struct device *dev, struct device_attribute *attr, static ssize_t show_max(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95241_data *data = i2c_get_clientdata(client); + struct lm95241_data *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE - 1, data->config & to_sensor_dev_attr(attr)->index ? @@ -250,8 +247,7 @@ static ssize_t show_max(struct device *dev, struct device_attribute *attr, static ssize_t set_max(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95241_data *data = i2c_get_clientdata(client); + struct lm95241_data *data = dev_get_drvdata(dev); long val; if (kstrtol(buf, 10, &val) < 0) @@ -267,7 +263,8 @@ static ssize_t set_max(struct device *dev, struct device_attribute *attr, data->config &= ~to_sensor_dev_attr(attr)->index; data->valid = 0; - i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, data->config); + i2c_smbus_write_byte_data(data->client, LM95241_REG_RW_CONFIG, + data->config); mutex_unlock(&data->update_lock); @@ -286,8 +283,7 @@ static ssize_t show_interval(struct device *dev, struct device_attribute *attr, static ssize_t set_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95241_data *data = i2c_get_clientdata(client); + struct lm95241_data *data = dev_get_drvdata(dev); unsigned long val; if (kstrtoul(buf, 10, &val) < 0) @@ -316,7 +312,7 @@ static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_max, set_max, static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval, set_interval); -static struct attribute *lm95241_attributes[] = { +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, @@ -329,10 +325,7 @@ static struct attribute *lm95241_attributes[] = { &dev_attr_update_interval.attr, NULL }; - -static const struct attribute_group lm95241_group = { - .attrs = lm95241_attributes, -}; +ATTRIBUTE_GROUPS(lm95241); /* Return 0 if detection is successful, -ENODEV otherwise */ static int lm95241_detect(struct i2c_client *new_client, @@ -366,10 +359,9 @@ static int lm95241_detect(struct i2c_client *new_client, return 0; } -static void lm95241_init_client(struct i2c_client *client) +static void lm95241_init_client(struct i2c_client *client, + struct lm95241_data *data) { - struct lm95241_data *data = i2c_get_clientdata(client); - data->interval = HZ; /* 1 sec default */ data->valid = 0; data->config = CFG_CR0076; @@ -385,49 +377,27 @@ static void lm95241_init_client(struct i2c_client *client) data->model); } -static int lm95241_probe(struct i2c_client *new_client, +static int lm95241_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; struct lm95241_data *data; - int err; + struct device *hwmon_dev; - data = devm_kzalloc(&new_client->dev, sizeof(struct lm95241_data), - GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct lm95241_data), GFP_KERNEL); if (!data) return -ENOMEM; - i2c_set_clientdata(new_client, data); + data->client = client; mutex_init(&data->update_lock); /* Initialize the LM95241 chip */ - lm95241_init_client(new_client); + lm95241_init_client(client, data); - /* Register sysfs hooks */ - err = sysfs_create_group(&new_client->dev.kobj, &lm95241_group); - if (err) - return err; - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - -exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &lm95241_group); - return err; -} - -static int lm95241_remove(struct i2c_client *client) -{ - struct lm95241_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &lm95241_group); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + lm95241_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } /* Driver data (common to all clients) */ @@ -444,7 +414,6 @@ static struct i2c_driver lm95241_driver = { .name = DEVNAME, }, .probe = lm95241_probe, - .remove = lm95241_remove, .id_table = lm95241_id, .detect = lm95241_detect, .address_list = normal_i2c, From 4b49cca36ee9bb8e043cb76e23b1c2864e3bbc86 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 1 Dec 2013 19:33:16 -0800 Subject: [PATCH 02/36] hwmon: (iio_hwmon) Convert to use hwmon_device_register_with_groups Simplify code and create name attribute automatically. Signed-off-by: Guenter Roeck --- drivers/hwmon/iio_hwmon.c | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index 708081b68c6f..9fbb1b1fdff3 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -31,6 +31,7 @@ struct iio_hwmon_state { int num_channels; struct device *hwmon_dev; struct attribute_group attr_group; + const struct attribute_group *groups[2]; struct attribute **attrs; }; @@ -56,19 +57,6 @@ static ssize_t iio_hwmon_read_val(struct device *dev, return sprintf(buf, "%d\n", result); } -static ssize_t show_name(struct device *dev, struct device_attribute *attr, - char *buf) -{ - const char *name = "iio_hwmon"; - - if (dev->of_node && dev->of_node->name) - name = dev->of_node->name; - - return sprintf(buf, "%s\n", name); -} - -static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); - static int iio_hwmon_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -78,6 +66,10 @@ static int iio_hwmon_probe(struct platform_device *pdev) int in_i = 1, temp_i = 1, curr_i = 1; enum iio_chan_type type; struct iio_channel *channels; + const char *name = "iio_hwmon"; + + if (dev->of_node && dev->of_node->name) + name = dev->of_node->name; channels = iio_channel_get_all(dev); if (IS_ERR(channels)) @@ -96,7 +88,7 @@ static int iio_hwmon_probe(struct platform_device *pdev) st->num_channels++; st->attrs = devm_kzalloc(dev, - sizeof(*st->attrs) * (st->num_channels + 2), + sizeof(*st->attrs) * (st->num_channels + 1), GFP_KERNEL); if (st->attrs == NULL) { ret = -ENOMEM; @@ -144,22 +136,18 @@ static int iio_hwmon_probe(struct platform_device *pdev) a->index = i; st->attrs[i] = &a->dev_attr.attr; } - st->attrs[st->num_channels] = &dev_attr_name.attr; - st->attr_group.attrs = st->attrs; - platform_set_drvdata(pdev, st); - ret = sysfs_create_group(&dev->kobj, &st->attr_group); - if (ret < 0) - goto error_release_channels; - st->hwmon_dev = hwmon_device_register(dev); + st->attr_group.attrs = st->attrs; + st->groups[0] = &st->attr_group; + st->hwmon_dev = hwmon_device_register_with_groups(dev, name, st, + st->groups); if (IS_ERR(st->hwmon_dev)) { ret = PTR_ERR(st->hwmon_dev); - goto error_remove_group; + goto error_release_channels; } + platform_set_drvdata(pdev, st); return 0; -error_remove_group: - sysfs_remove_group(&dev->kobj, &st->attr_group); error_release_channels: iio_channel_release_all(channels); return ret; @@ -170,7 +158,6 @@ static int iio_hwmon_remove(struct platform_device *pdev) struct iio_hwmon_state *st = platform_get_drvdata(pdev); hwmon_device_unregister(st->hwmon_dev); - sysfs_remove_group(&pdev->dev.kobj, &st->attr_group); iio_channel_release_all(st->channels); return 0; From 79ffe8594f23475d83112cd5bd68805f91e49170 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 13 Jan 2014 21:11:56 -0800 Subject: [PATCH 03/36] hwmon: Driver for Linear Technologies LTC4222 Driver Linear Technologies LTC4222 Dual Hot Swap Controller The driver currently only supports voltage monitoring, not voltage control. Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 12 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/ltc4222.c | 237 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 250 insertions(+) create mode 100644 drivers/hwmon/ltc4222.c diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5ce43d8dfa98..ebf1131d05ea 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -798,6 +798,18 @@ config SENSORS_LTC4215 This driver can also be built as a module. If so, the module will be called ltc4215. +config SENSORS_LTC4222 + tristate "Linear Technology LTC4222" + depends on I2C + select REGMAP_I2C + default n + help + If you say yes here you get support for Linear Technology LTC4222 + Dual Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4222. + config SENSORS_LTC4245 tristate "Linear Technology LTC4245" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index ec7cde06eb52..f2a4048fe6ca 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -97,6 +97,7 @@ obj-$(CONFIG_SENSORS_LM95241) += lm95241.o obj-$(CONFIG_SENSORS_LM95245) += lm95245.o obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o +obj-$(CONFIG_SENSORS_LTC4222) += ltc4222.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o diff --git a/drivers/hwmon/ltc4222.c b/drivers/hwmon/ltc4222.c new file mode 100644 index 000000000000..07c25653659f --- /dev/null +++ b/drivers/hwmon/ltc4222.c @@ -0,0 +1,237 @@ +/* + * Driver for Linear Technology LTC4222 Dual Hot Swap controller + * + * Copyright (c) 2014 Guenter Roeck + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* chip registers */ + +#define LTC4222_CONTROL1 0xd0 +#define LTC4222_ALERT1 0xd1 +#define LTC4222_STATUS1 0xd2 +#define LTC4222_FAULT1 0xd3 +#define LTC4222_CONTROL2 0xd4 +#define LTC4222_ALERT2 0xd5 +#define LTC4222_STATUS2 0xd6 +#define LTC4222_FAULT2 0xd7 +#define LTC4222_SOURCE1 0xd8 +#define LTC4222_SOURCE2 0xda +#define LTC4222_ADIN1 0xdc +#define LTC4222_ADIN2 0xde +#define LTC4222_SENSE1 0xe0 +#define LTC4222_SENSE2 0xe2 +#define LTC4222_ADC_CONTROL 0xe4 + +/* + * Fault register bits + */ +#define FAULT_OV BIT(0) +#define FAULT_UV BIT(1) +#define FAULT_OC BIT(2) +#define FAULT_POWER_BAD BIT(3) +#define FAULT_FET_BAD BIT(5) + +/* Return the voltage from the given register in mV or mA */ +static int ltc4222_get_value(struct device *dev, u8 reg) +{ + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int val; + u8 buf[2]; + int ret; + + ret = regmap_bulk_read(regmap, reg, buf, 2); + if (ret < 0) + return ret; + + val = ((buf[0] << 8) + buf[1]) >> 6; + + switch (reg) { + case LTC4222_ADIN1: + case LTC4222_ADIN2: + /* 1.25 mV resolution. Convert to mV. */ + val = DIV_ROUND_CLOSEST(val * 5, 4); + break; + case LTC4222_SOURCE1: + case LTC4222_SOURCE2: + /* 31.25 mV resolution. Convert to mV. */ + val = DIV_ROUND_CLOSEST(val * 125, 4); + break; + case LTC4222_SENSE1: + case LTC4222_SENSE2: + /* + * 62.5 uV resolution. Convert to current as measured with + * an 1 mOhm sense resistor, in mA. If a different sense + * resistor is installed, calculate the actual current by + * dividing the reported current by the sense resistor value + * in mOhm. + */ + val = DIV_ROUND_CLOSEST(val * 125, 2); + break; + default: + return -EINVAL; + } + return val; +} + +static ssize_t ltc4222_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int value; + + value = ltc4222_get_value(dev, attr->index); + if (value < 0) + return value; + return snprintf(buf, PAGE_SIZE, "%d\n", value); +} + +static ssize_t ltc4222_show_bool(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da); + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int fault; + int ret; + + ret = regmap_read(regmap, attr->nr, &fault); + if (ret < 0) + return ret; + fault &= attr->index; + if (fault) /* Clear reported faults in chip register */ + regmap_update_bits(regmap, attr->nr, attr->index, 0); + + return snprintf(buf, PAGE_SIZE, "%d\n", !!fault); +} + +/* Voltages */ +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4222_show_value, NULL, + LTC4222_SOURCE1); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4222_show_value, NULL, + LTC4222_ADIN1); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc4222_show_value, NULL, + LTC4222_SOURCE2); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc4222_show_value, NULL, + LTC4222_ADIN2); + +/* + * Voltage alarms + * UV/OV faults are associated with the input voltage, and power bad and fet + * faults are associated with the output voltage. + */ +static SENSOR_DEVICE_ATTR_2(in1_min_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT1, FAULT_UV); +static SENSOR_DEVICE_ATTR_2(in1_max_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT1, FAULT_OV); +static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT1, FAULT_POWER_BAD | FAULT_FET_BAD); + +static SENSOR_DEVICE_ATTR_2(in3_min_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT2, FAULT_UV); +static SENSOR_DEVICE_ATTR_2(in3_max_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT2, FAULT_OV); +static SENSOR_DEVICE_ATTR_2(in4_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT2, FAULT_POWER_BAD | FAULT_FET_BAD); + +/* Current (via sense resistor) */ +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4222_show_value, NULL, + LTC4222_SENSE1); +static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc4222_show_value, NULL, + LTC4222_SENSE2); + +/* Overcurrent alarm */ +static SENSOR_DEVICE_ATTR_2(curr1_max_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT1, FAULT_OC); +static SENSOR_DEVICE_ATTR_2(curr2_max_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT2, FAULT_OC); + +static struct attribute *ltc4222_attrs[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, + &sensor_dev_attr_in1_max_alarm.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_min_alarm.dev_attr.attr, + &sensor_dev_attr_in3_max_alarm.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + &sensor_dev_attr_curr2_input.dev_attr.attr, + &sensor_dev_attr_curr2_max_alarm.dev_attr.attr, + + NULL, +}; +ATTRIBUTE_GROUPS(ltc4222); + +static struct regmap_config ltc4222_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = LTC4222_ADC_CONTROL, +}; + +static int ltc4222_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, <c4222_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(regmap); + } + + /* Clear faults */ + regmap_write(regmap, LTC4222_FAULT1, 0x00); + regmap_write(regmap, LTC4222_FAULT2, 0x00); + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + regmap, + ltc4222_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id ltc4222_id[] = { + {"ltc4222", 0}, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ltc4222_id); + +static struct i2c_driver ltc4222_driver = { + .driver = { + .name = "ltc4222", + }, + .probe = ltc4222_probe, + .id_table = ltc4222_id, +}; + +module_i2c_driver(ltc4222_driver); + +MODULE_AUTHOR("Guenter Roeck "); +MODULE_DESCRIPTION("LTC4222 driver"); +MODULE_LICENSE("GPL"); From 69e1ded651ee31c8a570d58647902a55806d0db8 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 11 Jan 2014 00:17:05 -0800 Subject: [PATCH 04/36] hwmon: Driver for Linear Technologies LTC4260 LTC4260 is a Positive Voltage Hot Swap Controller. The driver currently only supports voltage monitoring, not voltage control. Signed-off-by: Guenter Roeck --- Documentation/hwmon/ltc4260 | 56 ++++++++++ drivers/hwmon/Kconfig | 12 +++ drivers/hwmon/Makefile | 1 + drivers/hwmon/ltc4260.c | 200 ++++++++++++++++++++++++++++++++++++ 4 files changed, 269 insertions(+) create mode 100644 Documentation/hwmon/ltc4260 create mode 100644 drivers/hwmon/ltc4260.c diff --git a/Documentation/hwmon/ltc4260 b/Documentation/hwmon/ltc4260 new file mode 100644 index 000000000000..c4ff4ad998b2 --- /dev/null +++ b/Documentation/hwmon/ltc4260 @@ -0,0 +1,56 @@ +Kernel driver ltc4260 +===================== + +Supported chips: + * Linear Technology LTC4260 + Prefix: 'ltc4260' + Addresses scanned: - + Datasheet: + http://cds.linear.com/docs/en/datasheet/4260fc.pdf + +Author: Guenter Roeck + + +Description +----------- + +The LTC4260 Hot Swap controller allows a board to be safely inserted +and removed from a live backplane. + + +Usage Notes +----------- + +This driver does not probe for LTC4260 devices, since there is no register +which can be safely used to identify the chip. You will have to instantiate +the devices explicitly. + +Example: the following will load the driver for an LTC4260 at address 0x10 +on I2C bus #1: +$ modprobe ltc4260 +$ echo ltc4260 0x10 > /sys/bus/i2c/devices/i2c-1/new_device + + +Sysfs entries +------------- + +Voltage readings provided by this driver are reported as obtained from the ADC +registers. If a set of voltage divider resistors is installed, calculate the +real voltage by multiplying the reported value with (R1+R2)/R2, where R1 is the +value of the divider resistor against the measured voltage and R2 is the value +of the divider resistor against Ground. + +Current reading provided by this driver is reported as obtained from the ADC +Current Sense register. The reported value assumes that a 1 mOhm sense resistor +is installed. If a different sense resistor is installed, calculate the real +current by dividing the reported value by the sense resistor value in mOhm. + +in1_input SOURCE voltage (mV) +in1_min_alarm Undervoltage alarm +in1_max_alarm Overvoltage alarm + +in2_input ADIN voltage (mV) +in2_alarm Power bad alarm + +curr1_input SENSE current (mA) +curr1_alarm SENSE overcurrent alarm diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index ebf1131d05ea..436c611a40ca 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -821,6 +821,18 @@ config SENSORS_LTC4245 This driver can also be built as a module. If so, the module will be called ltc4245. +config SENSORS_LTC4260 + tristate "Linear Technology LTC4260" + depends on I2C + select REGMAP_I2C + default n + help + If you say yes here you get support for Linear Technology LTC4261 + Positive Voltage Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4260. + config SENSORS_LTC4261 tristate "Linear Technology LTC4261" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index f2a4048fe6ca..cdf325818af7 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -99,6 +99,7 @@ obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4222) += ltc4222.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o +obj-$(CONFIG_SENSORS_LTC4260) += ltc4260.o obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX16065) += max16065.o diff --git a/drivers/hwmon/ltc4260.c b/drivers/hwmon/ltc4260.c new file mode 100644 index 000000000000..453a250d9df5 --- /dev/null +++ b/drivers/hwmon/ltc4260.c @@ -0,0 +1,200 @@ +/* + * Driver for Linear Technology LTC4260 I2C Positive Voltage Hot Swap Controller + * + * Copyright (c) 2014 Guenter Roeck + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* chip registers */ +#define LTC4260_CONTROL 0x00 +#define LTC4260_ALERT 0x01 +#define LTC4260_STATUS 0x02 +#define LTC4260_FAULT 0x03 +#define LTC4260_SENSE 0x04 +#define LTC4260_SOURCE 0x05 +#define LTC4260_ADIN 0x06 + +/* + * Fault register bits + */ +#define FAULT_OV (1 << 0) +#define FAULT_UV (1 << 1) +#define FAULT_OC (1 << 2) +#define FAULT_POWER_BAD (1 << 3) +#define FAULT_FET_SHORT (1 << 5) + +/* Return the voltage from the given register in mV or mA */ +static int ltc4260_get_value(struct device *dev, u8 reg) +{ + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int val; + int ret; + + ret = regmap_read(regmap, reg, &val); + if (ret < 0) + return ret; + + switch (reg) { + case LTC4260_ADIN: + /* 10 mV resolution. Convert to mV. */ + val = val * 10; + break; + case LTC4260_SOURCE: + /* 400 mV resolution. Convert to mV. */ + val = val * 400; + break; + case LTC4260_SENSE: + /* + * 300 uV resolution. Convert to current as measured with + * an 1 mOhm sense resistor, in mA. If a different sense + * resistor is installed, calculate the actual current by + * dividing the reported current by the sense resistor value + * in mOhm. + */ + val = val * 300; + break; + default: + return -EINVAL; + } + + return val; +} + +static ssize_t ltc4260_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int value; + + value = ltc4260_get_value(dev, attr->index); + if (value < 0) + return value; + return snprintf(buf, PAGE_SIZE, "%d\n", value); +} + +static ssize_t ltc4260_show_bool(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int fault; + int ret; + + ret = regmap_read(regmap, LTC4260_FAULT, &fault); + if (ret < 0) + return ret; + + fault &= attr->index; + if (fault) /* Clear reported faults in chip register */ + regmap_update_bits(regmap, LTC4260_FAULT, attr->index, 0); + + return snprintf(buf, PAGE_SIZE, "%d\n", !!fault); +} + +/* Voltages */ +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4260_show_value, NULL, + LTC4260_SOURCE); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4260_show_value, NULL, + LTC4260_ADIN); + +/* + * Voltage alarms + * UV/OV faults are associated with the input voltage, and the POWER BAD and + * FET SHORT faults are associated with the output voltage. + */ +static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ltc4260_show_bool, NULL, + FAULT_UV); +static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ltc4260_show_bool, NULL, + FAULT_OV); +static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, ltc4260_show_bool, NULL, + FAULT_POWER_BAD | FAULT_FET_SHORT); + +/* Current (via sense resistor) */ +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4260_show_value, NULL, + LTC4260_SENSE); + +/* Overcurrent alarm */ +static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc4260_show_bool, NULL, + FAULT_OC); + +static struct attribute *ltc4260_attrs[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, + &sensor_dev_attr_in1_max_alarm.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + + NULL, +}; +ATTRIBUTE_GROUPS(ltc4260); + +static struct regmap_config ltc4260_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = LTC4260_ADIN, +}; + +static int ltc4260_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, <c4260_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(regmap); + } + + /* Clear faults */ + regmap_write(regmap, LTC4260_FAULT, 0x00); + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + regmap, + ltc4260_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id ltc4260_id[] = { + {"ltc4260", 0}, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ltc4260_id); + +static struct i2c_driver ltc4260_driver = { + .driver = { + .name = "ltc4260", + }, + .probe = ltc4260_probe, + .id_table = ltc4260_id, +}; + +module_i2c_driver(ltc4260_driver); + +MODULE_AUTHOR("Guenter Roeck "); +MODULE_DESCRIPTION("LTC4260 driver"); +MODULE_LICENSE("GPL"); From 6700ce035f830149d48c270d84736debfb67179e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 11 Jan 2014 10:28:05 -0800 Subject: [PATCH 05/36] hwmon: Driver for Linear Technologies LTC2945 LTC2945 is a system monitor that measures current, voltage, and power. Signed-off-by: Guenter Roeck --- Documentation/hwmon/ltc2945 | 84 ++++++ drivers/hwmon/Kconfig | 12 + drivers/hwmon/Makefile | 1 + drivers/hwmon/ltc2945.c | 519 ++++++++++++++++++++++++++++++++++++ 4 files changed, 616 insertions(+) create mode 100644 Documentation/hwmon/ltc2945 create mode 100644 drivers/hwmon/ltc2945.c diff --git a/Documentation/hwmon/ltc2945 b/Documentation/hwmon/ltc2945 new file mode 100644 index 000000000000..f8d0f7f19adb --- /dev/null +++ b/Documentation/hwmon/ltc2945 @@ -0,0 +1,84 @@ +Kernel driver ltc2945 +===================== + +Supported chips: + * Linear Technology LTC2945 + Prefix: 'ltc2945' + Addresses scanned: - + Datasheet: + http://cds.linear.com/docs/en/datasheet/2945fa.pdf + +Author: Guenter Roeck + + +Description +----------- + +The LTC2945 is a rail-to-rail system monitor that measures current, voltage, +and power consumption. + + +Usage Notes +----------- + +This driver does not probe for LTC2945 devices, since there is no register +which can be safely used to identify the chip. You will have to instantiate +the devices explicitly. + +Example: the following will load the driver for an LTC2945 at address 0x10 +on I2C bus #1: +$ modprobe ltc2945 +$ echo ltc2945 0x10 > /sys/bus/i2c/devices/i2c-1/new_device + + +Sysfs entries +------------- + +Voltage readings provided by this driver are reported as obtained from the ADC +registers. If a set of voltage divider resistors is installed, calculate the +real voltage by multiplying the reported value with (R1+R2)/R2, where R1 is the +value of the divider resistor against the measured voltage and R2 is the value +of the divider resistor against Ground. + +Current reading provided by this driver is reported as obtained from the ADC +Current Sense register. The reported value assumes that a 1 mOhm sense resistor +is installed. If a different sense resistor is installed, calculate the real +current by dividing the reported value by the sense resistor value in mOhm. + +in1_input VIN voltage (mV). Voltage is measured either at + SENSE+ or VDD pin depending on chip configuration. +in1_min Undervoltage threshold +in1_max Overvoltage threshold +in1_lowest Lowest measured voltage +in1_highest Highest measured voltage +in1_reset_history Write 1 to reset in1 history +in1_min_alarm Undervoltage alarm +in1_max_alarm Overvoltage alarm + +in2_input ADIN voltage (mV) +in2_min Undervoltage threshold +in2_max Overvoltage threshold +in2_lowest Lowest measured voltage +in2_highest Highest measured voltage +in2_reset_history Write 1 to reset in2 history +in2_min_alarm Undervoltage alarm +in2_max_alarm Overvoltage alarm + +curr1_input SENSE current (mA) +curr1_min Undercurrent threshold +curr1_max Overcurrent threshold +curr1_lowest Lowest measured current +curr1_highest Highest measured current +curr1_reset_history Write 1 to reset curr1 history +curr1_min_alarm Undercurrent alarm +curr1_max_alarm Overcurrent alarm + +power1_input Power (in uW). Power is calculated based on SENSE+/VDD + voltage or ADIN voltage depending on chip configuration. +power1_min Low lower threshold +power1_max High power threshold +power1_input_lowest Historical minimum power use +power1_input_highest Historical maximum power use +power1_reset_history Write 1 to reset power1 history +power1_min_alarm Low power alarm +power1_max_alarm High power alarm diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 436c611a40ca..5bbc55b2ea1a 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -776,6 +776,18 @@ config SENSORS_LM93 This driver can also be built as a module. If so, the module will be called lm93. +config SENSORS_LTC2945 + tristate "Linear Technology LTC2945" + depends on I2C + select REGMAP_I2C + default n + help + If you say yes here you get support for Linear Technology LTC2945 + I2C System Monitor. + + This driver can also be built as a module. If so, the module will + be called ltc2945. + config SENSORS_LTC4151 tristate "Linear Technology LTC4151" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index cdf325818af7..355200bf5673 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -95,6 +95,7 @@ obj-$(CONFIG_SENSORS_LM93) += lm93.o obj-$(CONFIG_SENSORS_LM95234) += lm95234.o obj-$(CONFIG_SENSORS_LM95241) += lm95241.o obj-$(CONFIG_SENSORS_LM95245) += lm95245.o +obj-$(CONFIG_SENSORS_LTC2945) += ltc2945.o obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4222) += ltc4222.o diff --git a/drivers/hwmon/ltc2945.c b/drivers/hwmon/ltc2945.c new file mode 100644 index 000000000000..c104cc32989d --- /dev/null +++ b/drivers/hwmon/ltc2945.c @@ -0,0 +1,519 @@ +/* + * Driver for Linear Technology LTC2945 I2C Power Monitor + * + * Copyright (c) 2014 Guenter Roeck + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* chip registers */ +#define LTC2945_CONTROL 0x00 +#define LTC2945_ALERT 0x01 +#define LTC2945_STATUS 0x02 +#define LTC2945_FAULT 0x03 +#define LTC2945_POWER_H 0x05 +#define LTC2945_MAX_POWER_H 0x08 +#define LTC2945_MIN_POWER_H 0x0b +#define LTC2945_MAX_POWER_THRES_H 0x0e +#define LTC2945_MIN_POWER_THRES_H 0x11 +#define LTC2945_SENSE_H 0x14 +#define LTC2945_MAX_SENSE_H 0x16 +#define LTC2945_MIN_SENSE_H 0x18 +#define LTC2945_MAX_SENSE_THRES_H 0x1a +#define LTC2945_MIN_SENSE_THRES_H 0x1c +#define LTC2945_VIN_H 0x1e +#define LTC2945_MAX_VIN_H 0x20 +#define LTC2945_MIN_VIN_H 0x22 +#define LTC2945_MAX_VIN_THRES_H 0x24 +#define LTC2945_MIN_VIN_THRES_H 0x26 +#define LTC2945_ADIN_H 0x28 +#define LTC2945_MAX_ADIN_H 0x2a +#define LTC2945_MIN_ADIN_H 0x2c +#define LTC2945_MAX_ADIN_THRES_H 0x2e +#define LTC2945_MIN_ADIN_THRES_H 0x30 +#define LTC2945_MIN_ADIN_THRES_L 0x31 + +/* Fault register bits */ + +#define FAULT_ADIN_UV (1 << 0) +#define FAULT_ADIN_OV (1 << 1) +#define FAULT_VIN_UV (1 << 2) +#define FAULT_VIN_OV (1 << 3) +#define FAULT_SENSE_UV (1 << 4) +#define FAULT_SENSE_OV (1 << 5) +#define FAULT_POWER_UV (1 << 6) +#define FAULT_POWER_OV (1 << 7) + +/* Control register bits */ + +#define CONTROL_MULT_SELECT (1 << 0) +#define CONTROL_TEST_MODE (1 << 4) + +static inline bool is_power_reg(u8 reg) +{ + return reg < LTC2945_SENSE_H; +} + +/* Return the value from the given register in uW, mV, or mA */ +static long long ltc2945_reg_to_val(struct device *dev, u8 reg) +{ + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int control; + u8 buf[3]; + long long val; + int ret; + + ret = regmap_bulk_read(regmap, reg, buf, + is_power_reg(reg) ? 3 : 2); + if (ret < 0) + return ret; + + if (is_power_reg(reg)) { + /* power */ + val = (buf[0] << 16) + (buf[1] << 8) + buf[2]; + } else { + /* current, voltage */ + val = (buf[0] << 4) + (buf[1] >> 4); + } + + switch (reg) { + case LTC2945_POWER_H: + case LTC2945_MAX_POWER_H: + case LTC2945_MIN_POWER_H: + case LTC2945_MAX_POWER_THRES_H: + case LTC2945_MIN_POWER_THRES_H: + /* + * Convert to uW by assuming current is measured with + * an 1mOhm sense resistor, similar to current + * measurements. + * Control register bit 0 selects if voltage at SENSE+/VDD + * or voltage at ADIN is used to measure power. + */ + ret = regmap_read(regmap, LTC2945_CONTROL, &control); + if (ret < 0) + return ret; + if (control & CONTROL_MULT_SELECT) { + /* 25 mV * 25 uV = 0.625 uV resolution. */ + val *= 625LL; + } else { + /* 0.5 mV * 25 uV = 0.0125 uV resolution. */ + val = (val * 25LL) >> 1; + } + break; + case LTC2945_VIN_H: + case LTC2945_MAX_VIN_H: + case LTC2945_MIN_VIN_H: + case LTC2945_MAX_VIN_THRES_H: + case LTC2945_MIN_VIN_THRES_H: + /* 25 mV resolution. Convert to mV. */ + val *= 25; + break; + case LTC2945_ADIN_H: + case LTC2945_MAX_ADIN_H: + case LTC2945_MIN_ADIN_THRES_H: + case LTC2945_MAX_ADIN_THRES_H: + case LTC2945_MIN_ADIN_H: + /* 0.5mV resolution. Convert to mV. */ + val = val >> 1; + break; + case LTC2945_SENSE_H: + case LTC2945_MAX_SENSE_H: + case LTC2945_MIN_SENSE_H: + case LTC2945_MAX_SENSE_THRES_H: + case LTC2945_MIN_SENSE_THRES_H: + /* + * 25 uV resolution. Convert to current as measured with + * an 1 mOhm sense resistor, in mA. If a different sense + * resistor is installed, calculate the actual current by + * dividing the reported current by the sense resistor value + * in mOhm. + */ + val *= 25; + break; + default: + return -EINVAL; + } + return val; +} + +static int ltc2945_val_to_reg(struct device *dev, u8 reg, + unsigned long val) +{ + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int control; + int ret; + + switch (reg) { + case LTC2945_POWER_H: + case LTC2945_MAX_POWER_H: + case LTC2945_MIN_POWER_H: + case LTC2945_MAX_POWER_THRES_H: + case LTC2945_MIN_POWER_THRES_H: + /* + * Convert to register value by assuming current is measured + * with an 1mOhm sense resistor, similar to current + * measurements. + * Control register bit 0 selects if voltage at SENSE+/VDD + * or voltage at ADIN is used to measure power, which in turn + * determines register calculations. + */ + ret = regmap_read(regmap, LTC2945_CONTROL, &control); + if (ret < 0) + return ret; + if (control & CONTROL_MULT_SELECT) { + /* 25 mV * 25 uV = 0.625 uV resolution. */ + val = DIV_ROUND_CLOSEST(val, 625); + } else { + /* + * 0.5 mV * 25 uV = 0.0125 uV resolution. + * Divide first to avoid overflow; + * accept loss of accuracy. + */ + val = DIV_ROUND_CLOSEST(val, 25) * 2; + } + break; + case LTC2945_VIN_H: + case LTC2945_MAX_VIN_H: + case LTC2945_MIN_VIN_H: + case LTC2945_MAX_VIN_THRES_H: + case LTC2945_MIN_VIN_THRES_H: + /* 25 mV resolution. */ + val /= 25; + break; + case LTC2945_ADIN_H: + case LTC2945_MAX_ADIN_H: + case LTC2945_MIN_ADIN_THRES_H: + case LTC2945_MAX_ADIN_THRES_H: + case LTC2945_MIN_ADIN_H: + /* 0.5mV resolution. */ + val *= 2; + break; + case LTC2945_SENSE_H: + case LTC2945_MAX_SENSE_H: + case LTC2945_MIN_SENSE_H: + case LTC2945_MAX_SENSE_THRES_H: + case LTC2945_MIN_SENSE_THRES_H: + /* + * 25 uV resolution. Convert to current as measured with + * an 1 mOhm sense resistor, in mA. If a different sense + * resistor is installed, calculate the actual current by + * dividing the reported current by the sense resistor value + * in mOhm. + */ + val = DIV_ROUND_CLOSEST(val, 25); + break; + default: + return -EINVAL; + } + return val; +} + +static ssize_t ltc2945_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + long long value; + + value = ltc2945_reg_to_val(dev, attr->index); + if (value < 0) + return value; + return snprintf(buf, PAGE_SIZE, "%lld\n", value); +} + +static ssize_t ltc2945_set_value(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct regmap *regmap = dev_get_drvdata(dev); + u8 reg = attr->index; + unsigned long val; + u8 regbuf[3]; + int num_regs; + int regval; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + /* convert to register value, then clamp and write result */ + regval = ltc2945_val_to_reg(dev, reg, val); + if (is_power_reg(reg)) { + regval = clamp_val(regval, 0, 0xffffff); + regbuf[0] = regval >> 16; + regbuf[1] = (regval >> 8) & 0xff; + regbuf[2] = regval; + num_regs = 3; + } else { + regval = clamp_val(regval, 0, 0xfff) << 4; + regbuf[0] = regval >> 8; + regbuf[1] = regval & 0xff; + num_regs = 2; + } + ret = regmap_bulk_write(regmap, reg, regbuf, num_regs); + return ret < 0 ? ret : count; +} + +static ssize_t ltc2945_reset_history(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct regmap *regmap = dev_get_drvdata(dev); + u8 reg = attr->index; + int num_regs = is_power_reg(reg) ? 3 : 2; + u8 buf_min[3] = { 0xff, 0xff, 0xff }; + u8 buf_max[3] = { 0, 0, 0 }; + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + if (val != 1) + return -EINVAL; + + ret = regmap_update_bits(regmap, LTC2945_CONTROL, CONTROL_TEST_MODE, + CONTROL_TEST_MODE); + + /* Reset minimum */ + ret = regmap_bulk_write(regmap, reg, buf_min, num_regs); + if (ret) + return ret; + + switch (reg) { + case LTC2945_MIN_POWER_H: + reg = LTC2945_MAX_POWER_H; + break; + case LTC2945_MIN_SENSE_H: + reg = LTC2945_MAX_SENSE_H; + break; + case LTC2945_MIN_VIN_H: + reg = LTC2945_MAX_VIN_H; + break; + case LTC2945_MIN_ADIN_H: + reg = LTC2945_MAX_ADIN_H; + break; + default: + BUG(); + break; + } + /* Reset maximum */ + ret = regmap_bulk_write(regmap, reg, buf_max, num_regs); + + /* Try resetting test mode even if there was an error */ + regmap_update_bits(regmap, LTC2945_CONTROL, CONTROL_TEST_MODE, 0); + + return ret ? : count; +} + +static ssize_t ltc2945_show_bool(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int fault; + int ret; + + ret = regmap_read(regmap, LTC2945_FAULT, &fault); + if (ret < 0) + return ret; + + fault &= attr->index; + if (fault) /* Clear reported faults in chip register */ + regmap_update_bits(regmap, LTC2945_FAULT, attr->index, 0); + + return snprintf(buf, PAGE_SIZE, "%d\n", !!fault); +} + +/* Input voltages */ + +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_VIN_H); +static SENSOR_DEVICE_ATTR(in1_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_VIN_THRES_H); +static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_VIN_THRES_H); +static SENSOR_DEVICE_ATTR(in1_lowest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MIN_VIN_H); +static SENSOR_DEVICE_ATTR(in1_highest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MAX_VIN_H); +static SENSOR_DEVICE_ATTR(in1_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_VIN_H); + +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_ADIN_H); +static SENSOR_DEVICE_ATTR(in2_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_ADIN_THRES_H); +static SENSOR_DEVICE_ATTR(in2_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_ADIN_THRES_H); +static SENSOR_DEVICE_ATTR(in2_lowest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MIN_ADIN_H); +static SENSOR_DEVICE_ATTR(in2_highest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MAX_ADIN_H); +static SENSOR_DEVICE_ATTR(in2_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_ADIN_H); + +/* Voltage alarms */ + +static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_VIN_UV); +static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_VIN_OV); +static SENSOR_DEVICE_ATTR(in2_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_ADIN_UV); +static SENSOR_DEVICE_ATTR(in2_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_ADIN_OV); + +/* Currents (via sense resistor) */ + +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_SENSE_H); +static SENSOR_DEVICE_ATTR(curr1_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_SENSE_THRES_H); +static SENSOR_DEVICE_ATTR(curr1_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_SENSE_THRES_H); +static SENSOR_DEVICE_ATTR(curr1_lowest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MIN_SENSE_H); +static SENSOR_DEVICE_ATTR(curr1_highest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MAX_SENSE_H); +static SENSOR_DEVICE_ATTR(curr1_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_SENSE_H); + +/* Current alarms */ + +static SENSOR_DEVICE_ATTR(curr1_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_SENSE_UV); +static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_SENSE_OV); + +/* Power */ + +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_POWER_H); +static SENSOR_DEVICE_ATTR(power1_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_POWER_THRES_H); +static SENSOR_DEVICE_ATTR(power1_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_POWER_THRES_H); +static SENSOR_DEVICE_ATTR(power1_input_lowest, S_IRUGO, ltc2945_show_value, + NULL, LTC2945_MIN_POWER_H); +static SENSOR_DEVICE_ATTR(power1_input_highest, S_IRUGO, ltc2945_show_value, + NULL, LTC2945_MAX_POWER_H); +static SENSOR_DEVICE_ATTR(power1_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_POWER_H); + +/* Power alarms */ + +static SENSOR_DEVICE_ATTR(power1_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_POWER_UV); +static SENSOR_DEVICE_ATTR(power1_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_POWER_OV); + +static struct attribute *ltc2945_attrs[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in1_lowest.dev_attr.attr, + &sensor_dev_attr_in1_highest.dev_attr.attr, + &sensor_dev_attr_in1_reset_history.dev_attr.attr, + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, + &sensor_dev_attr_in1_max_alarm.dev_attr.attr, + + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in2_lowest.dev_attr.attr, + &sensor_dev_attr_in2_highest.dev_attr.attr, + &sensor_dev_attr_in2_reset_history.dev_attr.attr, + &sensor_dev_attr_in2_min_alarm.dev_attr.attr, + &sensor_dev_attr_in2_max_alarm.dev_attr.attr, + + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr1_min.dev_attr.attr, + &sensor_dev_attr_curr1_max.dev_attr.attr, + &sensor_dev_attr_curr1_lowest.dev_attr.attr, + &sensor_dev_attr_curr1_highest.dev_attr.attr, + &sensor_dev_attr_curr1_reset_history.dev_attr.attr, + &sensor_dev_attr_curr1_min_alarm.dev_attr.attr, + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + + &sensor_dev_attr_power1_input.dev_attr.attr, + &sensor_dev_attr_power1_min.dev_attr.attr, + &sensor_dev_attr_power1_max.dev_attr.attr, + &sensor_dev_attr_power1_input_lowest.dev_attr.attr, + &sensor_dev_attr_power1_input_highest.dev_attr.attr, + &sensor_dev_attr_power1_reset_history.dev_attr.attr, + &sensor_dev_attr_power1_min_alarm.dev_attr.attr, + &sensor_dev_attr_power1_max_alarm.dev_attr.attr, + + NULL, +}; +ATTRIBUTE_GROUPS(ltc2945); + +static struct regmap_config ltc2945_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = LTC2945_MIN_ADIN_THRES_L, +}; + +static int ltc2945_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, <c2945_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(regmap); + } + + /* Clear faults */ + regmap_write(regmap, LTC2945_FAULT, 0x00); + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + regmap, + ltc2945_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id ltc2945_id[] = { + {"ltc2945", 0}, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ltc2945_id); + +static struct i2c_driver ltc2945_driver = { + .driver = { + .name = "ltc2945", + }, + .probe = ltc2945_probe, + .id_table = ltc2945_id, +}; + +module_i2c_driver(ltc2945_driver); + +MODULE_AUTHOR("Guenter Roeck "); +MODULE_DESCRIPTION("LTC2945 driver"); +MODULE_LICENSE("GPL"); From 939a0a3fe900125c1e04dc7121444f8f9972892c Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 11 Feb 2014 21:10:50 +0900 Subject: [PATCH 06/36] hwmon: (jz4740) Use devm_ioremap_resource() Use devm_ioremap_resource() in order to make the code simpler, and move 'struct resource *mem' from 'struct jz4740_hwmon' to jz4740_hwmon_probe() because the 'mem' variable is used only in jz4740_hwmon_probe(). Also the redundant return value check of platform_get_resource() is removed, because the value is checked by devm_ioremap_resource(). Signed-off-by: Jingoo Han Acked-by: Lars-Peter Clausen Signed-off-by: Guenter Roeck --- drivers/hwmon/jz4740-hwmon.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/drivers/hwmon/jz4740-hwmon.c b/drivers/hwmon/jz4740-hwmon.c index a183e488db78..7488e36809c8 100644 --- a/drivers/hwmon/jz4740-hwmon.c +++ b/drivers/hwmon/jz4740-hwmon.c @@ -28,7 +28,6 @@ #include struct jz4740_hwmon { - struct resource *mem; void __iomem *base; int irq; @@ -106,6 +105,7 @@ static int jz4740_hwmon_probe(struct platform_device *pdev) { int ret; struct jz4740_hwmon *hwmon; + struct resource *mem; hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL); if (!hwmon) @@ -120,25 +120,10 @@ static int jz4740_hwmon_probe(struct platform_device *pdev) return hwmon->irq; } - hwmon->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!hwmon->mem) { - dev_err(&pdev->dev, "Failed to get platform mmio resource\n"); - return -ENOENT; - } - - hwmon->mem = devm_request_mem_region(&pdev->dev, hwmon->mem->start, - resource_size(hwmon->mem), pdev->name); - if (!hwmon->mem) { - dev_err(&pdev->dev, "Failed to request mmio memory region\n"); - return -EBUSY; - } - - hwmon->base = devm_ioremap_nocache(&pdev->dev, hwmon->mem->start, - resource_size(hwmon->mem)); - if (!hwmon->base) { - dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); - return -EBUSY; - } + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hwmon->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(hwmon->base)) + return PTR_ERR(hwmon->base); init_completion(&hwmon->read_completion); mutex_init(&hwmon->lock); From 2065bdd64cf3e72c8e9095575ba6e2aca98382f5 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 11 Feb 2014 20:46:12 +0800 Subject: [PATCH 07/36] hwmon: (ltc4260) Fix trivial typo Signed-off-by: Axel Lin Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5bbc55b2ea1a..7ed3aa62425c 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -839,7 +839,7 @@ config SENSORS_LTC4260 select REGMAP_I2C default n help - If you say yes here you get support for Linear Technology LTC4261 + If you say yes here you get support for Linear Technology LTC4260 Positive Voltage Hot Swap Controller I2C interface. This driver can also be built as a module. If so, the module will From f44e5c5cf9303daac57be0aa01bc0e8f29f1b9bd Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 11 Feb 2014 21:38:31 -0800 Subject: [PATCH 08/36] hwmon: (smm665): Return error from i2c_smbus_read_word_swapped There is no reason to replace one error code with another, and returning -1 as error code is wrong anyway. Smatch log: drivers/hwmon/smm665.c:225 smm665_read_adc() info: why not propagate 'rv' from i2c_smbus_read_word_swapped() instead of (-1)? Reviewed-by: Jean Delvare Signed-off-by: Guenter Roeck --- drivers/hwmon/smm665.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/smm665.c b/drivers/hwmon/smm665.c index d9e1b7de78da..4ef5802df6d8 100644 --- a/drivers/hwmon/smm665.c +++ b/drivers/hwmon/smm665.c @@ -222,7 +222,7 @@ static int smm665_read_adc(struct smm665_data *data, int adc) rv = i2c_smbus_read_word_swapped(client, 0); if (rv < 0) { dev_dbg(&client->dev, "Failed to read ADC value: error %d", rv); - return -1; + return rv; } /* * Validate/verify readback adc channel (in bit 11..14). From 9bf3babf3f6e70723528bece564c57d38d534652 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 11 Feb 2014 21:34:21 -0800 Subject: [PATCH 09/36] hwmon: (emc2103) Return error from i2c_smbus_read_byte_data There is no reason to replace one error with another. smatch message: drivers/hwmon/emc2103.c:352 set_fan_div() info: why not propagate 'status' from i2c_smbus_read_byte_data() instead of (-5)? Reviewed-by: Jean Delvare Signed-off-by: Guenter Roeck --- drivers/hwmon/emc2103.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/emc2103.c b/drivers/hwmon/emc2103.c index 2c137b26acb4..fd892dd48e4c 100644 --- a/drivers/hwmon/emc2103.c +++ b/drivers/hwmon/emc2103.c @@ -349,7 +349,7 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *da, dev_dbg(&client->dev, "reg 0x%02x, err %d\n", REG_FAN_CONF1, status); mutex_unlock(&data->update_lock); - return -EIO; + return status; } status &= 0x9F; status |= (new_range_bits << 5); From a5589cf193202412d9064863452c6c0b7145353e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 12 Feb 2014 10:04:46 +0100 Subject: [PATCH 10/36] hwmon: (lm95241) Avoid initializing the same field twice data is kzalloc'd, so data->valid and data->model are already 0. Signed-off-by: Jean Delvare Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95241.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index 7d5ce89908c2..cdf19adaec79 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -363,9 +363,7 @@ static void lm95241_init_client(struct i2c_client *client, struct lm95241_data *data) { data->interval = HZ; /* 1 sec default */ - data->valid = 0; data->config = CFG_CR0076; - data->model = 0; data->trutherm = (TT_OFF << TT1_SHIFT) | (TT_OFF << TT2_SHIFT); i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, data->config); From f91d22fc3ebd84423864ede097fb2a27d8808c82 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 12 Feb 2014 10:05:27 +0100 Subject: [PATCH 11/36] hwmon: (lm95245) Avoid initializing the same field twice data is kzalloc'd, so data->valid is already 0. Signed-off-by: Jean Delvare Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95245.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c index a6c85f0ff8f3..3f0956e18a23 100644 --- a/drivers/hwmon/lm95245.c +++ b/drivers/hwmon/lm95245.c @@ -440,7 +440,6 @@ static void lm95245_init_client(struct i2c_client *client) { struct lm95245_data *data = i2c_get_clientdata(client); - data->valid = 0; data->interval = lm95245_read_conversion_rate(client); data->config1 = i2c_smbus_read_byte_data(client, From ebf5e87791a97a7b71d5bd52e0382321a56bb12b Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 14 Feb 2014 10:45:37 +0100 Subject: [PATCH 12/36] hwmon: Sort Kconfig entries alphabetically Entries have been added at more or less random positions over time, so it becomes difficult to find what you are looking for or figure out where to insert new entries. Signed-off-by: Jean Delvare Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 628 +++++++++++++++++++++--------------------- 1 file changed, 314 insertions(+), 314 deletions(-) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 7ed3aa62425c..8087ed5f5cc5 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -111,22 +111,6 @@ config SENSORS_AD7418 This driver can also be built as a module. If so, the module will be called ad7418. -config SENSORS_ADCXX - tristate "National Semiconductor ADCxxxSxxx" - depends on SPI_MASTER - help - If you say yes here you get support for the National Semiconductor - ADCS chip family, where - * bb is the resolution in number of bits (8, 10, 12) - * c is the number of channels (1, 2, 4, 8) - * sss is the maximum conversion speed (021 for 200 kSPS, 051 for 500 - kSPS and 101 for 1 MSPS) - - Examples : ADC081S101, ADC124S501, ... - - This driver can also be built as a module. If so, the module - will be called adcxx. - config SENSORS_ADM1021 tristate "Analog Devices ADM1021 and compatibles" depends on I2C @@ -312,6 +296,31 @@ config SENSORS_FAM15H_POWER This driver can also be built as a module. If so, the module will be called fam15h_power. +config SENSORS_APPLESMC + tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)" + depends on INPUT && X86 + select NEW_LEDS + select LEDS_CLASS + select INPUT_POLLDEV + default n + help + This driver provides support for the Apple System Management + Controller, which provides an accelerometer (Apple Sudden Motion + Sensor), light sensors, temperature sensors, keyboard backlight + control and fan control. + + Only Intel-based Apple's computers are supported (MacBook Pro, + MacBook, MacMini). + + Data from the different sensors, keyboard backlight control and fan + control are accessible via sysfs. + + This driver also provides an absolute input class device, allowing + the laptop to act as a pinball machine-esque joystick. + + Say Y here if you have an applicable laptop and want to experience + the awesome power of applesmc. + config SENSORS_ASB100 tristate "Asus ASB100 Bach" depends on X86 && I2C @@ -435,6 +444,12 @@ config SENSORS_F75375S This driver can also be built as a module. If so, the module will be called f75375s. +config SENSORS_MC13783_ADC + tristate "Freescale MC13783/MC13892 ADC" + depends on MFD_MC13XXX + help + Support for the A/D converter on MC13783 and MC13892 PMIC. + config SENSORS_FSCHMD tristate "Fujitsu Siemens Computers sensor chips" depends on X86 && I2C @@ -451,26 +466,6 @@ config SENSORS_FSCHMD This driver can also be built as a module. If so, the module will be called fschmd. -config SENSORS_G760A - tristate "GMT G760A" - depends on I2C - help - If you say yes here you get support for Global Mixed-mode - Technology Inc G760A fan speed PWM controller chips. - - This driver can also be built as a module. If so, the module - will be called g760a. - -config SENSORS_G762 - tristate "GMT G762 and G763" - depends on I2C - help - If you say yes here you get support for Global Mixed-mode - Technology Inc G762 and G763 fan speed PWM controller chips. - - This driver can also be built as a module. If so, the module - will be called g762. - config SENSORS_GL518SM tristate "Genesys Logic GL518SM" depends on I2C @@ -492,6 +487,26 @@ config SENSORS_GL520SM This driver can also be built as a module. If so, the module will be called gl520sm. +config SENSORS_G760A + tristate "GMT G760A" + depends on I2C + help + If you say yes here you get support for Global Mixed-mode + Technology Inc G760A fan speed PWM controller chips. + + This driver can also be built as a module. If so, the module + will be called g760a. + +config SENSORS_G762 + tristate "GMT G762 and G763" + depends on I2C + help + If you say yes here you get support for Global Mixed-mode + Technology Inc G762 and G763 fan speed PWM controller chips. + + This driver can also be built as a module. If so, the module + will be called g762. + config SENSORS_GPIO_FAN tristate "GPIO fan" depends on GPIOLIB @@ -511,24 +526,6 @@ config SENSORS_HIH6130 This driver can also be built as a module. If so, the module will be called hih6130. -config SENSORS_HTU21 - tristate "Measurement Specialties HTU21D humidity/temperature sensors" - depends on I2C - help - If you say yes here you get support for the Measurement Specialties - HTU21D humidity and temperature sensors. - - This driver can also be built as a module. If so, the module - will be called htu21. - -config SENSORS_CORETEMP - tristate "Intel Core/Core2/Atom temperature sensor" - depends on X86 - help - If you say yes here you get support for the temperature - sensor inside your CPU. Most of the family 6 CPUs - are supported. Check Documentation/hwmon/coretemp for details. - config SENSORS_IBMAEM tristate "IBM Active Energy Manager temperature/power sensors and control" select IPMI_SI @@ -566,6 +563,14 @@ config SENSORS_IIO_HWMON for those channels specified in the map. This map can be provided either via platform data or the device tree bindings. +config SENSORS_CORETEMP + tristate "Intel Core/Core2/Atom temperature sensor" + depends on X86 + help + If you say yes here you get support for the temperature + sensor inside your CPU. Most of the family 6 CPUs + are supported. Check Documentation/hwmon/coretemp for details. + config SENSORS_IT87 tristate "ITE IT87xx and compatibles" depends on !PPC @@ -614,6 +619,219 @@ config SENSORS_LINEAGE This driver can also be built as a module. If so, the module will be called lineage-pem. +config SENSORS_LTC2945 + tristate "Linear Technology LTC2945" + depends on I2C + select REGMAP_I2C + default n + help + If you say yes here you get support for Linear Technology LTC2945 + I2C System Monitor. + + This driver can also be built as a module. If so, the module will + be called ltc2945. + +config SENSORS_LTC4151 + tristate "Linear Technology LTC4151" + depends on I2C + default n + help + If you say yes here you get support for Linear Technology LTC4151 + High Voltage I2C Current and Voltage Monitor interface. + + This driver can also be built as a module. If so, the module will + be called ltc4151. + +config SENSORS_LTC4215 + tristate "Linear Technology LTC4215" + depends on I2C + default n + help + If you say yes here you get support for Linear Technology LTC4215 + Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4215. + +config SENSORS_LTC4222 + tristate "Linear Technology LTC4222" + depends on I2C + select REGMAP_I2C + default n + help + If you say yes here you get support for Linear Technology LTC4222 + Dual Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4222. + +config SENSORS_LTC4245 + tristate "Linear Technology LTC4245" + depends on I2C + default n + help + If you say yes here you get support for Linear Technology LTC4245 + Multiple Supply Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4245. + +config SENSORS_LTC4260 + tristate "Linear Technology LTC4260" + depends on I2C + select REGMAP_I2C + default n + help + If you say yes here you get support for Linear Technology LTC4260 + Positive Voltage Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4260. + +config SENSORS_LTC4261 + tristate "Linear Technology LTC4261" + depends on I2C + default n + help + If you say yes here you get support for Linear Technology LTC4261 + Negative Voltage Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4261. + +config SENSORS_MAX1111 + tristate "Maxim MAX1111 Serial 8-bit ADC chip and compatibles" + depends on SPI_MASTER + help + Say y here to support Maxim's MAX1110, MAX1111, MAX1112, and MAX1113 + ADC chips. + + This driver can also be built as a module. If so, the module + will be called max1111. + +config SENSORS_MAX16065 + tristate "Maxim MAX16065 System Manager and compatibles" + depends on I2C + help + If you say yes here you get support for hardware monitoring + capabilities of the following Maxim System Manager chips. + MAX16065 + MAX16066 + MAX16067 + MAX16068 + MAX16070 + MAX16071 + + This driver can also be built as a module. If so, the module + will be called max16065. + +config SENSORS_MAX1619 + tristate "Maxim MAX1619 sensor chip" + depends on I2C + help + If you say yes here you get support for MAX1619 sensor chip. + + This driver can also be built as a module. If so, the module + will be called max1619. + +config SENSORS_MAX1668 + tristate "Maxim MAX1668 and compatibles" + depends on I2C + help + If you say yes here you get support for MAX1668, MAX1989 and + MAX1805 chips. + + This driver can also be built as a module. If so, the module + will be called max1668. + +config SENSORS_MAX197 + tristate "Maxim MAX197 and compatibles" + help + Support for the Maxim MAX197 A/D converter. + Support will include, but not be limited to, MAX197, and MAX199. + + This driver can also be built as a module. If so, the module + will be called max197. + +config SENSORS_MAX6639 + tristate "Maxim MAX6639 sensor chip" + depends on I2C + help + If you say yes here you get support for the MAX6639 + sensor chips. + + This driver can also be built as a module. If so, the module + will be called max6639. + +config SENSORS_MAX6642 + tristate "Maxim MAX6642 sensor chip" + depends on I2C + help + If you say yes here you get support for MAX6642 sensor chip. + MAX6642 is a SMBus-Compatible Remote/Local Temperature Sensor + with Overtemperature Alarm from Maxim. + + This driver can also be built as a module. If so, the module + will be called max6642. + +config SENSORS_MAX6650 + tristate "Maxim MAX6650 sensor chip" + depends on I2C + help + If you say yes here you get support for the MAX6650 / MAX6651 + sensor chips. + + This driver can also be built as a module. If so, the module + will be called max6650. + +config SENSORS_MAX6697 + tristate "Maxim MAX6697 and compatibles" + depends on I2C + help + If you say yes here you get support for MAX6581, MAX6602, MAX6622, + MAX6636, MAX6689, MAX6693, MAX6694, MAX6697, MAX6698, and MAX6699 + temperature sensor chips. + + This driver can also be built as a module. If so, the module + will be called max6697. + +config SENSORS_HTU21 + tristate "Measurement Specialties HTU21D humidity/temperature sensors" + depends on I2C + help + If you say yes here you get support for the Measurement Specialties + HTU21D humidity and temperature sensors. + + This driver can also be built as a module. If so, the module + will be called htu21. + +config SENSORS_MCP3021 + tristate "Microchip MCP3021 and compatibles" + depends on I2C + help + If you say yes here you get support for MCP3021 and MCP3221. + The MCP3021 is a A/D converter (ADC) with 10-bit and the MCP3221 + with 12-bit resolution. + + This driver can also be built as a module. If so, the module + will be called mcp3021. + +config SENSORS_ADCXX + tristate "National Semiconductor ADCxxxSxxx" + depends on SPI_MASTER + help + If you say yes here you get support for the National Semiconductor + ADCS chip family, where + * bb is the resolution in number of bits (8, 10, 12) + * c is the number of channels (1, 2, 4, 8) + * sss is the maximum conversion speed (021 for 200 kSPS, 051 for 500 + kSPS and 101 for 1 MSPS) + + Examples : ADC081S101, ADC124S501, ... + + This driver can also be built as a module. If so, the module + will be called adcxx. + config SENSORS_LM63 tristate "National Semiconductor LM63 and compatibles" depends on I2C @@ -776,86 +994,6 @@ config SENSORS_LM93 This driver can also be built as a module. If so, the module will be called lm93. -config SENSORS_LTC2945 - tristate "Linear Technology LTC2945" - depends on I2C - select REGMAP_I2C - default n - help - If you say yes here you get support for Linear Technology LTC2945 - I2C System Monitor. - - This driver can also be built as a module. If so, the module will - be called ltc2945. - -config SENSORS_LTC4151 - tristate "Linear Technology LTC4151" - depends on I2C - default n - help - If you say yes here you get support for Linear Technology LTC4151 - High Voltage I2C Current and Voltage Monitor interface. - - This driver can also be built as a module. If so, the module will - be called ltc4151. - -config SENSORS_LTC4215 - tristate "Linear Technology LTC4215" - depends on I2C - default n - help - If you say yes here you get support for Linear Technology LTC4215 - Hot Swap Controller I2C interface. - - This driver can also be built as a module. If so, the module will - be called ltc4215. - -config SENSORS_LTC4222 - tristate "Linear Technology LTC4222" - depends on I2C - select REGMAP_I2C - default n - help - If you say yes here you get support for Linear Technology LTC4222 - Dual Hot Swap Controller I2C interface. - - This driver can also be built as a module. If so, the module will - be called ltc4222. - -config SENSORS_LTC4245 - tristate "Linear Technology LTC4245" - depends on I2C - default n - help - If you say yes here you get support for Linear Technology LTC4245 - Multiple Supply Hot Swap Controller I2C interface. - - This driver can also be built as a module. If so, the module will - be called ltc4245. - -config SENSORS_LTC4260 - tristate "Linear Technology LTC4260" - depends on I2C - select REGMAP_I2C - default n - help - If you say yes here you get support for Linear Technology LTC4260 - Positive Voltage Hot Swap Controller I2C interface. - - This driver can also be built as a module. If so, the module will - be called ltc4260. - -config SENSORS_LTC4261 - tristate "Linear Technology LTC4261" - depends on I2C - default n - help - If you say yes here you get support for Linear Technology LTC4261 - Negative Voltage Hot Swap Controller I2C interface. - - This driver can also be built as a module. If so, the module will - be called ltc4261. - config SENSORS_LM95234 tristate "National Semiconductor LM95234" depends on I2C @@ -885,140 +1023,6 @@ config SENSORS_LM95245 This driver can also be built as a module. If so, the module will be called lm95245. -config SENSORS_MAX1111 - tristate "Maxim MAX1111 Serial 8-bit ADC chip and compatibles" - depends on SPI_MASTER - help - Say y here to support Maxim's MAX1110, MAX1111, MAX1112, and MAX1113 - ADC chips. - - This driver can also be built as a module. If so, the module - will be called max1111. - -config SENSORS_MAX16065 - tristate "Maxim MAX16065 System Manager and compatibles" - depends on I2C - help - If you say yes here you get support for hardware monitoring - capabilities of the following Maxim System Manager chips. - MAX16065 - MAX16066 - MAX16067 - MAX16068 - MAX16070 - MAX16071 - - This driver can also be built as a module. If so, the module - will be called max16065. - -config SENSORS_MAX1619 - tristate "Maxim MAX1619 sensor chip" - depends on I2C - help - If you say yes here you get support for MAX1619 sensor chip. - - This driver can also be built as a module. If so, the module - will be called max1619. - -config SENSORS_MAX1668 - tristate "Maxim MAX1668 and compatibles" - depends on I2C - help - If you say yes here you get support for MAX1668, MAX1989 and - MAX1805 chips. - - This driver can also be built as a module. If so, the module - will be called max1668. - -config SENSORS_MAX197 - tristate "Maxim MAX197 and compatibles" - help - Support for the Maxim MAX197 A/D converter. - Support will include, but not be limited to, MAX197, and MAX199. - - This driver can also be built as a module. If so, the module - will be called max197. - -config SENSORS_MAX6639 - tristate "Maxim MAX6639 sensor chip" - depends on I2C - help - If you say yes here you get support for the MAX6639 - sensor chips. - - This driver can also be built as a module. If so, the module - will be called max6639. - -config SENSORS_MAX6642 - tristate "Maxim MAX6642 sensor chip" - depends on I2C - help - If you say yes here you get support for MAX6642 sensor chip. - MAX6642 is a SMBus-Compatible Remote/Local Temperature Sensor - with Overtemperature Alarm from Maxim. - - This driver can also be built as a module. If so, the module - will be called max6642. - -config SENSORS_MAX6650 - tristate "Maxim MAX6650 sensor chip" - depends on I2C - help - If you say yes here you get support for the MAX6650 / MAX6651 - sensor chips. - - This driver can also be built as a module. If so, the module - will be called max6650. - -config SENSORS_MAX6697 - tristate "Maxim MAX6697 and compatibles" - depends on I2C - help - If you say yes here you get support for MAX6581, MAX6602, MAX6622, - MAX6636, MAX6689, MAX6693, MAX6694, MAX6697, MAX6698, and MAX6699 - temperature sensor chips. - - This driver can also be built as a module. If so, the module - will be called max6697. - -config SENSORS_MCP3021 - tristate "Microchip MCP3021 and compatibles" - depends on I2C - help - If you say yes here you get support for MCP3021 and MCP3221. - The MCP3021 is a A/D converter (ADC) with 10-bit and the MCP3221 - with 12-bit resolution. - - This driver can also be built as a module. If so, the module - will be called mcp3021. - -config SENSORS_NCT6775 - tristate "Nuvoton NCT6775F and compatibles" - depends on !PPC - select HWMON_VID - help - If you say yes here you get support for the hardware monitoring - functionality of the Nuvoton NCT6775F, NCT6776F, NCT6779D - and compatible Super-I/O chips. This driver replaces the - w83627ehf driver for NCT6775F and NCT6776F. - - This driver can also be built as a module. If so, the module - will be called nct6775. - -config SENSORS_NTC_THERMISTOR - tristate "NTC thermistor support" - depends on (!OF && !IIO) || (OF && IIO) - help - This driver supports NTC thermistors sensor reading and its - interpretation. The driver can also monitor the temperature and - send notifications about the temperature. - - Currently, this driver supports - NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, and NCP15WL333. - - This driver can also be built as a module. If so, the module - will be called ntc-thermistor. - config SENSORS_PC87360 tristate "National Semiconductor PC87360 family" depends on !PPC @@ -1047,6 +1051,33 @@ config SENSORS_PC87427 This driver can also be built as a module. If so, the module will be called pc87427. +config SENSORS_NTC_THERMISTOR + tristate "NTC thermistor support" + depends on (!OF && !IIO) || (OF && IIO) + help + This driver supports NTC thermistors sensor reading and its + interpretation. The driver can also monitor the temperature and + send notifications about the temperature. + + Currently, this driver supports + NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, and NCP15WL333. + + This driver can also be built as a module. If so, the module + will be called ntc-thermistor. + +config SENSORS_NCT6775 + tristate "Nuvoton NCT6775F and compatibles" + depends on !PPC + select HWMON_VID + help + If you say yes here you get support for the hardware monitoring + functionality of the Nuvoton NCT6775F, NCT6776F, NCT6779D + and compatible Super-I/O chips. This driver replaces the + w83627ehf driver for NCT6775F and NCT6776F. + + This driver can also be built as a module. If so, the module + will be called nct6775. + config SENSORS_PCF8591 tristate "Philips PCF8591 ADC/DAC" depends on I2C @@ -1110,21 +1141,6 @@ config SENSORS_SIS5595 This driver can also be built as a module. If so, the module will be called sis5595. -config SENSORS_SMM665 - tristate "Summit Microelectronics SMM665" - depends on I2C - default n - help - If you say yes here you get support for the hardware monitoring - features of the Summit Microelectronics SMM665/SMM665B Six-Channel - Active DC Output Controller / Monitor. - - Other supported chips are SMM465, SMM665C, SMM764, and SMM766. - Support for those chips is untested. - - This driver can also be built as a module. If so, the module will - be called smm665. - config SENSORS_DME1737 tristate "SMSC DME1737, SCH311x and compatibles" depends on I2C && !PPC @@ -1246,6 +1262,21 @@ config SENSORS_SCH5636 This driver can also be built as a module. If so, the module will be called sch5636. +config SENSORS_SMM665 + tristate "Summit Microelectronics SMM665" + depends on I2C + default n + help + If you say yes here you get support for the hardware monitoring + features of the Summit Microelectronics SMM665/SMM665B Six-Channel + Active DC Output Controller / Monitor. + + Other supported chips are SMM465, SMM665C, SMM764, and SMM766. + Support for those chips is untested. + + This driver can also be built as a module. If so, the module will + be called smm665. + config SENSORS_ADS1015 tristate "Texas Instruments ADS1015" depends on I2C @@ -1561,37 +1592,6 @@ config SENSORS_ULTRA45 This driver provides support for the Ultra45 workstation environmental sensors. -config SENSORS_APPLESMC - tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)" - depends on INPUT && X86 - select NEW_LEDS - select LEDS_CLASS - select INPUT_POLLDEV - default n - help - This driver provides support for the Apple System Management - Controller, which provides an accelerometer (Apple Sudden Motion - Sensor), light sensors, temperature sensors, keyboard backlight - control and fan control. - - Only Intel-based Apple's computers are supported (MacBook Pro, - MacBook, MacMini). - - Data from the different sensors, keyboard backlight control and fan - control are accessible via sysfs. - - This driver also provides an absolute input class device, allowing - the laptop to act as a pinball machine-esque joystick. - - Say Y here if you have an applicable laptop and want to experience - the awesome power of applesmc. - -config SENSORS_MC13783_ADC - tristate "Freescale MC13783/MC13892 ADC" - depends on MFD_MC13XXX - help - Support for the A/D converter on MC13783 and MC13892 PMIC. - if ACPI comment "ACPI drivers" From b4c9c1a7987ef07d8345bf9d19f36d97423b6b25 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 24 Jan 2014 22:25:33 -0800 Subject: [PATCH 13/36] hwmon: Driver for TI ADC128D818 ADC128D818 is a System Monitor with Temperature Sensor. It is similar to LM80 and LM96080, but has 16 bit wide sensor registers and no fan speed monitoring. Signed-off-by: Guenter Roeck --- Documentation/hwmon/adc128d818 | 47 ++++ drivers/hwmon/Kconfig | 10 + drivers/hwmon/Makefile | 1 + drivers/hwmon/adc128d818.c | 491 +++++++++++++++++++++++++++++++++ 4 files changed, 549 insertions(+) create mode 100644 Documentation/hwmon/adc128d818 create mode 100644 drivers/hwmon/adc128d818.c diff --git a/Documentation/hwmon/adc128d818 b/Documentation/hwmon/adc128d818 new file mode 100644 index 000000000000..39c95004dabc --- /dev/null +++ b/Documentation/hwmon/adc128d818 @@ -0,0 +1,47 @@ +Kernel driver adc128d818 +======================== + +Supported chips: + * Texas Instruments ADC818D818 + Prefix: 'adc818d818' + Addresses scanned: I2C 0x1d, 0x1e, 0x1f, 0x2d, 0x2e, 0x2f + Datasheet: Publicly available at the TI website + http://www.ti.com/ + +Author: Guenter Roeck + +Description +----------- + +This driver implements support for the Texas Instruments ADC128D818. +It is described as 'ADC System Monitor with Temperature Sensor'. + +The ADC128D818 implements one temperature sensor and seven voltage sensors. + +Temperatures are measured in degrees Celsius. There is one set of limits. +When the HOT Temperature Limit is crossed, this will cause an alarm that will +be reasserted until the temperature drops below the HOT Hysteresis. +Measurements are guaranteed between -55 and +125 degrees. The temperature +measurement has a resolution of 0.5 degrees; the limits have a resolution +of 1 degree. + +Voltage sensors (also known as IN sensors) report their values in volts. +An alarm is triggered if the voltage has crossed a programmable minimum +or maximum limit. Note that minimum in this case always means 'closest to +zero'; this is important for negative voltage measurements. All voltage +inputs can measure voltages between 0 and 2.55 volts, with a resolution +of 0.625 mV. + +If an alarm triggers, it will remain triggered until the hardware register +is read at least once. This means that the cause for the alarm may +already have disappeared by the time the alarm is read. The driver +caches the alarm status for each sensor until it is at least reported +once, to ensure that alarms are reported to user space. + +The ADC128D818 only updates its values approximately once per second; +reading it more often will do no harm, but will return 'old' values. + +In addition to the scanned address list, the chip can also be configured for +addresses 0x35 to 0x37. Those addresses are not scanned. You have to instantiate +the driver explicitly if the chip is configured for any of those addresses in +your system. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 8087ed5f5cc5..f288b60a87be 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1277,6 +1277,16 @@ config SENSORS_SMM665 This driver can also be built as a module. If so, the module will be called smm665. +config SENSORS_ADC128D818 + tristate "Texas Instruments ADC128D818" + depends on I2C + help + If you say yes here you get support for the Texas Instruments + ADC128D818 System Monitor with Temperature Sensor chip. + + This driver can also be built as a module. If so, the module + will be called adc128d818. + config SENSORS_ADS1015 tristate "Texas Instruments ADS1015" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 355200bf5673..c48f9873ac73 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o obj-$(CONFIG_SENSORS_AD7314) += ad7314.o obj-$(CONFIG_SENSORS_AD7414) += ad7414.o obj-$(CONFIG_SENSORS_AD7418) += ad7418.o +obj-$(CONFIG_SENSORS_ADC128D818) += adc128d818.o obj-$(CONFIG_SENSORS_ADCXX) += adcxx.o obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o diff --git a/drivers/hwmon/adc128d818.c b/drivers/hwmon/adc128d818.c new file mode 100644 index 000000000000..5ffd81f19d01 --- /dev/null +++ b/drivers/hwmon/adc128d818.c @@ -0,0 +1,491 @@ +/* + * Driver for TI ADC128D818 System Monitor with Temperature Sensor + * + * Copyright (c) 2014 Guenter Roeck + * + * Derived from lm80.c + * Copyright (C) 1998, 1999 Frodo Looijaard + * and Philip Edelbrock + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* Addresses to scan + * The chip also supports addresses 0x35..0x37. Don't scan those addresses + * since they are also used by some EEPROMs, which may result in false + * positives. + */ +static const unsigned short normal_i2c[] = { + 0x1d, 0x1e, 0x1f, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; + +/* registers */ +#define ADC128_REG_IN_MAX(nr) (0x2a + (nr) * 2) +#define ADC128_REG_IN_MIN(nr) (0x2b + (nr) * 2) +#define ADC128_REG_IN(nr) (0x20 + (nr)) + +#define ADC128_REG_TEMP 0x27 +#define ADC128_REG_TEMP_MAX 0x38 +#define ADC128_REG_TEMP_HYST 0x39 + +#define ADC128_REG_CONFIG 0x00 +#define ADC128_REG_ALARM 0x01 +#define ADC128_REG_MASK 0x03 +#define ADC128_REG_CONV_RATE 0x07 +#define ADC128_REG_ONESHOT 0x09 +#define ADC128_REG_SHUTDOWN 0x0a +#define ADC128_REG_CONFIG_ADV 0x0b +#define ADC128_REG_BUSY_STATUS 0x0c + +#define ADC128_REG_MAN_ID 0x3e +#define ADC128_REG_DEV_ID 0x3f + +struct adc128_data { + struct i2c_client *client; + struct regulator *regulator; + int vref; /* Reference voltage in mV */ + struct mutex update_lock; + bool valid; /* true if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u16 in[3][7]; /* Register value, normalized to 12 bit + * 0: input voltage + * 1: min limit + * 2: max limit + */ + s16 temp[3]; /* Register value, normalized to 9 bit + * 0: sensor 1: limit 2: hyst + */ + u8 alarms; /* alarm register value */ +}; + +static struct adc128_data *adc128_update_device(struct device *dev) +{ + struct adc128_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + struct adc128_data *ret = data; + int i, rv; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + for (i = 0; i < 7; i++) { + rv = i2c_smbus_read_word_swapped(client, + ADC128_REG_IN(i)); + if (rv < 0) + goto abort; + data->in[0][i] = rv >> 4; + + rv = i2c_smbus_read_byte_data(client, + ADC128_REG_IN_MIN(i)); + if (rv < 0) + goto abort; + data->in[1][i] = rv << 4; + + rv = i2c_smbus_read_byte_data(client, + ADC128_REG_IN_MAX(i)); + if (rv < 0) + goto abort; + data->in[2][i] = rv << 4; + } + + rv = i2c_smbus_read_word_swapped(client, ADC128_REG_TEMP); + if (rv < 0) + goto abort; + data->temp[0] = rv >> 7; + + rv = i2c_smbus_read_byte_data(client, ADC128_REG_TEMP_MAX); + if (rv < 0) + goto abort; + data->temp[1] = rv << 1; + + rv = i2c_smbus_read_byte_data(client, ADC128_REG_TEMP_HYST); + if (rv < 0) + goto abort; + data->temp[2] = rv << 1; + + rv = i2c_smbus_read_byte_data(client, ADC128_REG_ALARM); + if (rv < 0) + goto abort; + data->alarms |= rv; + + data->last_updated = jiffies; + data->valid = true; + } + goto done; + +abort: + ret = ERR_PTR(rv); + data->valid = false; +done: + mutex_unlock(&data->update_lock); + return ret; +} + +static ssize_t adc128_show_in(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adc128_data *data = adc128_update_device(dev); + int index = to_sensor_dev_attr_2(attr)->index; + int nr = to_sensor_dev_attr_2(attr)->nr; + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = DIV_ROUND_CLOSEST(data->in[index][nr] * data->vref, 4095); + return sprintf(buf, "%d\n", val); +} + +static ssize_t adc128_set_in(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adc128_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr_2(attr)->index; + int nr = to_sensor_dev_attr_2(attr)->nr; + u8 reg, regval; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + mutex_lock(&data->update_lock); + /* 10 mV LSB on limit registers */ + regval = clamp_val(DIV_ROUND_CLOSEST(val, 10), 0, 255); + data->in[index][nr] = regval << 4; + reg = index == 1 ? ADC128_REG_IN_MIN(nr) : ADC128_REG_IN_MAX(nr); + i2c_smbus_write_byte_data(data->client, reg, regval); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t adc128_show_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adc128_data *data = adc128_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + int temp; + + if (IS_ERR(data)) + return PTR_ERR(data); + + temp = (data->temp[index] << 7) >> 7; /* sign extend */ + return sprintf(buf, "%d\n", temp * 500);/* 0.5 degrees C resolution */ +} + +static ssize_t adc128_set_temp(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adc128_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(attr)->index; + long val; + int err; + s8 regval; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + mutex_lock(&data->update_lock); + regval = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127); + data->temp[index] = regval << 1; + i2c_smbus_write_byte_data(data->client, + index == 1 ? ADC128_REG_TEMP_MAX + : ADC128_REG_TEMP_HYST, + regval); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t adc128_show_alarm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adc128_data *data = adc128_update_device(dev); + int mask = 1 << to_sensor_dev_attr(attr)->index; + u8 alarms; + + if (IS_ERR(data)) + return PTR_ERR(data); + + /* + * Clear an alarm after reporting it to user space. If it is still + * active, the next update sequence will set the alarm bit again. + */ + alarms = data->alarms; + data->alarms &= ~mask; + + return sprintf(buf, "%u\n", !!(alarms & mask)); +} + +static SENSOR_DEVICE_ATTR_2(in0_input, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 0, 0); +static SENSOR_DEVICE_ATTR_2(in0_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 0, 1); +static SENSOR_DEVICE_ATTR_2(in0_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 0, 2); + +static SENSOR_DEVICE_ATTR_2(in1_input, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 1, 0); +static SENSOR_DEVICE_ATTR_2(in1_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 1, 1); +static SENSOR_DEVICE_ATTR_2(in1_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 1, 2); + +static SENSOR_DEVICE_ATTR_2(in2_input, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 2, 0); +static SENSOR_DEVICE_ATTR_2(in2_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 2, 1); +static SENSOR_DEVICE_ATTR_2(in2_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 2, 2); + +static SENSOR_DEVICE_ATTR_2(in3_input, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 3, 0); +static SENSOR_DEVICE_ATTR_2(in3_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 3, 1); +static SENSOR_DEVICE_ATTR_2(in3_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 3, 2); + +static SENSOR_DEVICE_ATTR_2(in4_input, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 4, 0); +static SENSOR_DEVICE_ATTR_2(in4_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 4, 1); +static SENSOR_DEVICE_ATTR_2(in4_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 4, 2); + +static SENSOR_DEVICE_ATTR_2(in5_input, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 5, 0); +static SENSOR_DEVICE_ATTR_2(in5_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 5, 1); +static SENSOR_DEVICE_ATTR_2(in5_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 5, 2); + +static SENSOR_DEVICE_ATTR_2(in6_input, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 6, 0); +static SENSOR_DEVICE_ATTR_2(in6_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 6, 1); +static SENSOR_DEVICE_ATTR_2(in6_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 6, 2); + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adc128_show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, + adc128_show_temp, adc128_set_temp, 1); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, + adc128_show_temp, adc128_set_temp, 2); + +static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, adc128_show_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, adc128_show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, adc128_show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, adc128_show_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, adc128_show_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, adc128_show_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, adc128_show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adc128_show_alarm, NULL, 7); + +static struct attribute *adc128_attrs[] = { + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in6_min.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, + &sensor_dev_attr_in6_max.dev_attr.attr, + &sensor_dev_attr_in0_input.dev_attr.attr, + &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_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_in0_alarm.dev_attr.attr, + &sensor_dev_attr_in1_alarm.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + &sensor_dev_attr_in3_alarm.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + &sensor_dev_attr_in5_alarm.dev_attr.attr, + &sensor_dev_attr_in6_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(adc128); + +static int adc128_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + int man_id, dev_id; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + man_id = i2c_smbus_read_byte_data(client, ADC128_REG_MAN_ID); + dev_id = i2c_smbus_read_byte_data(client, ADC128_REG_DEV_ID); + if (man_id != 0x01 || dev_id != 0x09) + return -ENODEV; + + /* Check unused bits for confirmation */ + if (i2c_smbus_read_byte_data(client, ADC128_REG_CONFIG) & 0xf4) + return -ENODEV; + if (i2c_smbus_read_byte_data(client, ADC128_REG_CONV_RATE) & 0xfe) + return -ENODEV; + if (i2c_smbus_read_byte_data(client, ADC128_REG_ONESHOT) & 0xfe) + return -ENODEV; + if (i2c_smbus_read_byte_data(client, ADC128_REG_SHUTDOWN) & 0xfe) + return -ENODEV; + if (i2c_smbus_read_byte_data(client, ADC128_REG_CONFIG_ADV) & 0xf8) + return -ENODEV; + if (i2c_smbus_read_byte_data(client, ADC128_REG_BUSY_STATUS) & 0xfc) + return -ENODEV; + + strlcpy(info->type, "adc128d818", I2C_NAME_SIZE); + + return 0; +} + +static int adc128_init_client(struct adc128_data *data) +{ + struct i2c_client *client = data->client; + int err; + + /* + * Reset chip to defaults. + * This makes most other initializations unnecessary. + */ + err = i2c_smbus_write_byte_data(client, ADC128_REG_CONFIG, 0x80); + if (err) + return err; + + /* Start monitoring */ + err = i2c_smbus_write_byte_data(client, ADC128_REG_CONFIG, 0x01); + if (err) + return err; + + /* If external vref is selected, configure the chip to use it */ + if (data->regulator) { + err = i2c_smbus_write_byte_data(client, + ADC128_REG_CONFIG_ADV, 0x01); + if (err) + return err; + } + + return 0; +} + +static int adc128_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct regulator *regulator; + struct device *hwmon_dev; + struct adc128_data *data; + int err, vref; + + data = devm_kzalloc(dev, sizeof(struct adc128_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* vref is optional. If specified, is used as chip reference voltage */ + regulator = devm_regulator_get_optional(dev, "vref"); + if (!IS_ERR(regulator)) { + data->regulator = regulator; + err = regulator_enable(regulator); + if (err < 0) + return err; + vref = regulator_get_voltage(regulator); + if (vref < 0) { + err = vref; + goto error; + } + data->vref = DIV_ROUND_CLOSEST(vref, 1000); + } else { + data->vref = 2560; /* 2.56V, in mV */ + } + + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* Initialize the chip */ + err = adc128_init_client(data); + if (err < 0) + goto error; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, adc128_groups); + if (IS_ERR(hwmon_dev)) { + err = PTR_ERR(hwmon_dev); + goto error; + } + + return 0; + +error: + if (data->regulator) + regulator_disable(data->regulator); + return err; +} + +static int adc128_remove(struct i2c_client *client) +{ + struct adc128_data *data = i2c_get_clientdata(client); + + if (data->regulator) + regulator_disable(data->regulator); + + return 0; +} + +static const struct i2c_device_id adc128_id[] = { + { "adc128d818", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adc128_id); + +static struct i2c_driver adc128_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "adc128d818", + }, + .probe = adc128_probe, + .remove = adc128_remove, + .id_table = adc128_id, + .detect = adc128_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(adc128_driver); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("Driver for ADC128D818"); +MODULE_LICENSE("GPL"); From c5a706697f79b2aa5a2000f8024602320e37658f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 15 Feb 2014 17:59:05 -0800 Subject: [PATCH 14/36] hwmon: (max1668) Don't hide return value from i2c_smbus_write_byte_data i2c_smbus_write_byte_data returns a valid error code. Return it to the user instead of replacing it with -EIO. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare --- drivers/hwmon/max1668.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c index 029b65e6c589..bbbe340332b9 100644 --- a/drivers/hwmon/max1668.c +++ b/drivers/hwmon/max1668.c @@ -216,10 +216,11 @@ static ssize_t set_temp_max(struct device *dev, mutex_lock(&data->update_lock); data->temp_max[index] = clamp_val(temp/1000, -128, 127); - if (i2c_smbus_write_byte_data(client, + ret = i2c_smbus_write_byte_data(client, MAX1668_REG_LIMH_WR(index), - data->temp_max[index])) - count = -EIO; + data->temp_max[index]); + if (ret < 0) + count = ret; mutex_unlock(&data->update_lock); return count; @@ -241,10 +242,11 @@ static ssize_t set_temp_min(struct device *dev, mutex_lock(&data->update_lock); data->temp_min[index] = clamp_val(temp/1000, -128, 127); - if (i2c_smbus_write_byte_data(client, + ret = i2c_smbus_write_byte_data(client, MAX1668_REG_LIML_WR(index), - data->temp_min[index])) - count = -EIO; + data->temp_min[index]); + if (ret < 0) + count = ret; mutex_unlock(&data->update_lock); return count; From 5230551a26f9f4ab41929b81bf4de96a37eb0ae5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 15 Feb 2014 17:50:48 -0800 Subject: [PATCH 15/36] hwmon: (max1668) Convert to use devm_hwmon_device_register_with_groups Simplify code, reduce code size, and attach hwmon attributes to hwmon device. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare --- drivers/hwmon/max1668.c | 67 +++++++++++------------------------------ 1 file changed, 18 insertions(+), 49 deletions(-) diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c index bbbe340332b9..e3ed0a5b6d94 100644 --- a/drivers/hwmon/max1668.c +++ b/drivers/hwmon/max1668.c @@ -66,7 +66,8 @@ MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); enum chips { max1668, max1805, max1989 }; struct max1668_data { - struct device *hwmon_dev; + struct i2c_client *client; + const struct attribute_group *groups[3]; enum chips type; struct mutex update_lock; @@ -82,8 +83,8 @@ struct max1668_data { static struct max1668_data *max1668_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct max1668_data *data = i2c_get_clientdata(client); + struct max1668_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; struct max1668_data *ret = data; s32 val; int i; @@ -205,8 +206,8 @@ static ssize_t set_temp_max(struct device *dev, const char *buf, size_t count) { int index = to_sensor_dev_attr(devattr)->index; - struct i2c_client *client = to_i2c_client(dev); - struct max1668_data *data = i2c_get_clientdata(client); + struct max1668_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; long temp; int ret; @@ -231,8 +232,8 @@ static ssize_t set_temp_min(struct device *dev, const char *buf, size_t count) { int index = to_sensor_dev_attr(devattr)->index; - struct i2c_client *client = to_i2c_client(dev); - struct max1668_data *data = i2c_get_clientdata(client); + struct max1668_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; long temp; int ret; @@ -407,60 +408,29 @@ static int max1668_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; + struct device *hwmon_dev; struct max1668_data *data; - int err; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - data = devm_kzalloc(&client->dev, sizeof(struct max1668_data), - GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct max1668_data), GFP_KERNEL); if (!data) return -ENOMEM; - i2c_set_clientdata(client, data); + data->client = client; data->type = id->driver_data; mutex_init(&data->update_lock); - /* Register sysfs hooks */ - err = sysfs_create_group(&client->dev.kobj, &max1668_group_common); - if (err) - return err; - - if (data->type == max1668 || data->type == max1989) { - err = sysfs_create_group(&client->dev.kobj, - &max1668_group_unique); - if (err) - goto error_sysrem0; - } - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto error_sysrem1; - } - - return 0; - -error_sysrem1: + /* sysfs hooks */ + data->groups[0] = &max1668_group_common; if (data->type == max1668 || data->type == max1989) - sysfs_remove_group(&client->dev.kobj, &max1668_group_unique); -error_sysrem0: - sysfs_remove_group(&client->dev.kobj, &max1668_group_common); - return err; -} + data->groups[1] = &max1668_group_unique; -static int max1668_remove(struct i2c_client *client) -{ - struct max1668_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - if (data->type == max1668 || data->type == max1989) - sysfs_remove_group(&client->dev.kobj, &max1668_group_unique); - - sysfs_remove_group(&client->dev.kobj, &max1668_group_common); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, data->groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct i2c_device_id max1668_id[] = { @@ -478,7 +448,6 @@ static struct i2c_driver max1668_driver = { .name = "max1668", }, .probe = max1668_probe, - .remove = max1668_remove, .id_table = max1668_id, .detect = max1668_detect, .address_list = max1668_addr_list, From 94c42cf9a109b70486733669b79a5875f55b6684 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 2 Feb 2014 14:12:40 -0800 Subject: [PATCH 16/36] hwmon: (ltc4245) Use device managed function to register hwmon device This simplifies the code and reduces code size. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare --- drivers/hwmon/ltc4245.c | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c index d4172933ce4f..2c6da5d955f5 100644 --- a/drivers/hwmon/ltc4245.c +++ b/drivers/hwmon/ltc4245.c @@ -512,24 +512,10 @@ static int ltc4245_probe(struct i2c_client *client, /* Add sysfs hooks */ ltc4245_sysfs_add_groups(data); - hwmon_dev = hwmon_device_register_with_groups(&client->dev, - client->name, data, - data->groups); - if (IS_ERR(hwmon_dev)) - return PTR_ERR(hwmon_dev); - - i2c_set_clientdata(client, hwmon_dev); - - return 0; -} - -static int ltc4245_remove(struct i2c_client *client) -{ - struct device *hwmon_dev = i2c_get_clientdata(client); - - hwmon_device_unregister(hwmon_dev); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, + client->name, data, + data->groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct i2c_device_id ltc4245_id[] = { @@ -544,7 +530,6 @@ static struct i2c_driver ltc4245_driver = { .name = "ltc4245", }, .probe = ltc4245_probe, - .remove = ltc4245_remove, .id_table = ltc4245_id, }; From bdd41aae69f99394d4dd2026e5adfa882b748ba1 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 2 Feb 2014 21:32:40 -0800 Subject: [PATCH 17/36] hwmon: (ltc4245) Drop debug messages The debug messages in this driver have little if any value. Drop them. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare --- drivers/hwmon/ltc4245.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c index 2c6da5d955f5..aa36c2332614 100644 --- a/drivers/hwmon/ltc4245.c +++ b/drivers/hwmon/ltc4245.c @@ -95,7 +95,6 @@ static void ltc4245_update_gpios(struct device *dev) * readings as stale by setting them to -EAGAIN */ if (time_after(jiffies, data->last_updated + 5 * HZ)) { - dev_dbg(&client->dev, "Marking GPIOs invalid\n"); for (i = 0; i < ARRAY_SIZE(data->gpios); i++) data->gpios[i] = -EAGAIN; } @@ -141,8 +140,6 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev) if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - dev_dbg(&client->dev, "Starting ltc4245 update\n"); - /* Read control registers -- 0x00 to 0x07 */ for (i = 0; i < ARRAY_SIZE(data->cregs); i++) { val = i2c_smbus_read_byte_data(client, i); From 4fa6cbb1b88b647a37776a50ee31e68c30732345 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 2 Feb 2014 14:14:26 -0800 Subject: [PATCH 18/36] hwmon: (ltc4245) Remove devicetree conditionals Devicetree functions are stubbed out if CONFIG_OF is undefined. Therefore, conditional compilation is unnecessary and can be removed. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare --- drivers/hwmon/ltc4245.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c index aa36c2332614..681b5b7b3c3b 100644 --- a/drivers/hwmon/ltc4245.c +++ b/drivers/hwmon/ltc4245.c @@ -467,19 +467,15 @@ static void ltc4245_sysfs_add_groups(struct ltc4245_data *data) static bool ltc4245_use_extra_gpios(struct i2c_client *client) { struct ltc4245_platform_data *pdata = dev_get_platdata(&client->dev); -#ifdef CONFIG_OF struct device_node *np = client->dev.of_node; -#endif /* prefer platform data */ if (pdata) return pdata->use_extra_gpios; -#ifdef CONFIG_OF /* fallback on OF */ if (of_find_property(np, "ltc4245,use-extra-gpios", NULL)) return true; -#endif return false; } From 1075305de47d8ebf909acd3d52cade78b9e8f160 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 16 Feb 2014 13:23:25 -0800 Subject: [PATCH 19/36] hwmon: (coretemp) Use sysfs_create_group to create sysfs attributes Instead of creating each attribute individually, use sysfs_create_group to create all attributes for one core with a single call. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare Tested-by: Jean Delvare --- drivers/hwmon/coretemp.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index bbb0b0d463f7..3f87db26433d 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -94,6 +94,8 @@ struct temp_data { bool valid; struct sensor_device_attribute sd_attrs[TOTAL_ATTRS]; char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH]; + struct attribute *attrs[TOTAL_ATTRS + 1]; + struct attribute_group attr_group; struct mutex update_lock; }; @@ -406,7 +408,7 @@ static int create_name_attr(struct platform_data *pdata, static int create_core_attrs(struct temp_data *tdata, struct device *dev, int attr_no) { - int err, i; + int i; static ssize_t (*const rd_ptr[TOTAL_ATTRS]) (struct device *dev, struct device_attribute *devattr, char *buf) = { show_label, show_crit_alarm, show_temp, show_tjmax, @@ -424,16 +426,10 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev, tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO; tdata->sd_attrs[i].dev_attr.show = rd_ptr[i]; tdata->sd_attrs[i].index = attr_no; - err = device_create_file(dev, &tdata->sd_attrs[i].dev_attr); - if (err) - goto exit_free; + tdata->attrs[i] = &tdata->sd_attrs[i].dev_attr.attr; } - return 0; - -exit_free: - while (--i >= 0) - device_remove_file(dev, &tdata->sd_attrs[i].dev_attr); - return err; + tdata->attr_group.attrs = tdata->attrs; + return sysfs_create_group(&dev->kobj, &tdata->attr_group); } @@ -575,12 +571,10 @@ static void coretemp_add_core(unsigned int cpu, int pkg_flag) static void coretemp_remove_core(struct platform_data *pdata, struct device *dev, int indx) { - int i; struct temp_data *tdata = pdata->core_data[indx]; /* Remove the sysfs attributes */ - for (i = 0; i < tdata->attr_size; i++) - device_remove_file(dev, &tdata->sd_attrs[i].dev_attr); + sysfs_remove_group(&dev->kobj, &tdata->attr_group); kfree(pdata->core_data[indx]); pdata->core_data[indx] = NULL; From c503a811e44f4a861e3db0540dd7d4f2146a10f2 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 16 Feb 2014 15:29:55 -0800 Subject: [PATCH 20/36] hwmon: (coretemp) Allocate platform data with devm_kzalloc This simplifies error handling. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare Tested-by: Jean Delvare --- drivers/hwmon/coretemp.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 3f87db26433d..944f850d1118 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -582,22 +582,23 @@ static void coretemp_remove_core(struct platform_data *pdata, static int coretemp_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct platform_data *pdata; int err; /* Initialize the per-package data structures */ - pdata = kzalloc(sizeof(struct platform_data), GFP_KERNEL); + pdata = devm_kzalloc(dev, sizeof(struct platform_data), GFP_KERNEL); if (!pdata) return -ENOMEM; - err = create_name_attr(pdata, &pdev->dev); + err = create_name_attr(pdata, dev); if (err) - goto exit_free; + return err; pdata->phys_proc_id = pdev->id; platform_set_drvdata(pdev, pdata); - pdata->hwmon_dev = hwmon_device_register(&pdev->dev); + pdata->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(pdata->hwmon_dev)) { err = PTR_ERR(pdata->hwmon_dev); dev_err(&pdev->dev, "Class registration failed (%d)\n", err); @@ -607,8 +608,6 @@ static int coretemp_probe(struct platform_device *pdev) exit_name: device_remove_file(&pdev->dev, &pdata->name_attr); -exit_free: - kfree(pdata); return err; } @@ -623,7 +622,6 @@ static int coretemp_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &pdata->name_attr); hwmon_device_unregister(pdata->hwmon_dev); - kfree(pdata); return 0; } From d72d19c26c417d514787ae85b197af34de286c0a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 16 Feb 2014 15:49:04 -0800 Subject: [PATCH 21/36] hwmon: (coretemp) Convert to use devm_hwmon_device_register_with_groups Simplify code, reduce code size, and attach sysfs attributes to hwmon device. For this driver, the only attribute created is the name attribute. Other attributes are still created and removed dynamically as cores are added or removed. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare Tested-by: Jean Delvare --- drivers/hwmon/coretemp.c | 47 +++++++--------------------------------- 1 file changed, 8 insertions(+), 39 deletions(-) diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 944f850d1118..f31bc4c48644 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -116,12 +116,6 @@ struct pdev_entry { static LIST_HEAD(pdev_list); static DEFINE_MUTEX(pdev_list_mutex); -static ssize_t show_name(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - return sprintf(buf, "%s\n", DRVNAME); -} - static ssize_t show_label(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -395,16 +389,6 @@ static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) return adjust_tjmax(c, id, dev); } -static int create_name_attr(struct platform_data *pdata, - struct device *dev) -{ - sysfs_attr_init(&pdata->name_attr.attr); - pdata->name_attr.attr.name = "name"; - pdata->name_attr.attr.mode = S_IRUGO; - pdata->name_attr.show = show_name; - return device_create_file(dev, &pdata->name_attr); -} - static int create_core_attrs(struct temp_data *tdata, struct device *dev, int attr_no) { @@ -544,7 +528,7 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu, pdata->core_data[attr_no] = tdata; /* Create sysfs interfaces */ - err = create_core_attrs(tdata, &pdev->dev, attr_no); + err = create_core_attrs(tdata, pdata->hwmon_dev, attr_no); if (err) goto exit_free; @@ -569,12 +553,12 @@ static void coretemp_add_core(unsigned int cpu, int pkg_flag) } static void coretemp_remove_core(struct platform_data *pdata, - struct device *dev, int indx) + int indx) { struct temp_data *tdata = pdata->core_data[indx]; /* Remove the sysfs attributes */ - sysfs_remove_group(&dev->kobj, &tdata->attr_group); + sysfs_remove_group(&pdata->hwmon_dev->kobj, &tdata->attr_group); kfree(pdata->core_data[indx]); pdata->core_data[indx] = NULL; @@ -584,31 +568,18 @@ static int coretemp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct platform_data *pdata; - int err; /* Initialize the per-package data structures */ pdata = devm_kzalloc(dev, sizeof(struct platform_data), GFP_KERNEL); if (!pdata) return -ENOMEM; - err = create_name_attr(pdata, dev); - if (err) - return err; - pdata->phys_proc_id = pdev->id; platform_set_drvdata(pdev, pdata); - pdata->hwmon_dev = hwmon_device_register(dev); - if (IS_ERR(pdata->hwmon_dev)) { - err = PTR_ERR(pdata->hwmon_dev); - dev_err(&pdev->dev, "Class registration failed (%d)\n", err); - goto exit_name; - } - return 0; - -exit_name: - device_remove_file(&pdev->dev, &pdata->name_attr); - return err; + pdata->hwmon_dev = devm_hwmon_device_register_with_groups(dev, DRVNAME, + pdata, NULL); + return PTR_ERR_OR_ZERO(pdata->hwmon_dev); } static int coretemp_remove(struct platform_device *pdev) @@ -618,10 +589,8 @@ static int coretemp_remove(struct platform_device *pdev) for (i = MAX_CORE_DATA - 1; i >= 0; --i) if (pdata->core_data[i]) - coretemp_remove_core(pdata, &pdev->dev, i); + coretemp_remove_core(pdata, i); - device_remove_file(&pdev->dev, &pdata->name_attr); - hwmon_device_unregister(pdata->hwmon_dev); return 0; } @@ -769,7 +738,7 @@ static void put_core_offline(unsigned int cpu) return; if (pdata->core_data[indx] && pdata->core_data[indx]->cpu == cpu) - coretemp_remove_core(pdata, &pdev->dev, indx); + coretemp_remove_core(pdata, indx); /* * If a HT sibling of a core is taken offline, but another HT sibling From 6c588b45b0d4005814cd3188f47db621b929c922 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 15 Feb 2014 22:05:17 -0800 Subject: [PATCH 22/36] hwmon: (ltc4215) Convert to devm_hwmon_device_register_with_groups Simplify code, reduce code size, and attach sysfs attributes to hwmon device. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare --- drivers/hwmon/ltc4215.c | 51 +++++++++++------------------------------ 1 file changed, 13 insertions(+), 38 deletions(-) diff --git a/drivers/hwmon/ltc4215.c b/drivers/hwmon/ltc4215.c index 8a142960d69e..c8a9bd9b050f 100644 --- a/drivers/hwmon/ltc4215.c +++ b/drivers/hwmon/ltc4215.c @@ -33,7 +33,7 @@ enum ltc4215_cmd { }; struct ltc4215_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; bool valid; @@ -45,8 +45,8 @@ struct ltc4215_data { static struct ltc4215_data *ltc4215_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct ltc4215_data *data = i2c_get_clientdata(client); + struct ltc4215_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; s32 val; int i; @@ -214,7 +214,7 @@ static SENSOR_DEVICE_ATTR(in2_min_alarm, S_IRUGO, ltc4215_show_alarm, NULL, * Finally, construct an array of pointers to members of the above objects, * as required for sysfs_create_group() */ -static struct attribute *ltc4215_attributes[] = { +static struct attribute *ltc4215_attrs[] = { &sensor_dev_attr_curr1_input.dev_attr.attr, &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, @@ -229,57 +229,33 @@ static struct attribute *ltc4215_attributes[] = { NULL, }; - -static const struct attribute_group ltc4215_group = { - .attrs = ltc4215_attributes, -}; +ATTRIBUTE_GROUPS(ltc4215); static int ltc4215_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; struct ltc4215_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); /* Initialize the LTC4215 chip */ i2c_smbus_write_byte_data(client, LTC4215_FAULT, 0x00); - /* Register sysfs hooks */ - ret = sysfs_create_group(&client->dev.kobj, <c4215_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, <c4215_group); - return ret; -} - -static int ltc4215_remove(struct i2c_client *client) -{ - struct ltc4215_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, <c4215_group); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + ltc4215_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct i2c_device_id ltc4215_id[] = { @@ -294,7 +270,6 @@ static struct i2c_driver ltc4215_driver = { .name = "ltc4215", }, .probe = ltc4215_probe, - .remove = ltc4215_remove, .id_table = ltc4215_id, }; From 1577f94bcefb0ef88d4fb15313076a45b8bea8a1 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 11 Feb 2014 22:26:12 -0800 Subject: [PATCH 23/36] hwmon: (max6650) Rearrange code to no longer require forward declarations Cleanup only; no functional change. Reviewed-by: Jean Delvare Signed-off-by: Guenter Roeck --- drivers/hwmon/max6650.c | 234 +++++++++++++++++++--------------------- 1 file changed, 112 insertions(+), 122 deletions(-) diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index 0cafc390db4d..26b24f772f5c 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -105,32 +105,6 @@ module_param(clock, int, S_IRUGO); #define DIV_FROM_REG(reg) (1 << (reg & 7)) -static int max6650_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int max6650_init_client(struct i2c_client *client); -static int max6650_remove(struct i2c_client *client); -static struct max6650_data *max6650_update_device(struct device *dev); - -/* - * Driver data (common to all clients) - */ - -static const struct i2c_device_id max6650_id[] = { - { "max6650", 1 }, - { "max6651", 4 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, max6650_id); - -static struct i2c_driver max6650_driver = { - .driver = { - .name = "max6650", - }, - .probe = max6650_probe, - .remove = max6650_remove, - .id_table = max6650_id, -}; - /* * Client data (each client gets its own) */ @@ -151,6 +125,51 @@ struct max6650_data { u8 alarm; }; +static const u8 tach_reg[] = { + MAX6650_REG_TACH0, + MAX6650_REG_TACH1, + MAX6650_REG_TACH2, + MAX6650_REG_TACH3, +}; + +static struct max6650_data *max6650_update_device(struct device *dev) +{ + int i; + struct i2c_client *client = to_i2c_client(dev); + struct max6650_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + data->speed = i2c_smbus_read_byte_data(client, + MAX6650_REG_SPEED); + data->config = i2c_smbus_read_byte_data(client, + MAX6650_REG_CONFIG); + for (i = 0; i < data->nr_fans; i++) { + data->tach[i] = i2c_smbus_read_byte_data(client, + tach_reg[i]); + } + data->count = i2c_smbus_read_byte_data(client, + MAX6650_REG_COUNT); + data->dac = i2c_smbus_read_byte_data(client, MAX6650_REG_DAC); + + /* + * Alarms are cleared on read in case the condition that + * caused the alarm is removed. Keep the value latched here + * for providing the register through different alarm files. + */ + data->alarm |= i2c_smbus_read_byte_data(client, + MAX6650_REG_ALARM); + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + static ssize_t get_fan(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -539,64 +558,6 @@ static const struct attribute_group max6651_attr_grp = { * Real code */ -static int max6650_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct max6650_data *data; - int err; - - data = devm_kzalloc(&client->dev, sizeof(struct max6650_data), - GFP_KERNEL); - if (!data) { - dev_err(&client->dev, "out of memory.\n"); - return -ENOMEM; - } - - i2c_set_clientdata(client, data); - mutex_init(&data->update_lock); - data->nr_fans = id->driver_data; - - /* - * Initialize the max6650 chip - */ - err = max6650_init_client(client); - if (err) - return err; - - err = sysfs_create_group(&client->dev.kobj, &max6650_attr_grp); - if (err) - return err; - /* 3 additional fan inputs for the MAX6651 */ - if (data->nr_fans == 4) { - err = sysfs_create_group(&client->dev.kobj, &max6651_attr_grp); - if (err) - goto err_remove; - } - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (!IS_ERR(data->hwmon_dev)) - return 0; - - err = PTR_ERR(data->hwmon_dev); - dev_err(&client->dev, "error registering hwmon device.\n"); - if (data->nr_fans == 4) - sysfs_remove_group(&client->dev.kobj, &max6651_attr_grp); -err_remove: - sysfs_remove_group(&client->dev.kobj, &max6650_attr_grp); - return err; -} - -static int max6650_remove(struct i2c_client *client) -{ - struct max6650_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - if (data->nr_fans == 4) - sysfs_remove_group(&client->dev.kobj, &max6651_attr_grp); - sysfs_remove_group(&client->dev.kobj, &max6650_attr_grp); - return 0; -} - static int max6650_init_client(struct i2c_client *client) { struct max6650_data *data = i2c_get_clientdata(client); @@ -684,51 +645,80 @@ static int max6650_init_client(struct i2c_client *client) return 0; } -static const u8 tach_reg[] = { - MAX6650_REG_TACH0, - MAX6650_REG_TACH1, - MAX6650_REG_TACH2, - MAX6650_REG_TACH3, -}; - -static struct max6650_data *max6650_update_device(struct device *dev) +static int max6650_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - int i; - struct i2c_client *client = to_i2c_client(dev); - struct max6650_data *data = i2c_get_clientdata(client); + struct max6650_data *data; + int err; - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - data->speed = i2c_smbus_read_byte_data(client, - MAX6650_REG_SPEED); - data->config = i2c_smbus_read_byte_data(client, - MAX6650_REG_CONFIG); - for (i = 0; i < data->nr_fans; i++) { - data->tach[i] = i2c_smbus_read_byte_data(client, - tach_reg[i]); - } - data->count = i2c_smbus_read_byte_data(client, - MAX6650_REG_COUNT); - data->dac = i2c_smbus_read_byte_data(client, MAX6650_REG_DAC); - - /* - * Alarms are cleared on read in case the condition that - * caused the alarm is removed. Keep the value latched here - * for providing the register through different alarm files. - */ - data->alarm |= i2c_smbus_read_byte_data(client, - MAX6650_REG_ALARM); - - data->last_updated = jiffies; - data->valid = 1; + data = devm_kzalloc(&client->dev, sizeof(struct max6650_data), + GFP_KERNEL); + if (!data) { + dev_err(&client->dev, "out of memory.\n"); + return -ENOMEM; } - mutex_unlock(&data->update_lock); + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + data->nr_fans = id->driver_data; - return data; + /* + * Initialize the max6650 chip + */ + err = max6650_init_client(client); + if (err) + return err; + + err = sysfs_create_group(&client->dev.kobj, &max6650_attr_grp); + if (err) + return err; + /* 3 additional fan inputs for the MAX6651 */ + if (data->nr_fans == 4) { + err = sysfs_create_group(&client->dev.kobj, &max6651_attr_grp); + if (err) + goto err_remove; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (!IS_ERR(data->hwmon_dev)) + return 0; + + err = PTR_ERR(data->hwmon_dev); + dev_err(&client->dev, "error registering hwmon device.\n"); + if (data->nr_fans == 4) + sysfs_remove_group(&client->dev.kobj, &max6651_attr_grp); +err_remove: + sysfs_remove_group(&client->dev.kobj, &max6650_attr_grp); + return err; } +static int max6650_remove(struct i2c_client *client) +{ + struct max6650_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + if (data->nr_fans == 4) + sysfs_remove_group(&client->dev.kobj, &max6651_attr_grp); + sysfs_remove_group(&client->dev.kobj, &max6650_attr_grp); + return 0; +} + +static const struct i2c_device_id max6650_id[] = { + { "max6650", 1 }, + { "max6651", 4 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max6650_id); + +static struct i2c_driver max6650_driver = { + .driver = { + .name = "max6650", + }, + .probe = max6650_probe, + .remove = max6650_remove, + .id_table = max6650_id, +}; + module_i2c_driver(max6650_driver); MODULE_AUTHOR("Hans J. Koch"); From 17eaa25c3e39bbd09ec9a032f0e058f34ba70ed2 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 11 Feb 2014 22:37:01 -0800 Subject: [PATCH 24/36] hwmon: (max6650) Convert to use devm_hwmon_device_register_with_groups Simplify code, reduce code size, and attach sensor attributes to hwmon device. Reviewed-by: Jean Delvare Acked-by: Lee Jones Signed-off-by: Guenter Roeck --- drivers/hwmon/max6650.c | 77 +++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 49 deletions(-) diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index 26b24f772f5c..badff94edef8 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -110,7 +110,8 @@ module_param(clock, int, S_IRUGO); */ struct max6650_data { - struct device *hwmon_dev; + struct i2c_client *client; + const struct attribute_group *groups[3]; struct mutex update_lock; int nr_fans; char valid; /* zero until following fields are valid */ @@ -134,9 +135,9 @@ static const u8 tach_reg[] = { static struct max6650_data *max6650_update_device(struct device *dev) { + struct max6650_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; int i; - struct i2c_client *client = to_i2c_client(dev); - struct max6650_data *data = i2c_get_clientdata(client); mutex_lock(&data->update_lock); @@ -254,8 +255,8 @@ static ssize_t get_target(struct device *dev, struct device_attribute *devattr, static ssize_t set_target(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6650_data *data = i2c_get_clientdata(client); + struct max6650_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; int kscale, ktach; unsigned long rpm; int err; @@ -323,8 +324,8 @@ static ssize_t get_pwm(struct device *dev, struct device_attribute *devattr, static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6650_data *data = i2c_get_clientdata(client); + struct max6650_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long pwm; int err; @@ -369,8 +370,8 @@ static ssize_t get_enable(struct device *dev, struct device_attribute *devattr, static ssize_t set_enable(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6650_data *data = i2c_get_clientdata(client); + 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; @@ -419,8 +420,8 @@ static ssize_t get_div(struct device *dev, struct device_attribute *devattr, static ssize_t set_div(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6650_data *data = i2c_get_clientdata(client); + struct max6650_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long div; int err; @@ -465,7 +466,7 @@ static ssize_t get_alarm(struct device *dev, struct device_attribute *devattr, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct max6650_data *data = max6650_update_device(dev); - struct i2c_client *client = to_i2c_client(dev); + struct i2c_client *client = data->client; int alarm = 0; if (data->alarm & attr->index) { @@ -503,7 +504,8 @@ static umode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a, int n) { struct device *dev = container_of(kobj, struct device, kobj); - struct i2c_client *client = to_i2c_client(dev); + struct max6650_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; u8 alarm_en = i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM_EN); struct device_attribute *devattr; @@ -538,7 +540,7 @@ static struct attribute *max6650_attrs[] = { NULL }; -static struct attribute_group max6650_attr_grp = { +static const struct attribute_group max6650_group = { .attrs = max6650_attrs, .is_visible = max6650_attrs_visible, }; @@ -550,7 +552,7 @@ static struct attribute *max6651_attrs[] = { NULL }; -static const struct attribute_group max6651_attr_grp = { +static const struct attribute_group max6651_group = { .attrs = max6651_attrs, }; @@ -558,9 +560,9 @@ static const struct attribute_group max6651_attr_grp = { * Real code */ -static int max6650_init_client(struct i2c_client *client) +static int max6650_init_client(struct max6650_data *data, + struct i2c_client *client) { - struct max6650_data *data = i2c_get_clientdata(client); int config; int err = -EIO; @@ -649,6 +651,7 @@ static int max6650_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct max6650_data *data; + struct device *hwmon_dev; int err; data = devm_kzalloc(&client->dev, sizeof(struct max6650_data), @@ -658,49 +661,26 @@ static int max6650_probe(struct i2c_client *client, return -ENOMEM; } - i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->update_lock); data->nr_fans = id->driver_data; /* * Initialize the max6650 chip */ - err = max6650_init_client(client); + err = max6650_init_client(data, client); if (err) return err; - err = sysfs_create_group(&client->dev.kobj, &max6650_attr_grp); - if (err) - return err; + data->groups[0] = &max6650_group; /* 3 additional fan inputs for the MAX6651 */ - if (data->nr_fans == 4) { - err = sysfs_create_group(&client->dev.kobj, &max6651_attr_grp); - if (err) - goto err_remove; - } - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (!IS_ERR(data->hwmon_dev)) - return 0; - - err = PTR_ERR(data->hwmon_dev); - dev_err(&client->dev, "error registering hwmon device.\n"); if (data->nr_fans == 4) - sysfs_remove_group(&client->dev.kobj, &max6651_attr_grp); -err_remove: - sysfs_remove_group(&client->dev.kobj, &max6650_attr_grp); - return err; -} + data->groups[1] = &max6651_group; -static int max6650_remove(struct i2c_client *client) -{ - struct max6650_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - if (data->nr_fans == 4) - sysfs_remove_group(&client->dev.kobj, &max6651_attr_grp); - sysfs_remove_group(&client->dev.kobj, &max6650_attr_grp); - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, + client->name, data, + data->groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct i2c_device_id max6650_id[] = { @@ -715,7 +695,6 @@ static struct i2c_driver max6650_driver = { .name = "max6650", }, .probe = max6650_probe, - .remove = max6650_remove, .id_table = max6650_id, }; From 0b5e33b6325a9d7e9410feda270020f17cf1507c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 11 Feb 2014 22:40:33 -0800 Subject: [PATCH 25/36] hwmon: (max6650) Drop error message after memory allocation failures The core code already generates an error message. Reviewed-by: Jean Delvare Signed-off-by: Guenter Roeck --- drivers/hwmon/max6650.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index badff94edef8..74916e777264 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -656,10 +656,8 @@ static int max6650_probe(struct i2c_client *client, data = devm_kzalloc(&client->dev, sizeof(struct max6650_data), GFP_KERNEL); - if (!data) { - dev_err(&client->dev, "out of memory.\n"); + if (!data) return -ENOMEM; - } data->client = client; mutex_init(&data->update_lock); From 71ba0f315cc024e3467a49ee63f0b4b4019cdb46 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 11 Feb 2014 22:43:54 -0800 Subject: [PATCH 26/36] hwmon: (max6650) Introduce local 'dev' variable This makes the code a bit easier to read. Reviewed-by: Jean Delvare Signed-off-by: Guenter Roeck --- drivers/hwmon/max6650.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index 74916e777264..162a520f4bd6 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -563,13 +563,14 @@ static const struct attribute_group max6651_group = { static int max6650_init_client(struct max6650_data *data, struct i2c_client *client) { + struct device *dev = &client->dev; int config; int err = -EIO; config = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG); if (config < 0) { - dev_err(&client->dev, "Error reading config, aborting.\n"); + dev_err(dev, "Error reading config, aborting.\n"); return err; } @@ -583,11 +584,11 @@ static int max6650_init_client(struct max6650_data *data, config |= MAX6650_CFG_V12; break; default: - dev_err(&client->dev, "illegal value for fan_voltage (%d)\n", + dev_err(dev, "illegal value for fan_voltage (%d)\n", fan_voltage); } - dev_info(&client->dev, "Fan voltage is set to %dV.\n", + dev_info(dev, "Fan voltage is set to %dV.\n", (config & MAX6650_CFG_V12) ? 12 : 5); switch (prescaler) { @@ -613,11 +614,10 @@ static int max6650_init_client(struct max6650_data *data, | MAX6650_CFG_PRESCALER_16; break; default: - dev_err(&client->dev, "illegal value for prescaler (%d)\n", - prescaler); + dev_err(dev, "illegal value for prescaler (%d)\n", prescaler); } - dev_info(&client->dev, "Prescaler is set to %d.\n", + dev_info(dev, "Prescaler is set to %d.\n", 1 << (config & MAX6650_CFG_PRESCALER_MASK)); /* @@ -627,17 +627,17 @@ static int max6650_init_client(struct max6650_data *data, */ if ((config & MAX6650_CFG_MODE_MASK) == MAX6650_CFG_MODE_OFF) { - dev_dbg(&client->dev, "Change mode to open loop, full off.\n"); + 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(&client->dev, "DAC write error, aborting.\n"); + dev_err(dev, "DAC write error, aborting.\n"); return err; } } if (i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, config)) { - dev_err(&client->dev, "Config write error, aborting.\n"); + dev_err(dev, "Config write error, aborting.\n"); return err; } @@ -650,12 +650,12 @@ static int max6650_init_client(struct max6650_data *data, static int max6650_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; struct max6650_data *data; struct device *hwmon_dev; int err; - data = devm_kzalloc(&client->dev, sizeof(struct max6650_data), - GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct max6650_data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -675,7 +675,7 @@ static int max6650_probe(struct i2c_client *client, if (data->nr_fans == 4) data->groups[1] = &max6651_group; - hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, data, data->groups); return PTR_ERR_OR_ZERO(hwmon_dev); From c1ea0a0431bc63a24bc0a887c94e93c1ecafd668 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 17 Feb 2014 10:21:26 -0800 Subject: [PATCH 27/36] hwmon: (max6639) Introduce local dev variable, and reduce noisiness Introduce local variable 'dev' in probe function to make code easier to read. Also drop dev_info message in probe to reduce noisiness during boot. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare --- drivers/hwmon/max6639.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c index 066e587a18a5..52646bba748b 100644 --- a/drivers/hwmon/max6639.c +++ b/drivers/hwmon/max6639.c @@ -545,11 +545,11 @@ static int max6639_detect(struct i2c_client *client, static int max6639_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; struct max6639_data *data; int err; - data = devm_kzalloc(&client->dev, sizeof(struct max6639_data), - GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct max6639_data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -562,22 +562,20 @@ static int max6639_probe(struct i2c_client *client, return err; /* Register sysfs hooks */ - err = sysfs_create_group(&client->dev.kobj, &max6639_group); + err = sysfs_create_group(&dev->kobj, &max6639_group); if (err) return err; - data->hwmon_dev = hwmon_device_register(&client->dev); + data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); goto error_remove; } - dev_info(&client->dev, "temperature sensor and fan control found\n"); - return 0; error_remove: - sysfs_remove_group(&client->dev.kobj, &max6639_group); + sysfs_remove_group(&dev->kobj, &max6639_group); return err; } From 7981c5846aa168a28b024c2461b766f16dd8f585 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 17 Feb 2014 10:34:29 -0800 Subject: [PATCH 28/36] hwmon: (max6639) Convert to use devm_hwmon_device_register_with_groups Simplify code, reduce code size, and attach sysfs attricutes to hwmon device. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare --- drivers/hwmon/max6639.c | 81 +++++++++++++---------------------------- 1 file changed, 26 insertions(+), 55 deletions(-) diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c index 52646bba748b..0c5bdfeb76bc 100644 --- a/drivers/hwmon/max6639.c +++ b/drivers/hwmon/max6639.c @@ -80,7 +80,7 @@ static const int rpm_ranges[] = { 2000, 4000, 8000, 16000 }; * Client data (each client gets its own) */ struct max6639_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ @@ -104,8 +104,8 @@ struct max6639_data { static struct max6639_data *max6639_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct max6639_data *data = i2c_get_clientdata(client); + struct max6639_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; struct max6639_data *ret = data; int i; int status_reg; @@ -191,9 +191,8 @@ static ssize_t show_temp_fault(struct device *dev, static ssize_t show_temp_max(struct device *dev, struct device_attribute *dev_attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct max6639_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); return sprintf(buf, "%d\n", (data->temp_therm[attr->index] * 1000)); } @@ -202,9 +201,9 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *dev_attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6639_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long val; int res; @@ -224,9 +223,8 @@ static ssize_t set_temp_max(struct device *dev, static ssize_t show_temp_crit(struct device *dev, struct device_attribute *dev_attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct max6639_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); return sprintf(buf, "%d\n", (data->temp_alert[attr->index] * 1000)); } @@ -235,9 +233,9 @@ static ssize_t set_temp_crit(struct device *dev, struct device_attribute *dev_attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6639_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long val; int res; @@ -258,9 +256,8 @@ static ssize_t show_temp_emergency(struct device *dev, struct device_attribute *dev_attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct max6639_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); return sprintf(buf, "%d\n", (data->temp_ot[attr->index] * 1000)); } @@ -269,9 +266,9 @@ static ssize_t set_temp_emergency(struct device *dev, struct device_attribute *dev_attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6639_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long val; int res; @@ -291,9 +288,8 @@ static ssize_t set_temp_emergency(struct device *dev, static ssize_t show_pwm(struct device *dev, struct device_attribute *dev_attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct max6639_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); return sprintf(buf, "%d\n", data->pwm[attr->index] * 255 / 120); } @@ -302,9 +298,9 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *dev_attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6639_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long val; int res; @@ -378,7 +374,7 @@ static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO, show_alarm, NULL, 5); static SENSOR_DEVICE_ATTR(temp2_emergency_alarm, S_IRUGO, show_alarm, NULL, 4); -static struct attribute *max6639_attributes[] = { +static struct attribute *max6639_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp1_fault.dev_attr.attr, @@ -403,10 +399,7 @@ static struct attribute *max6639_attributes[] = { &sensor_dev_attr_temp2_emergency_alarm.dev_attr.attr, NULL }; - -static const struct attribute_group max6639_group = { - .attrs = max6639_attributes, -}; +ATTRIBUTE_GROUPS(max6639); /* * returns respective index in rpm_ranges table @@ -424,9 +417,9 @@ static int rpm_range_to_reg(int range) return 1; /* default: 4000 RPM */ } -static int max6639_init_client(struct i2c_client *client) +static int max6639_init_client(struct i2c_client *client, + struct max6639_data *data) { - struct max6639_data *data = i2c_get_clientdata(client); struct max6639_platform_data *max6639_info = dev_get_platdata(&client->dev); int i; @@ -547,46 +540,25 @@ static int max6639_probe(struct i2c_client *client, { struct device *dev = &client->dev; struct max6639_data *data; + struct device *hwmon_dev; int err; data = devm_kzalloc(dev, sizeof(struct max6639_data), GFP_KERNEL); if (!data) return -ENOMEM; - i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->update_lock); /* Initialize the max6639 chip */ - err = max6639_init_client(client); + err = max6639_init_client(client, data); if (err < 0) return err; - /* Register sysfs hooks */ - err = sysfs_create_group(&dev->kobj, &max6639_group); - if (err) - return err; - - data->hwmon_dev = hwmon_device_register(dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto error_remove; - } - - return 0; - -error_remove: - sysfs_remove_group(&dev->kobj, &max6639_group); - return err; -} - -static int max6639_remove(struct i2c_client *client) -{ - struct max6639_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &max6639_group); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + max6639_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } #ifdef CONFIG_PM_SLEEP @@ -631,7 +603,6 @@ static struct i2c_driver max6639_driver = { .pm = &max6639_pm_ops, }, .probe = max6639_probe, - .remove = max6639_remove, .id_table = max6639_id, .detect = max6639_detect, .address_list = normal_i2c, From 408c15eaca71ab01b7fb30a271687c6cc5dc4d8e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 2 Feb 2014 18:29:28 -0800 Subject: [PATCH 29/36] hwmon: (lm95245) Fix hysteresis temperatures Hysteresis temperatures are defined as absolute temperatures, not as delta value from the critical temperatures. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare --- drivers/hwmon/lm95245.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c index 3f0956e18a23..a400d4c4416b 100644 --- a/drivers/hwmon/lm95245.c +++ b/drivers/hwmon/lm95245.c @@ -272,27 +272,38 @@ static ssize_t set_limit(struct device *dev, struct device_attribute *attr, return count; } +static ssize_t show_crit_hyst(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; + int hyst = data->regs[index] - data->regs[8]; + + return snprintf(buf, PAGE_SIZE - 1, "%d\n", hyst * 1000); +} + static ssize_t set_crit_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct lm95245_data *data = i2c_get_clientdata(client); + int index = to_sensor_dev_attr(attr)->index; unsigned long val; + int hyst, limit; if (kstrtoul(buf, 10, &val) < 0) return -EINVAL; - val /= 1000; - - val = clamp_val(val, 0, 31); - mutex_lock(&data->update_lock); - data->valid = 0; + 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, - val); + hyst); mutex_unlock(&data->update_lock); @@ -378,16 +389,16 @@ static ssize_t set_interval(struct device *dev, struct device_attribute *attr, 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_limit, - set_crit_hyst, 8); +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_IWUSR | S_IRUGO, show_limit, - set_crit_hyst, 8); +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_crit_hyst, + set_crit_hyst, 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, From ce50e7faf115b292725b73d7dfb8188b2d577887 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 22 Feb 2014 07:59:59 -0800 Subject: [PATCH 30/36] hwmon: (lm95245) Drop useless debug message It doesn't provide value to get a log message whenever the driver updates its cached data, so drop the message. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare --- drivers/hwmon/lm95245.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c index a400d4c4416b..07d69dc8d44e 100644 --- a/drivers/hwmon/lm95245.c +++ b/drivers/hwmon/lm95245.c @@ -149,7 +149,6 @@ static struct lm95245_data *lm95245_update_device(struct device *dev) + msecs_to_jiffies(data->interval)) || !data->valid) { int i; - dev_dbg(&client->dev, "Updating lm95245 data.\n"); for (i = 0; i < ARRAY_SIZE(lm95245_reg_address); i++) data->regs[i] = i2c_smbus_read_byte_data(client, From 7276d55e62f79f2b81d2c414e48de43edea5aa78 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 20 Jan 2014 09:17:16 -0800 Subject: [PATCH 31/36] hwmon: (lm95245) Convert to use devm_hwmon_device_register_with_groups Simplify code, reduce code size, and attach hwmon attributes to hwmon device. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare --- drivers/hwmon/lm95245.c | 80 ++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 54 deletions(-) diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c index 07d69dc8d44e..acf36794e7e3 100644 --- a/drivers/hwmon/lm95245.c +++ b/drivers/hwmon/lm95245.c @@ -115,7 +115,7 @@ static const u8 lm95245_reg_address[] = { /* Client data (each client gets its own) */ struct lm95245_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; unsigned long last_updated; /* in jiffies */ unsigned long interval; /* in msecs */ @@ -140,8 +140,8 @@ static int temp_from_reg_signed(u8 val_h, u8 val_l) static struct lm95245_data *lm95245_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95245_data *data = i2c_get_clientdata(client); + struct lm95245_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; mutex_lock(&data->update_lock); @@ -248,9 +248,9 @@ static ssize_t show_limit(struct device *dev, struct device_attribute *attr, static ssize_t set_limit(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95245_data *data = i2c_get_clientdata(client); + 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) @@ -284,9 +284,9 @@ static ssize_t show_crit_hyst(struct device *dev, struct device_attribute *attr, static ssize_t set_crit_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95245_data *data = i2c_get_clientdata(client); + 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; @@ -312,8 +312,7 @@ static ssize_t set_crit_hyst(struct device *dev, struct device_attribute *attr, static ssize_t show_type(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95245_data *data = i2c_get_clientdata(client); + struct lm95245_data *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE - 1, data->config2 & CFG2_REMOTE_TT ? "1\n" : "2\n"); @@ -322,8 +321,8 @@ static ssize_t show_type(struct device *dev, struct device_attribute *attr, static ssize_t set_type(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95245_data *data = i2c_get_clientdata(client); + struct lm95245_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long val; if (kstrtoul(buf, 10, &val) < 0) @@ -369,8 +368,8 @@ static ssize_t show_interval(struct device *dev, struct device_attribute *attr, static ssize_t set_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95245_data *data = i2c_get_clientdata(client); + struct lm95245_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long val; if (kstrtoul(buf, 10, &val) < 0) @@ -408,7 +407,7 @@ static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval, set_interval); -static struct attribute *lm95245_attributes[] = { +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, @@ -422,10 +421,7 @@ static struct attribute *lm95245_attributes[] = { &dev_attr_update_interval.attr, NULL }; - -static const struct attribute_group lm95245_group = { - .attrs = lm95245_attributes, -}; +ATTRIBUTE_GROUPS(lm95245); /* Return 0 if detection is successful, -ENODEV otherwise */ static int lm95245_detect(struct i2c_client *new_client, @@ -446,10 +442,9 @@ static int lm95245_detect(struct i2c_client *new_client, return 0; } -static void lm95245_init_client(struct i2c_client *client) +static void lm95245_init_client(struct i2c_client *client, + struct lm95245_data *data) { - struct lm95245_data *data = i2c_get_clientdata(client); - data->interval = lm95245_read_conversion_rate(client); data->config1 = i2c_smbus_read_byte_data(client, @@ -465,49 +460,27 @@ static void lm95245_init_client(struct i2c_client *client) } } -static int lm95245_probe(struct i2c_client *new_client, +static int lm95245_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; struct lm95245_data *data; - int err; + struct device *hwmon_dev; - data = devm_kzalloc(&new_client->dev, sizeof(struct lm95245_data), - GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct lm95245_data), GFP_KERNEL); if (!data) return -ENOMEM; - i2c_set_clientdata(new_client, data); + data->client = client; mutex_init(&data->update_lock); /* Initialize the LM95245 chip */ - lm95245_init_client(new_client); + lm95245_init_client(client, data); - /* Register sysfs hooks */ - err = sysfs_create_group(&new_client->dev.kobj, &lm95245_group); - if (err) - return err; - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - -exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &lm95245_group); - return err; -} - -static int lm95245_remove(struct i2c_client *client) -{ - struct lm95245_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &lm95245_group); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + lm95245_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } /* Driver data (common to all clients) */ @@ -523,7 +496,6 @@ static struct i2c_driver lm95245_driver = { .name = DEVNAME, }, .probe = lm95245_probe, - .remove = lm95245_remove, .id_table = lm95245_id, .detect = lm95245_detect, .address_list = normal_i2c, From a41a8927e7adfa5de607d5bd43fa8fbbef44d6f2 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 22 Feb 2014 08:17:34 -0800 Subject: [PATCH 32/36] hwmon: (lm95245) Make temp2_crit_hyst read-only The hysteresis register is shared among both temperature sensors. This means changing one also affects the other. To avoid confusion, established way to express this is to make only the first instance writable and keep all other instances as read-only. Otherwise users may be confused that changing the second writable value also affects the first, while it is more obvious that a writable value may affect a different read-only value. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare --- Documentation/hwmon/lm95245 | 8 ++++++-- drivers/hwmon/lm95245.c | 3 +-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Documentation/hwmon/lm95245 b/Documentation/hwmon/lm95245 index cbd8aeab7124..77eaf2812d25 100644 --- a/Documentation/hwmon/lm95245 +++ b/Documentation/hwmon/lm95245 @@ -24,8 +24,12 @@ is given within a range of -127 to +127.875 degrees. Remote temperatures are given within a range of -127 to +255 degrees. Resolution depends on temperature input and range. -Each sensor has its own critical limit, but the hysteresis is common to all -two channels. +Each sensor has its own critical limit. Additionally, there is a relative +hysteresis value common to both critical limits. To make life easier to +user-space applications, two absolute values are exported, one for each +channel, but these values are of course linked. Only the local hysteresis +can be set from user-space, and the same delta applies to the remote +hysteresis. The lm95245 driver can change its update interval to a fixed set of values. It will round up to the next selectable interval. See the datasheet for exact diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c index acf36794e7e3..0ae0dfdafdff 100644 --- a/drivers/hwmon/lm95245.c +++ b/drivers/hwmon/lm95245.c @@ -395,8 +395,7 @@ static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 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_IWUSR | S_IRUGO, show_crit_hyst, - set_crit_hyst, 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, From 768821a302ae98c2d6a570e0ad63780180ba34db Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 27 Feb 2014 20:38:20 +0900 Subject: [PATCH 33/36] hwmon: (max6639) Use SIMPLE_DEV_PM_OPS macro Use SIMPLE_DEV_PM_OPS macro in order to make the code simpler. Signed-off-by: Jingoo Han Acked-by: Roland Stigge Signed-off-by: Guenter Roeck --- drivers/hwmon/max6639.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c index 0c5bdfeb76bc..70650de2cbd1 100644 --- a/drivers/hwmon/max6639.c +++ b/drivers/hwmon/max6639.c @@ -592,9 +592,7 @@ static const struct i2c_device_id max6639_id[] = { MODULE_DEVICE_TABLE(i2c, max6639_id); -static const struct dev_pm_ops max6639_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(max6639_suspend, max6639_resume) -}; +static SIMPLE_DEV_PM_OPS(max6639_pm_ops, max6639_suspend, max6639_resume); static struct i2c_driver max6639_driver = { .class = I2C_CLASS_HWMON, From 648cd48c9e566f53c5df30d79857e0937ae13b09 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 28 Feb 2014 10:37:55 -0800 Subject: [PATCH 34/36] hwmon: Do not accept invalid name attributes hwmon name attributes must not include '-', as specified in Documentation/hwmon/sysfs-interface. Also filter out spaces, tabs, wildcards, and newline characters. Tested-by: Jean Delvare Reviewed-by: Jean Delvare Signed-off-by: Guenter Roeck --- drivers/hwmon/hwmon.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index e176a43af63d..a26c385a435b 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -22,6 +22,7 @@ #include #include #include +#include #define HWMON_ID_PREFIX "hwmon" #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" @@ -99,6 +100,10 @@ hwmon_device_register_with_groups(struct device *dev, const char *name, struct hwmon_device *hwdev; int err, id; + /* Do not accept invalid characters in hwmon name attribute */ + if (name && (!strlen(name) || strpbrk(name, "-* \t\n"))) + return ERR_PTR(-EINVAL); + id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL); if (id < 0) return ERR_PTR(id); From a2e151074366487dcde943fdc08d88acfe476f9d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 5 Mar 2014 09:33:01 -0800 Subject: [PATCH 35/36] hwmon: (pmbus/ltc2978) Add new chip ID for LTC2974 LTC2974 datasheet revision C lists the chip ID for LTC2974 as 0x0213. Reviewed-by: Robert Coulson Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/ltc2978.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index de3c152a1d9a..629bfe0fe90a 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -53,7 +53,8 @@ enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883 }; /* LTC3883 only */ #define LTC3883_MFR_IIN_PEAK 0xe1 -#define LTC2974_ID 0x0212 +#define LTC2974_ID_REV1 0x0212 +#define LTC2974_ID_REV2 0x0213 #define LTC2977_ID 0x0130 #define LTC2978_ID_REV1 0x0121 #define LTC2978_ID_REV2 0x0122 @@ -394,7 +395,7 @@ static int ltc2978_probe(struct i2c_client *client, if (chip_id < 0) return chip_id; - if (chip_id == LTC2974_ID) { + if (chip_id == LTC2974_ID_REV1 || chip_id == LTC2974_ID_REV2) { data->id = ltc2974; } else if (chip_id == LTC2977_ID) { data->id = ltc2977; From f76992b00a4111fc4679b0b2d49fe75f05c2b04d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 5 Mar 2014 21:23:01 -0800 Subject: [PATCH 36/36] hwmon: (pmbus/ltc2978) Add support for LTM4676 The chip's programming interface is quite similar to LTC3880 and supports the same set of sensors. Reviewed-by: Robert Coulson Signed-off-by: Guenter Roeck --- Documentation/hwmon/ltc2978 | 17 +++++++++++------ drivers/hwmon/pmbus/ltc2978.c | 20 +++++++++++--------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/Documentation/hwmon/ltc2978 b/Documentation/hwmon/ltc2978 index a0546fc42273..686c078bb0e0 100644 --- a/Documentation/hwmon/ltc2978 +++ b/Documentation/hwmon/ltc2978 @@ -23,6 +23,10 @@ Supported chips: Prefix: 'ltc3883' Addresses scanned: - Datasheet: http://www.linear.com/product/ltc3883 + * Linear Technology LTM4676 + Prefix: 'ltm4676' + Addresses scanned: - + Datasheet: http://www.linear.com/product/ltm4676 Author: Guenter Roeck @@ -33,7 +37,8 @@ Description LTC2974 is a quad digital power supply manager. LTC2978 is an octal power supply monitor. LTC2977 is a pin compatible replacement for LTC2978. LTC3880 is a dual output poly-phase step-down DC/DC controller. LTC3883 is a single phase -step-down DC/DC controller. +step-down DC/DC controller. LTM4676 is a dual 13A or single 26A uModule +regulator. Usage Notes @@ -75,7 +80,7 @@ in[N]_label "vout[1-8]". LTC2974: N=2-5 LTC2977: N=2-9 LTC2978: N=2-9 - LTC3880: N=2-3 + LTC3880, LTM4676: N=2-3 LTC3883: N=2 in[N]_input Measured output voltage. in[N]_min Minimum output voltage. @@ -95,7 +100,7 @@ temp[N]_input Measured temperature. and temp5 reports the chip temperature. On LTC2977 and LTC2978, only one temperature measurement is supported and reports the chip temperature. - On LTC3880, temp1 and temp2 report external + On LTC3880 and LTM4676, temp1 and temp2 report external temperatures, and temp3 reports the chip temperature. On LTC3883, temp1 reports an external temperature, and temp2 reports the chip temperature. @@ -123,11 +128,11 @@ power[N]_label "pout[1-4]". LTC2974: N=1-4 LTC2977: Not supported LTC2978: Not supported - LTC3880: N=1-2 + LTC3880, LTM4676: N=1-2 LTC3883: N=2 power[N]_input Measured output power. -curr1_label "iin". LTC3880 and LTC3883 only. +curr1_label "iin". LTC3880, LTC3883, and LTM4676 only. curr1_input Measured input current. curr1_max Maximum input current. curr1_max_alarm Input current high alarm. @@ -138,7 +143,7 @@ curr[N]_label "iout[1-4]". LTC2974: N=1-4 LTC2977: not supported LTC2978: not supported - LTC3880: N=2-3 + LTC3880, LTM4676: N=2-3 LTC3883: N=2 curr[N]_input Measured output current. curr[N]_max Maximum output current. diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index 629bfe0fe90a..e24ed521051a 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -1,9 +1,9 @@ /* * Hardware monitoring driver for LTC2974, LTC2977, LTC2978, LTC3880, - * and LTC3883 + * LTC3883, and LTM4676 * * Copyright (c) 2011 Ericsson AB. - * Copyright (c) 2013 Guenter Roeck + * Copyright (c) 2013, 2014 Guenter Roeck * * 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 @@ -14,10 +14,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 @@ -28,7 +24,7 @@ #include #include "pmbus.h" -enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883 }; +enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883, ltm4676 }; /* Common for all chips */ #define LTC2978_MFR_VOUT_PEAK 0xdd @@ -45,7 +41,7 @@ enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883 }; #define LTC2974_MFR_IOUT_PEAK 0xd7 #define LTC2974_MFR_IOUT_MIN 0xd8 -/* LTC3880 and LTC3883 */ +/* LTC3880, LTC3883, and LTM4676 */ #define LTC3880_MFR_IOUT_PEAK 0xd7 #define LTC3880_MFR_CLEAR_PEAKS 0xe3 #define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4 @@ -63,6 +59,8 @@ enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883 }; #define LTC3880_ID_MASK 0xff00 #define LTC3883_ID 0x4300 #define LTC3883_ID_MASK 0xff00 +#define LTM4676_ID 0x4480 /* datasheet claims 0x440X */ +#define LTM4676_ID_MASK 0xfff0 #define LTC2974_NUM_PAGES 4 #define LTC2978_NUM_PAGES 8 @@ -371,6 +369,7 @@ static const struct i2c_device_id ltc2978_id[] = { {"ltc2978", ltc2978}, {"ltc3880", ltc3880}, {"ltc3883", ltc3883}, + {"ltm4676", ltm4676}, {} }; MODULE_DEVICE_TABLE(i2c, ltc2978_id); @@ -406,6 +405,8 @@ static int ltc2978_probe(struct i2c_client *client, data->id = ltc3880; } else if ((chip_id & LTC3883_ID_MASK) == LTC3883_ID) { data->id = ltc3883; + } else if ((chip_id & LTM4676_ID_MASK) == LTM4676_ID) { + data->id = ltm4676; } else { dev_err(&client->dev, "Unsupported chip ID 0x%x\n", chip_id); return -ENODEV; @@ -459,6 +460,7 @@ static int ltc2978_probe(struct i2c_client *client, } break; case ltc3880: + case ltm4676: info->read_word_data = ltc3880_read_word_data; info->pages = LTC3880_NUM_PAGES; info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN @@ -501,5 +503,5 @@ static struct i2c_driver ltc2978_driver = { module_i2c_driver(ltc2978_driver); MODULE_AUTHOR("Guenter Roeck"); -MODULE_DESCRIPTION("PMBus driver for LTC2974, LTC2978, LTC3880, and LTC3883"); +MODULE_DESCRIPTION("PMBus driver for LTC2974, LTC2978, LTC3880, LTC3883, and LTM4676"); MODULE_LICENSE("GPL");