diff options
-rw-r--r-- | Documentation/hwmon/ina2xx | 29 | ||||
-rw-r--r-- | drivers/hwmon/Kconfig | 13 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/ina2xx.c | 368 | ||||
-rw-r--r-- | include/linux/platform_data/ina2xx.h | 19 |
5 files changed, 430 insertions, 0 deletions
diff --git a/Documentation/hwmon/ina2xx b/Documentation/hwmon/ina2xx new file mode 100644 index 000000000000..f50a6cc27616 --- /dev/null +++ b/Documentation/hwmon/ina2xx | |||
@@ -0,0 +1,29 @@ | |||
1 | Kernel driver ina2xx | ||
2 | ==================== | ||
3 | |||
4 | Supported chips: | ||
5 | * Texas Instruments INA219 | ||
6 | Prefix: 'ina219' | ||
7 | Addresses: I2C 0x40 - 0x4f | ||
8 | Datasheet: Publicly available at the Texas Instruments website | ||
9 | http://www.ti.com/ | ||
10 | |||
11 | * Texas Instruments INA226 | ||
12 | Prefix: 'ina226' | ||
13 | Addresses: I2C 0x40 - 0x4f | ||
14 | Datasheet: Publicly available at the Texas Instruments website | ||
15 | http://www.ti.com/ | ||
16 | |||
17 | Author: Lothar Felten <l-felten@ti.com> | ||
18 | |||
19 | Description | ||
20 | ----------- | ||
21 | |||
22 | The INA219 is a high-side current shunt and power monitor with an I2C | ||
23 | interface. The INA219 monitors both shunt drop and supply voltage, with | ||
24 | programmable conversion times and filtering. | ||
25 | |||
26 | The INA226 is a current shunt and power monitor with an I2C interface. | ||
27 | The INA226 monitors both a shunt voltage drop and bus supply voltage. | ||
28 | |||
29 | The shunt value in micro-ohms can be set via platform data. | ||
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 8deedc1b9840..1c7bbd458902 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig | |||
@@ -1102,6 +1102,19 @@ config SENSORS_AMC6821 | |||
1102 | This driver can also be build as a module. If so, the module | 1102 | This driver can also be build as a module. If so, the module |
1103 | will be called amc6821. | 1103 | will be called amc6821. |
1104 | 1104 | ||
1105 | config SENSORS_INA2XX | ||
1106 | tristate "Texas Instruments INA219, INA226" | ||
1107 | depends on I2C && EXPERIMENTAL | ||
1108 | help | ||
1109 | If you say yes here you get support for INA219 and INA226 power | ||
1110 | monitor chips. | ||
1111 | |||
1112 | The INA2xx driver is configured for the default configuration of | ||
1113 | the part as described in the datasheet. | ||
1114 | Default value for Rshunt is 10 mOhms. | ||
1115 | This driver can also be built as a module. If so, the module | ||
1116 | will be called ina2xx. | ||
1117 | |||
1105 | config SENSORS_THMC50 | 1118 | config SENSORS_THMC50 |
1106 | tristate "Texas Instruments THMC50 / Analog Devices ADM1022" | 1119 | tristate "Texas Instruments THMC50 / Analog Devices ADM1022" |
1107 | depends on I2C | 1120 | depends on I2C |
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 6d3f11f71815..e1eeac13b851 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile | |||
@@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o | |||
62 | obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o | 62 | obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o |
63 | obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o | 63 | obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o |
64 | obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o | 64 | obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o |
65 | obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o | ||
65 | obj-$(CONFIG_SENSORS_IT87) += it87.o | 66 | obj-$(CONFIG_SENSORS_IT87) += it87.o |
66 | obj-$(CONFIG_SENSORS_JC42) += jc42.o | 67 | obj-$(CONFIG_SENSORS_JC42) += jc42.o |
67 | obj-$(CONFIG_SENSORS_JZ4740) += jz4740-hwmon.o | 68 | obj-$(CONFIG_SENSORS_JZ4740) += jz4740-hwmon.o |
diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c new file mode 100644 index 000000000000..7f3f4a385729 --- /dev/null +++ b/drivers/hwmon/ina2xx.c | |||
@@ -0,0 +1,368 @@ | |||
1 | /* | ||
2 | * Driver for Texas Instruments INA219, INA226 power monitor chips | ||
3 | * | ||
4 | * INA219: | ||
5 | * Zero Drift Bi-Directional Current/Power Monitor with I2C Interface | ||
6 | * Datasheet: http://www.ti.com/product/ina219 | ||
7 | * | ||
8 | * INA226: | ||
9 | * Bi-Directional Current/Power Monitor with I2C Interface | ||
10 | * Datasheet: http://www.ti.com/product/ina226 | ||
11 | * | ||
12 | * Copyright (C) 2012 Lothar Felten <l-felten@ti.com> | ||
13 | * Thanks to Jan Volkering | ||
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; version 2 of the License. | ||
18 | */ | ||
19 | |||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/err.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/i2c.h> | ||
26 | #include <linux/hwmon.h> | ||
27 | #include <linux/hwmon-sysfs.h> | ||
28 | |||
29 | #include <linux/platform_data/ina2xx.h> | ||
30 | |||
31 | /* common register definitions */ | ||
32 | #define INA2XX_CONFIG 0x00 | ||
33 | #define INA2XX_SHUNT_VOLTAGE 0x01 /* readonly */ | ||
34 | #define INA2XX_BUS_VOLTAGE 0x02 /* readonly */ | ||
35 | #define INA2XX_POWER 0x03 /* readonly */ | ||
36 | #define INA2XX_CURRENT 0x04 /* readonly */ | ||
37 | #define INA2XX_CALIBRATION 0x05 | ||
38 | |||
39 | /* INA226 register definitions */ | ||
40 | #define INA226_MASK_ENABLE 0x06 | ||
41 | #define INA226_ALERT_LIMIT 0x07 | ||
42 | #define INA226_DIE_ID 0xFF | ||
43 | |||
44 | |||
45 | /* register count */ | ||
46 | #define INA219_REGISTERS 6 | ||
47 | #define INA226_REGISTERS 8 | ||
48 | |||
49 | #define INA2XX_MAX_REGISTERS 8 | ||
50 | |||
51 | /* settings - depend on use case */ | ||
52 | #define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */ | ||
53 | #define INA226_CONFIG_DEFAULT 0x4527 /* averages=16 */ | ||
54 | |||
55 | /* worst case is 68.10 ms (~14.6Hz, ina219) */ | ||
56 | #define INA2XX_CONVERSION_RATE 15 | ||
57 | |||
58 | enum ina2xx_ids { ina219, ina226 }; | ||
59 | |||
60 | struct ina2xx_data { | ||
61 | struct device *hwmon_dev; | ||
62 | |||
63 | struct mutex update_lock; | ||
64 | bool valid; | ||
65 | unsigned long last_updated; | ||
66 | |||
67 | int kind; | ||
68 | int registers; | ||
69 | u16 regs[INA2XX_MAX_REGISTERS]; | ||
70 | }; | ||
71 | |||
72 | int ina2xx_read_word(struct i2c_client *client, int reg) | ||
73 | { | ||
74 | int val = i2c_smbus_read_word_data(client, reg); | ||
75 | if (unlikely(val < 0)) { | ||
76 | dev_dbg(&client->dev, | ||
77 | "Failed to read register: %d\n", reg); | ||
78 | return val; | ||
79 | } | ||
80 | return be16_to_cpu(val); | ||
81 | } | ||
82 | |||
83 | void ina2xx_write_word(struct i2c_client *client, int reg, int data) | ||
84 | { | ||
85 | i2c_smbus_write_word_data(client, reg, cpu_to_be16(data)); | ||
86 | } | ||
87 | |||
88 | static struct ina2xx_data *ina2xx_update_device(struct device *dev) | ||
89 | { | ||
90 | struct i2c_client *client = to_i2c_client(dev); | ||
91 | struct ina2xx_data *data = i2c_get_clientdata(client); | ||
92 | struct ina2xx_data *ret = data; | ||
93 | |||
94 | mutex_lock(&data->update_lock); | ||
95 | |||
96 | if (time_after(jiffies, data->last_updated + | ||
97 | HZ / INA2XX_CONVERSION_RATE) || !data->valid) { | ||
98 | |||
99 | int i; | ||
100 | |||
101 | dev_dbg(&client->dev, "Starting ina2xx update\n"); | ||
102 | |||
103 | /* Read all registers */ | ||
104 | for (i = 0; i < data->registers; i++) { | ||
105 | int rv = ina2xx_read_word(client, i); | ||
106 | if (rv < 0) { | ||
107 | ret = ERR_PTR(rv); | ||
108 | goto abort; | ||
109 | } | ||
110 | data->regs[i] = rv; | ||
111 | } | ||
112 | data->last_updated = jiffies; | ||
113 | data->valid = 1; | ||
114 | } | ||
115 | abort: | ||
116 | mutex_unlock(&data->update_lock); | ||
117 | return ret; | ||
118 | } | ||
119 | |||
120 | static int ina219_get_value(struct ina2xx_data *data, u8 reg) | ||
121 | { | ||
122 | /* | ||
123 | * calculate exact value for the given register | ||
124 | * we assume default power-on reset settings: | ||
125 | * bus voltage range 32V | ||
126 | * gain = /8 | ||
127 | * adc 1 & 2 -> conversion time 532uS | ||
128 | * mode is continuous shunt and bus | ||
129 | * calibration value is INA219_CALIBRATION_VALUE | ||
130 | */ | ||
131 | int val = data->regs[reg]; | ||
132 | |||
133 | switch (reg) { | ||
134 | case INA2XX_SHUNT_VOLTAGE: | ||
135 | /* LSB=10uV. Convert to mV. */ | ||
136 | val = DIV_ROUND_CLOSEST(val, 100); | ||
137 | break; | ||
138 | case INA2XX_BUS_VOLTAGE: | ||
139 | /* LSB=4mV. Register is not right aligned, convert to mV. */ | ||
140 | val = (val >> 3) * 4; | ||
141 | break; | ||
142 | case INA2XX_POWER: | ||
143 | /* LSB=20mW. Convert to uW */ | ||
144 | val = val * 20 * 1000; | ||
145 | break; | ||
146 | case INA2XX_CURRENT: | ||
147 | /* LSB=1mA (selected). Is in mA */ | ||
148 | break; | ||
149 | default: | ||
150 | /* programmer goofed */ | ||
151 | WARN_ON_ONCE(1); | ||
152 | val = 0; | ||
153 | break; | ||
154 | } | ||
155 | |||
156 | return val; | ||
157 | } | ||
158 | |||
159 | static int ina226_get_value(struct ina2xx_data *data, u8 reg) | ||
160 | { | ||
161 | /* | ||
162 | * calculate exact value for the given register | ||
163 | * we assume default power-on reset settings: | ||
164 | * bus voltage range 32V | ||
165 | * gain = /8 | ||
166 | * adc 1 & 2 -> conversion time 532uS | ||
167 | * mode is continuous shunt and bus | ||
168 | * calibration value is INA226_CALIBRATION_VALUE | ||
169 | */ | ||
170 | int val = data->regs[reg]; | ||
171 | |||
172 | switch (reg) { | ||
173 | case INA2XX_SHUNT_VOLTAGE: | ||
174 | /* LSB=2.5uV. Convert to mV. */ | ||
175 | val = DIV_ROUND_CLOSEST(val, 400); | ||
176 | break; | ||
177 | case INA2XX_BUS_VOLTAGE: | ||
178 | /* LSB=1.25mV. Convert to mV. */ | ||
179 | val = val + DIV_ROUND_CLOSEST(val, 4); | ||
180 | break; | ||
181 | case INA2XX_POWER: | ||
182 | /* LSB=25mW. Convert to uW */ | ||
183 | val = val * 25 * 1000; | ||
184 | break; | ||
185 | case INA2XX_CURRENT: | ||
186 | /* LSB=1mA (selected). Is in mA */ | ||
187 | break; | ||
188 | default: | ||
189 | /* programmer goofed */ | ||
190 | WARN_ON_ONCE(1); | ||
191 | val = 0; | ||
192 | break; | ||
193 | } | ||
194 | |||
195 | return val; | ||
196 | } | ||
197 | |||
198 | static ssize_t ina2xx_show_value(struct device *dev, | ||
199 | struct device_attribute *da, char *buf) | ||
200 | { | ||
201 | struct sensor_device_attribute *attr = to_sensor_dev_attr(da); | ||
202 | struct ina2xx_data *data = ina2xx_update_device(dev); | ||
203 | int value = 0; | ||
204 | |||
205 | if (IS_ERR(data)) | ||
206 | return PTR_ERR(data); | ||
207 | |||
208 | switch (data->kind) { | ||
209 | case ina219: | ||
210 | value = ina219_get_value(data, attr->index); | ||
211 | break; | ||
212 | case ina226: | ||
213 | value = ina226_get_value(data, attr->index); | ||
214 | break; | ||
215 | default: | ||
216 | WARN_ON_ONCE(1); | ||
217 | break; | ||
218 | } | ||
219 | return snprintf(buf, PAGE_SIZE, "%d\n", value); | ||
220 | } | ||
221 | |||
222 | /* shunt voltage */ | ||
223 | static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, \ | ||
224 | ina2xx_show_value, NULL, INA2XX_SHUNT_VOLTAGE); | ||
225 | |||
226 | /* bus voltage */ | ||
227 | static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, \ | ||
228 | ina2xx_show_value, NULL, INA2XX_BUS_VOLTAGE); | ||
229 | |||
230 | /* calculated current */ | ||
231 | static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, \ | ||
232 | ina2xx_show_value, NULL, INA2XX_CURRENT); | ||
233 | |||
234 | /* calculated power */ | ||
235 | static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, \ | ||
236 | ina2xx_show_value, NULL, INA2XX_POWER); | ||
237 | |||
238 | /* pointers to created device attributes */ | ||
239 | static struct attribute *ina2xx_attributes[] = { | ||
240 | &sensor_dev_attr_in0_input.dev_attr.attr, | ||
241 | &sensor_dev_attr_in1_input.dev_attr.attr, | ||
242 | &sensor_dev_attr_curr1_input.dev_attr.attr, | ||
243 | &sensor_dev_attr_power1_input.dev_attr.attr, | ||
244 | NULL, | ||
245 | }; | ||
246 | |||
247 | static const struct attribute_group ina2xx_group = { | ||
248 | .attrs = ina2xx_attributes, | ||
249 | }; | ||
250 | |||
251 | static int ina2xx_probe(struct i2c_client *client, | ||
252 | const struct i2c_device_id *id) | ||
253 | { | ||
254 | struct i2c_adapter *adapter = client->adapter; | ||
255 | struct ina2xx_data *data; | ||
256 | struct ina2xx_platform_data *pdata; | ||
257 | int ret = 0; | ||
258 | long shunt = 10000; /* default shunt value 10mOhms */ | ||
259 | |||
260 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) | ||
261 | return -ENODEV; | ||
262 | |||
263 | data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); | ||
264 | if (!data) | ||
265 | return -ENOMEM; | ||
266 | |||
267 | if (client->dev.platform_data) { | ||
268 | pdata = | ||
269 | (struct ina2xx_platform_data *)client->dev.platform_data; | ||
270 | shunt = pdata->shunt_uohms; | ||
271 | } | ||
272 | |||
273 | if (shunt <= 0) | ||
274 | return -ENODEV; | ||
275 | |||
276 | /* set the device type */ | ||
277 | data->kind = id->driver_data; | ||
278 | |||
279 | switch (data->kind) { | ||
280 | case ina219: | ||
281 | /* device configuration */ | ||
282 | ina2xx_write_word(client, INA2XX_CONFIG, INA219_CONFIG_DEFAULT); | ||
283 | |||
284 | /* set current LSB to 1mA, shunt is in uOhms */ | ||
285 | /* (equation 13 in datasheet) */ | ||
286 | ina2xx_write_word(client, INA2XX_CALIBRATION, 40960000 / shunt); | ||
287 | dev_info(&client->dev, | ||
288 | "power monitor INA219 (Rshunt = %li uOhm)\n", shunt); | ||
289 | data->registers = INA219_REGISTERS; | ||
290 | break; | ||
291 | case ina226: | ||
292 | /* device configuration */ | ||
293 | ina2xx_write_word(client, INA2XX_CONFIG, INA226_CONFIG_DEFAULT); | ||
294 | |||
295 | /* set current LSB to 1mA, shunt is in uOhms */ | ||
296 | /* (equation 1 in datasheet)*/ | ||
297 | ina2xx_write_word(client, INA2XX_CALIBRATION, 5120000 / shunt); | ||
298 | dev_info(&client->dev, | ||
299 | "power monitor INA226 (Rshunt = %li uOhm)\n", shunt); | ||
300 | data->registers = INA226_REGISTERS; | ||
301 | break; | ||
302 | default: | ||
303 | /* unknown device id */ | ||
304 | return -ENODEV; | ||
305 | } | ||
306 | |||
307 | i2c_set_clientdata(client, data); | ||
308 | mutex_init(&data->update_lock); | ||
309 | |||
310 | ret = sysfs_create_group(&client->dev.kobj, &ina2xx_group); | ||
311 | if (ret) | ||
312 | return ret; | ||
313 | |||
314 | data->hwmon_dev = hwmon_device_register(&client->dev); | ||
315 | if (IS_ERR(data->hwmon_dev)) { | ||
316 | ret = PTR_ERR(data->hwmon_dev); | ||
317 | goto out_err_hwmon; | ||
318 | } | ||
319 | |||
320 | return 0; | ||
321 | |||
322 | out_err_hwmon: | ||
323 | sysfs_remove_group(&client->dev.kobj, &ina2xx_group); | ||
324 | return ret; | ||
325 | } | ||
326 | |||
327 | static int ina2xx_remove(struct i2c_client *client) | ||
328 | { | ||
329 | struct ina2xx_data *data = i2c_get_clientdata(client); | ||
330 | |||
331 | hwmon_device_unregister(data->hwmon_dev); | ||
332 | sysfs_remove_group(&client->dev.kobj, &ina2xx_group); | ||
333 | |||
334 | return 0; | ||
335 | } | ||
336 | |||
337 | static const struct i2c_device_id ina2xx_id[] = { | ||
338 | { "ina219", ina219 }, | ||
339 | { "ina226", ina226 }, | ||
340 | { } | ||
341 | }; | ||
342 | MODULE_DEVICE_TABLE(i2c, ina2xx_id); | ||
343 | |||
344 | static struct i2c_driver ina2xx_driver = { | ||
345 | .driver = { | ||
346 | .name = "ina2xx", | ||
347 | }, | ||
348 | .probe = ina2xx_probe, | ||
349 | .remove = ina2xx_remove, | ||
350 | .id_table = ina2xx_id, | ||
351 | }; | ||
352 | |||
353 | static int __init ina2xx_init(void) | ||
354 | { | ||
355 | return i2c_add_driver(&ina2xx_driver); | ||
356 | } | ||
357 | |||
358 | static void __exit ina2xx_exit(void) | ||
359 | { | ||
360 | i2c_del_driver(&ina2xx_driver); | ||
361 | } | ||
362 | |||
363 | MODULE_AUTHOR("Lothar Felten <l-felten@ti.com>"); | ||
364 | MODULE_DESCRIPTION("ina2xx driver"); | ||
365 | MODULE_LICENSE("GPL"); | ||
366 | |||
367 | module_init(ina2xx_init); | ||
368 | module_exit(ina2xx_exit); | ||
diff --git a/include/linux/platform_data/ina2xx.h b/include/linux/platform_data/ina2xx.h new file mode 100644 index 000000000000..9abc0ca7259b --- /dev/null +++ b/include/linux/platform_data/ina2xx.h | |||
@@ -0,0 +1,19 @@ | |||
1 | /* | ||
2 | * Driver for Texas Instruments INA219, INA226 power monitor chips | ||
3 | * | ||
4 | * Copyright (C) 2012 Lothar Felten <l-felten@ti.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * For further information, see the Documentation/hwmon/ina2xx file. | ||
11 | */ | ||
12 | |||
13 | /** | ||
14 | * struct ina2xx_platform_data - ina2xx info | ||
15 | * @shunt_uohms shunt resistance in microohms | ||
16 | */ | ||
17 | struct ina2xx_platform_data { | ||
18 | long shunt_uohms; | ||
19 | }; | ||