Commit de7790155f745d30d58ed131ed112b8735413ab7

Authored by per.dalen@appeartv.com
Committed by Guenter Roeck
1 parent 9f6ad1ce64

hwmon: Add support for LTC4151

LTC4151 is High Voltage I2C Current and Voltage Monitor from Linear
Technology.

Signed-off-by: Per Dalen <per.dalen@appeartv.com>
Signed-off-by: Guenter Roeck <guenter.roeck@ericsson.com>

Showing 4 changed files with 315 additions and 0 deletions Side-by-side Diff

Documentation/hwmon/ltc4151
  1 +Kernel driver ltc4151
  2 +=====================
  3 +
  4 +Supported chips:
  5 + * Linear Technology LTC4151
  6 + Prefix: 'ltc4151'
  7 + Addresses scanned: -
  8 + Datasheet:
  9 + http://www.linear.com/docs/Datasheet/4151fc.pdf
  10 +
  11 +Author: Per Dalen <per.dalen@appeartv.com>
  12 +
  13 +
  14 +Description
  15 +-----------
  16 +
  17 +The LTC4151 is a High Voltage I2C Current and Voltage Monitor.
  18 +
  19 +
  20 +Usage Notes
  21 +-----------
  22 +
  23 +This driver does not probe for LTC4151 devices, since there is no register
  24 +which can be safely used to identify the chip. You will have to instantiate
  25 +the devices explicitly.
  26 +
  27 +Example: the following will load the driver for an LTC4151 at address 0x6f
  28 +on I2C bus #0:
  29 +# modprobe ltc4151
  30 +# echo ltc4151 0x6f > /sys/bus/i2c/devices/i2c-0/new_device
  31 +
  32 +
  33 +Sysfs entries
  34 +-------------
  35 +
  36 +Voltage readings provided by this driver are reported as obtained from the ADIN
  37 +and VIN registers.
  38 +
  39 +Current reading provided by this driver is reported as obtained from the Current
  40 +Sense register. The reported value assumes that a 1 mOhm sense resistor is
  41 +installed.
  42 +
  43 +in1_input VDIN voltage (mV)
  44 +
  45 +in2_input ADIN voltage (mV)
  46 +
  47 +curr1_input SENSE current (mA)
drivers/hwmon/Kconfig
... ... @@ -636,6 +636,17 @@
636 636 This driver can also be built as a module. If so, the module
637 637 will be called lm93.
638 638  
  639 +config SENSORS_LTC4151
  640 + tristate "Linear Technology LTC4151"
  641 + depends on I2C
  642 + default n
  643 + help
  644 + If you say yes here you get support for Linear Technology LTC4151
  645 + High Voltage I2C Current and Voltage Monitor interface.
  646 +
  647 + This driver can also be built as a module. If so, the module will
  648 + be called ltc4151.
  649 +
639 650 config SENSORS_LTC4215
640 651 tristate "Linear Technology LTC4215"
641 652 depends on I2C && EXPERIMENTAL
drivers/hwmon/Makefile
... ... @@ -80,6 +80,7 @@
80 80 obj-$(CONFIG_SENSORS_LM92) += lm92.o
81 81 obj-$(CONFIG_SENSORS_LM93) += lm93.o
82 82 obj-$(CONFIG_SENSORS_LM95241) += lm95241.o
  83 +obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o
83 84 obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o
84 85 obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o
85 86 obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o
drivers/hwmon/ltc4151.c
  1 +/*
  2 + * Driver for Linear Technology LTC4151 High Voltage I2C Current
  3 + * and Voltage Monitor
  4 + *
  5 + * Copyright (C) 2011 AppearTV AS
  6 + *
  7 + * Derived from:
  8 + *
  9 + * Driver for Linear Technology LTC4261 I2C Negative Voltage Hot
  10 + * Swap Controller
  11 + * Copyright (C) 2010 Ericsson AB.
  12 + *
  13 + * Datasheet: http://www.linear.com/docs/Datasheet/4151fc.pdf
  14 + *
  15 + * This program is free software; you can redistribute it and/or modify
  16 + * it under the terms of the GNU General Public License as published by
  17 + * the Free Software Foundation; either version 2 of the License, or
  18 + * (at your option) any later version.
  19 + *
  20 + * This program is distributed in the hope that it will be useful,
  21 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  23 + * GNU General Public License for more details.
  24 + *
  25 + * You should have received a copy of the GNU General Public License
  26 + * along with this program; if not, write to the Free Software
  27 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  28 + *
  29 + */
  30 +
  31 +#include <linux/kernel.h>
  32 +#include <linux/module.h>
  33 +#include <linux/init.h>
  34 +#include <linux/err.h>
  35 +#include <linux/slab.h>
  36 +#include <linux/i2c.h>
  37 +#include <linux/hwmon.h>
  38 +#include <linux/hwmon-sysfs.h>
  39 +
  40 +/* chip registers */
  41 +#define LTC4151_SENSE_H 0x00
  42 +#define LTC4151_SENSE_L 0x01
  43 +#define LTC4151_VIN_H 0x02
  44 +#define LTC4151_VIN_L 0x03
  45 +#define LTC4151_ADIN_H 0x04
  46 +#define LTC4151_ADIN_L 0x05
  47 +
  48 +struct ltc4151_data {
  49 + struct device *hwmon_dev;
  50 +
  51 + struct mutex update_lock;
  52 + bool valid;
  53 + unsigned long last_updated; /* in jiffies */
  54 +
  55 + /* Registers */
  56 + u8 regs[6];
  57 +};
  58 +
  59 +static struct ltc4151_data *ltc4151_update_device(struct device *dev)
  60 +{
  61 + struct i2c_client *client = to_i2c_client(dev);
  62 + struct ltc4151_data *data = i2c_get_clientdata(client);
  63 + struct ltc4151_data *ret = data;
  64 +
  65 + mutex_lock(&data->update_lock);
  66 +
  67 + /*
  68 + * The chip's A/D updates 6 times per second
  69 + * (Conversion Rate 6 - 9 Hz)
  70 + */
  71 + if (time_after(jiffies, data->last_updated + HZ / 6) || !data->valid) {
  72 + int i;
  73 +
  74 + dev_dbg(&client->dev, "Starting ltc4151 update\n");
  75 +
  76 + /* Read all registers */
  77 + for (i = 0; i < ARRAY_SIZE(data->regs); i++) {
  78 + int val;
  79 +
  80 + val = i2c_smbus_read_byte_data(client, i);
  81 + if (unlikely(val < 0)) {
  82 + dev_dbg(dev,
  83 + "Failed to read ADC value: error %d\n",
  84 + val);
  85 + ret = ERR_PTR(val);
  86 + goto abort;
  87 + }
  88 + data->regs[i] = val;
  89 + }
  90 + data->last_updated = jiffies;
  91 + data->valid = 1;
  92 + }
  93 +abort:
  94 + mutex_unlock(&data->update_lock);
  95 + return ret;
  96 +}
  97 +
  98 +/* Return the voltage from the given register in mV */
  99 +static int ltc4151_get_value(struct ltc4151_data *data, u8 reg)
  100 +{
  101 + u32 val;
  102 +
  103 + val = (data->regs[reg] << 4) + (data->regs[reg + 1] >> 4);
  104 +
  105 + switch (reg) {
  106 + case LTC4151_ADIN_H:
  107 + /* 500uV resolution. Convert to mV. */
  108 + val = val * 500 / 1000;
  109 + break;
  110 + case LTC4151_SENSE_H:
  111 + /*
  112 + * 20uV resolution. Convert to current as measured with
  113 + * an 1 mOhm sense resistor, in mA.
  114 + */
  115 + val = val * 20;
  116 + break;
  117 + case LTC4151_VIN_H:
  118 + /* 25 mV per increment */
  119 + val = val * 25;
  120 + break;
  121 + default:
  122 + /* If we get here, the developer messed up */
  123 + WARN_ON_ONCE(1);
  124 + val = 0;
  125 + break;
  126 + }
  127 +
  128 + return val;
  129 +}
  130 +
  131 +static ssize_t ltc4151_show_value(struct device *dev,
  132 + struct device_attribute *da, char *buf)
  133 +{
  134 + struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
  135 + struct ltc4151_data *data = ltc4151_update_device(dev);
  136 + int value;
  137 +
  138 + if (IS_ERR(data))
  139 + return PTR_ERR(data);
  140 +
  141 + value = ltc4151_get_value(data, attr->index);
  142 + return snprintf(buf, PAGE_SIZE, "%d\n", value);
  143 +}
  144 +
  145 +/*
  146 + * Input voltages.
  147 + */
  148 +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, \
  149 + ltc4151_show_value, NULL, LTC4151_VIN_H);
  150 +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, \
  151 + ltc4151_show_value, NULL, LTC4151_ADIN_H);
  152 +
  153 +/* Currents (via sense resistor) */
  154 +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, \
  155 + ltc4151_show_value, NULL, LTC4151_SENSE_H);
  156 +
  157 +/* Finally, construct an array of pointers to members of the above objects,
  158 + * as required for sysfs_create_group()
  159 + */
  160 +static struct attribute *ltc4151_attributes[] = {
  161 + &sensor_dev_attr_in1_input.dev_attr.attr,
  162 + &sensor_dev_attr_in2_input.dev_attr.attr,
  163 +
  164 + &sensor_dev_attr_curr1_input.dev_attr.attr,
  165 +
  166 + NULL,
  167 +};
  168 +
  169 +static const struct attribute_group ltc4151_group = {
  170 + .attrs = ltc4151_attributes,
  171 +};
  172 +
  173 +static int ltc4151_probe(struct i2c_client *client,
  174 + const struct i2c_device_id *id)
  175 +{
  176 + struct i2c_adapter *adapter = client->adapter;
  177 + struct ltc4151_data *data;
  178 + int ret;
  179 +
  180 + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
  181 + return -ENODEV;
  182 +
  183 + data = kzalloc(sizeof(*data), GFP_KERNEL);
  184 + if (!data) {
  185 + ret = -ENOMEM;
  186 + goto out_kzalloc;
  187 + }
  188 +
  189 + i2c_set_clientdata(client, data);
  190 + mutex_init(&data->update_lock);
  191 +
  192 + /* Register sysfs hooks */
  193 + ret = sysfs_create_group(&client->dev.kobj, &ltc4151_group);
  194 + if (ret)
  195 + goto out_sysfs_create_group;
  196 +
  197 + data->hwmon_dev = hwmon_device_register(&client->dev);
  198 + if (IS_ERR(data->hwmon_dev)) {
  199 + ret = PTR_ERR(data->hwmon_dev);
  200 + goto out_hwmon_device_register;
  201 + }
  202 +
  203 + return 0;
  204 +
  205 +out_hwmon_device_register:
  206 + sysfs_remove_group(&client->dev.kobj, &ltc4151_group);
  207 +out_sysfs_create_group:
  208 + kfree(data);
  209 +out_kzalloc:
  210 + return ret;
  211 +}
  212 +
  213 +static int ltc4151_remove(struct i2c_client *client)
  214 +{
  215 + struct ltc4151_data *data = i2c_get_clientdata(client);
  216 +
  217 + hwmon_device_unregister(data->hwmon_dev);
  218 + sysfs_remove_group(&client->dev.kobj, &ltc4151_group);
  219 +
  220 + kfree(data);
  221 +
  222 + return 0;
  223 +}
  224 +
  225 +static const struct i2c_device_id ltc4151_id[] = {
  226 + { "ltc4151", 0 },
  227 + { }
  228 +};
  229 +MODULE_DEVICE_TABLE(i2c, ltc4151_id);
  230 +
  231 +/* This is the driver that will be inserted */
  232 +static struct i2c_driver ltc4151_driver = {
  233 + .driver = {
  234 + .name = "ltc4151",
  235 + },
  236 + .probe = ltc4151_probe,
  237 + .remove = ltc4151_remove,
  238 + .id_table = ltc4151_id,
  239 +};
  240 +
  241 +static int __init ltc4151_init(void)
  242 +{
  243 + return i2c_add_driver(&ltc4151_driver);
  244 +}
  245 +
  246 +static void __exit ltc4151_exit(void)
  247 +{
  248 + i2c_del_driver(&ltc4151_driver);
  249 +}
  250 +
  251 +MODULE_AUTHOR("Per Dalen <per.dalen@appeartv.com>");
  252 +MODULE_DESCRIPTION("LTC4151 driver");
  253 +MODULE_LICENSE("GPL");
  254 +
  255 +module_init(ltc4151_init);
  256 +module_exit(ltc4151_exit);