Commit de7790155f745d30d58ed131ed112b8735413ab7
Committed by
Guenter Roeck
1 parent
9f6ad1ce64
Exists in
master
and in
39 other branches
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, <c4151_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, <c4151_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, <c4151_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(<c4151_driver); | |
244 | +} | |
245 | + | |
246 | +static void __exit ltc4151_exit(void) | |
247 | +{ | |
248 | + i2c_del_driver(<c4151_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); |