diff options
Diffstat (limited to 'drivers/hwmon/ltc4245.c')
| -rw-r--r-- | drivers/hwmon/ltc4245.c | 433 |
1 files changed, 165 insertions, 268 deletions
diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c index cdc1ecc6734d..e8131a48bda7 100644 --- a/drivers/hwmon/ltc4245.c +++ b/drivers/hwmon/ltc4245.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Driver for Linear Technology LTC4245 I2C Multiple Supply Hot Swap Controller * * Copyright (C) 2008 Ira W. Snyder <iws@ovro.caltech.edu> * - * 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; version 2 of the License. - * * This driver is based on the ds1621 and ina209 drivers. * * Datasheet: @@ -16,13 +13,13 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/bitops.h> #include <linux/err.h> #include <linux/slab.h> #include <linux/i2c.h> #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> #include <linux/jiffies.h> -#include <linux/i2c/ltc4245.h> +#include <linux/platform_data/ltc4245.h> /* Here are names of the chip's registers (a.k.a. commands) */ enum ltc4245_cmd { @@ -51,9 +48,8 @@ enum ltc4245_cmd { }; struct ltc4245_data { - struct device *hwmon_dev; + struct i2c_client *client; - struct mutex update_lock; bool valid; unsigned long last_updated; /* in jiffies */ @@ -77,8 +73,8 @@ struct ltc4245_data { */ static void ltc4245_update_gpios(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct ltc4245_data *data = i2c_get_clientdata(client); + struct ltc4245_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; u8 gpio_curr, gpio_next, gpio_reg; int i; @@ -93,7 +89,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; } @@ -130,17 +125,12 @@ static void ltc4245_update_gpios(struct device *dev) static struct ltc4245_data *ltc4245_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct ltc4245_data *data = i2c_get_clientdata(client); + struct ltc4245_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; s32 val; int i; - mutex_lock(&data->update_lock); - 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); @@ -163,11 +153,9 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev) ltc4245_update_gpios(dev); data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } - mutex_unlock(&data->update_lock); - return data; } @@ -257,267 +245,199 @@ static unsigned int ltc4245_get_current(struct device *dev, u8 reg) return curr; } -static ssize_t ltc4245_show_voltage(struct device *dev, - struct device_attribute *da, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - const int voltage = ltc4245_get_voltage(dev, attr->index); +/* Map from voltage channel index to voltage register */ - return snprintf(buf, PAGE_SIZE, "%d\n", voltage); -} +static const s8 ltc4245_in_regs[] = { + LTC4245_12VIN, LTC4245_5VIN, LTC4245_3VIN, LTC4245_VEEIN, + LTC4245_12VOUT, LTC4245_5VOUT, LTC4245_3VOUT, LTC4245_VEEOUT, +}; -static ssize_t ltc4245_show_current(struct device *dev, - struct device_attribute *da, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - const unsigned int curr = ltc4245_get_current(dev, attr->index); +/* Map from current channel index to current register */ - return snprintf(buf, PAGE_SIZE, "%u\n", curr); -} +static const s8 ltc4245_curr_regs[] = { + LTC4245_12VSENSE, LTC4245_5VSENSE, LTC4245_3VSENSE, LTC4245_VEESENSE, +}; -static ssize_t ltc4245_show_power(struct device *dev, - struct device_attribute *da, - char *buf) +static int ltc4245_read_curr(struct device *dev, u32 attr, int channel, + long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - const unsigned int curr = ltc4245_get_current(dev, attr->index); - const int output_voltage = ltc4245_get_voltage(dev, attr->index+1); - - /* current in mA * voltage in mV == power in uW */ - const unsigned int power = abs(output_voltage * curr); + struct ltc4245_data *data = ltc4245_update_device(dev); - return snprintf(buf, PAGE_SIZE, "%u\n", power); + switch (attr) { + case hwmon_curr_input: + *val = ltc4245_get_current(dev, ltc4245_curr_regs[channel]); + return 0; + case hwmon_curr_max_alarm: + *val = !!(data->cregs[LTC4245_FAULT1] & BIT(channel + 4)); + return 0; + default: + return -EOPNOTSUPP; + } } -static ssize_t ltc4245_show_alarm(struct device *dev, - struct device_attribute *da, - char *buf) +static int ltc4245_read_in(struct device *dev, u32 attr, int channel, long *val) { - struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da); struct ltc4245_data *data = ltc4245_update_device(dev); - const u8 reg = data->cregs[attr->index]; - const u32 mask = attr->nr; - return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0); + switch (attr) { + case hwmon_in_input: + if (channel < 8) { + *val = ltc4245_get_voltage(dev, + ltc4245_in_regs[channel]); + } else { + int regval = data->gpios[channel - 8]; + + if (regval < 0) + return regval; + *val = regval * 10; + } + return 0; + case hwmon_in_min_alarm: + if (channel < 4) + *val = !!(data->cregs[LTC4245_FAULT1] & BIT(channel)); + else + *val = !!(data->cregs[LTC4245_FAULT2] & + BIT(channel - 4)); + return 0; + default: + return -EOPNOTSUPP; + } } -static ssize_t ltc4245_show_gpio(struct device *dev, - struct device_attribute *da, - char *buf) +static int ltc4245_read_power(struct device *dev, u32 attr, int channel, + long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct ltc4245_data *data = ltc4245_update_device(dev); - int val = data->gpios[attr->index]; - - /* handle stale GPIO's */ - if (val < 0) - return val; - - /* Convert to millivolts and print */ - return snprintf(buf, PAGE_SIZE, "%u\n", val * 10); + unsigned long curr; + long voltage; + + switch (attr) { + case hwmon_power_input: + (void)ltc4245_update_device(dev); + curr = ltc4245_get_current(dev, ltc4245_curr_regs[channel]); + voltage = ltc4245_get_voltage(dev, ltc4245_in_regs[channel]); + *val = abs(curr * voltage); + return 0; + default: + return -EOPNOTSUPP; + } } -/* Construct a sensor_device_attribute structure for each register */ - -/* Input voltages */ -static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4245_show_voltage, NULL, - LTC4245_12VIN); -static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4245_show_voltage, NULL, - LTC4245_5VIN); -static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc4245_show_voltage, NULL, - LTC4245_3VIN); -static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc4245_show_voltage, NULL, - LTC4245_VEEIN); - -/* Input undervoltage alarms */ -static SENSOR_DEVICE_ATTR_2(in1_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 0, LTC4245_FAULT1); -static SENSOR_DEVICE_ATTR_2(in2_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 1, LTC4245_FAULT1); -static SENSOR_DEVICE_ATTR_2(in3_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 2, LTC4245_FAULT1); -static SENSOR_DEVICE_ATTR_2(in4_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 3, LTC4245_FAULT1); - -/* Currents (via sense resistor) */ -static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4245_show_current, NULL, - LTC4245_12VSENSE); -static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc4245_show_current, NULL, - LTC4245_5VSENSE); -static SENSOR_DEVICE_ATTR(curr3_input, S_IRUGO, ltc4245_show_current, NULL, - LTC4245_3VSENSE); -static SENSOR_DEVICE_ATTR(curr4_input, S_IRUGO, ltc4245_show_current, NULL, - LTC4245_VEESENSE); - -/* Overcurrent alarms */ -static SENSOR_DEVICE_ATTR_2(curr1_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 4, LTC4245_FAULT1); -static SENSOR_DEVICE_ATTR_2(curr2_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 5, LTC4245_FAULT1); -static SENSOR_DEVICE_ATTR_2(curr3_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 6, LTC4245_FAULT1); -static SENSOR_DEVICE_ATTR_2(curr4_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 7, LTC4245_FAULT1); - -/* Output voltages */ -static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, ltc4245_show_voltage, NULL, - LTC4245_12VOUT); -static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, ltc4245_show_voltage, NULL, - LTC4245_5VOUT); -static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, ltc4245_show_voltage, NULL, - LTC4245_3VOUT); -static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, ltc4245_show_voltage, NULL, - LTC4245_VEEOUT); - -/* Power Bad alarms */ -static SENSOR_DEVICE_ATTR_2(in5_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 0, LTC4245_FAULT2); -static SENSOR_DEVICE_ATTR_2(in6_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 1, LTC4245_FAULT2); -static SENSOR_DEVICE_ATTR_2(in7_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 2, LTC4245_FAULT2); -static SENSOR_DEVICE_ATTR_2(in8_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, - 1 << 3, LTC4245_FAULT2); - -/* GPIO voltages */ -static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, ltc4245_show_gpio, NULL, 0); -static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, ltc4245_show_gpio, NULL, 1); -static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, ltc4245_show_gpio, NULL, 2); - -/* Power Consumption (virtual) */ -static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ltc4245_show_power, NULL, - LTC4245_12VSENSE); -static SENSOR_DEVICE_ATTR(power2_input, S_IRUGO, ltc4245_show_power, NULL, - LTC4245_5VSENSE); -static SENSOR_DEVICE_ATTR(power3_input, S_IRUGO, ltc4245_show_power, NULL, - LTC4245_3VSENSE); -static SENSOR_DEVICE_ATTR(power4_input, S_IRUGO, ltc4245_show_power, NULL, - LTC4245_VEESENSE); - -/* - * Finally, construct an array of pointers to members of the above objects, - * as required for sysfs_create_group() - */ -static struct attribute *ltc4245_std_attributes[] = { - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_in2_input.dev_attr.attr, - &sensor_dev_attr_in3_input.dev_attr.attr, - &sensor_dev_attr_in4_input.dev_attr.attr, - - &sensor_dev_attr_in1_min_alarm.dev_attr.attr, - &sensor_dev_attr_in2_min_alarm.dev_attr.attr, - &sensor_dev_attr_in3_min_alarm.dev_attr.attr, - &sensor_dev_attr_in4_min_alarm.dev_attr.attr, - - &sensor_dev_attr_curr1_input.dev_attr.attr, - &sensor_dev_attr_curr2_input.dev_attr.attr, - &sensor_dev_attr_curr3_input.dev_attr.attr, - &sensor_dev_attr_curr4_input.dev_attr.attr, - - &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, - &sensor_dev_attr_curr2_max_alarm.dev_attr.attr, - &sensor_dev_attr_curr3_max_alarm.dev_attr.attr, - &sensor_dev_attr_curr4_max_alarm.dev_attr.attr, - - &sensor_dev_attr_in5_input.dev_attr.attr, - &sensor_dev_attr_in6_input.dev_attr.attr, - &sensor_dev_attr_in7_input.dev_attr.attr, - &sensor_dev_attr_in8_input.dev_attr.attr, - - &sensor_dev_attr_in5_min_alarm.dev_attr.attr, - &sensor_dev_attr_in6_min_alarm.dev_attr.attr, - &sensor_dev_attr_in7_min_alarm.dev_attr.attr, - &sensor_dev_attr_in8_min_alarm.dev_attr.attr, - - &sensor_dev_attr_in9_input.dev_attr.attr, - - &sensor_dev_attr_power1_input.dev_attr.attr, - &sensor_dev_attr_power2_input.dev_attr.attr, - &sensor_dev_attr_power3_input.dev_attr.attr, - &sensor_dev_attr_power4_input.dev_attr.attr, - - NULL, -}; - -static struct attribute *ltc4245_gpio_attributes[] = { - &sensor_dev_attr_in10_input.dev_attr.attr, - &sensor_dev_attr_in11_input.dev_attr.attr, - NULL, -}; - -static const struct attribute_group ltc4245_std_group = { - .attrs = ltc4245_std_attributes, -}; - -static const struct attribute_group ltc4245_gpio_group = { - .attrs = ltc4245_gpio_attributes, -}; - -static int ltc4245_sysfs_create_groups(struct i2c_client *client) +static int ltc4245_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) { - struct ltc4245_data *data = i2c_get_clientdata(client); - struct device *dev = &client->dev; - int ret; - - /* register the standard sysfs attributes */ - ret = sysfs_create_group(&dev->kobj, <c4245_std_group); - if (ret) { - dev_err(dev, "unable to register standard attributes\n"); - return ret; + + switch (type) { + case hwmon_curr: + return ltc4245_read_curr(dev, attr, channel, val); + case hwmon_power: + return ltc4245_read_power(dev, attr, channel, val); + case hwmon_in: + return ltc4245_read_in(dev, attr, channel - 1, val); + default: + return -EOPNOTSUPP; } +} - /* if we're using the extra gpio support, register it's attributes */ - if (data->use_extra_gpios) { - ret = sysfs_create_group(&dev->kobj, <c4245_gpio_group); - if (ret) { - dev_err(dev, "unable to register gpio attributes\n"); - sysfs_remove_group(&dev->kobj, <c4245_std_group); - return ret; +static umode_t ltc4245_is_visible(const void *_data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct ltc4245_data *data = _data; + + switch (type) { + case hwmon_in: + if (channel == 0) + return 0; + switch (attr) { + case hwmon_in_input: + if (channel > 9 && !data->use_extra_gpios) + return 0; + return 0444; + case hwmon_in_min_alarm: + if (channel > 8) + return 0; + return 0444; + default: + return 0; + } + case hwmon_curr: + switch (attr) { + case hwmon_curr_input: + case hwmon_curr_max_alarm: + return 0444; + default: + return 0; + } + case hwmon_power: + switch (attr) { + case hwmon_power_input: + return 0444; + default: + return 0; } + default: + return 0; } - - return 0; } -static void ltc4245_sysfs_remove_groups(struct i2c_client *client) -{ - struct ltc4245_data *data = i2c_get_clientdata(client); - struct device *dev = &client->dev; +static const struct hwmon_channel_info * const ltc4245_info[] = { + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT, + HWMON_I_INPUT | HWMON_I_MIN_ALARM, + HWMON_I_INPUT | HWMON_I_MIN_ALARM, + HWMON_I_INPUT | HWMON_I_MIN_ALARM, + HWMON_I_INPUT | HWMON_I_MIN_ALARM, + HWMON_I_INPUT | HWMON_I_MIN_ALARM, + HWMON_I_INPUT | HWMON_I_MIN_ALARM, + HWMON_I_INPUT | HWMON_I_MIN_ALARM, + HWMON_I_INPUT | HWMON_I_MIN_ALARM, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT), + HWMON_CHANNEL_INFO(curr, + HWMON_C_INPUT | HWMON_C_MAX_ALARM, + HWMON_C_INPUT | HWMON_C_MAX_ALARM, + HWMON_C_INPUT | HWMON_C_MAX_ALARM, + HWMON_C_INPUT | HWMON_C_MAX_ALARM), + HWMON_CHANNEL_INFO(power, + HWMON_P_INPUT, + HWMON_P_INPUT, + HWMON_P_INPUT, + HWMON_P_INPUT), + NULL +}; - if (data->use_extra_gpios) - sysfs_remove_group(&dev->kobj, <c4245_gpio_group); +static const struct hwmon_ops ltc4245_hwmon_ops = { + .is_visible = ltc4245_is_visible, + .read = ltc4245_read, +}; - sysfs_remove_group(&dev->kobj, <c4245_std_group); -} +static const struct hwmon_chip_info ltc4245_chip_info = { + .ops = <c4245_hwmon_ops, + .info = ltc4245_info, +}; static bool ltc4245_use_extra_gpios(struct i2c_client *client) { 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)) + if (of_property_read_bool(np, "ltc4245,use-extra-gpios")) return true; -#endif return false; } -static int ltc4245_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ltc4245_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct ltc4245_data *data; - int ret; + struct device *hwmon_dev; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; @@ -526,44 +446,22 @@ static int ltc4245_probe(struct i2c_client *client, if (!data) return -ENOMEM; - i2c_set_clientdata(client, data); - mutex_init(&data->update_lock); + data->client = client; data->use_extra_gpios = ltc4245_use_extra_gpios(client); /* Initialize the LTC4245 chip */ i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00); i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00); - /* Register sysfs hooks */ - ret = ltc4245_sysfs_create_groups(client); - 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: - ltc4245_sysfs_remove_groups(client); - return ret; -} - -static int ltc4245_remove(struct i2c_client *client) -{ - struct ltc4245_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - ltc4245_sysfs_remove_groups(client); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_info(&client->dev, + client->name, data, + <c4245_chip_info, + NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct i2c_device_id ltc4245_id[] = { - { "ltc4245", 0 }, + { "ltc4245" }, { } }; MODULE_DEVICE_TABLE(i2c, ltc4245_id); @@ -574,7 +472,6 @@ static struct i2c_driver ltc4245_driver = { .name = "ltc4245", }, .probe = ltc4245_probe, - .remove = ltc4245_remove, .id_table = ltc4245_id, }; |
