History

hwmon: INA219 and INA226 support

Add support for the Texas Instruments INA219 and INA226 power monitors.

Felten, Lothar committed 14 years ago

Commit f7c2fe31 parent 9172b5d5 files · +430 −0Browse files

Unified · Split

5 files
  1. Documentation/hwmon/ina2xx +29 −0
  2. drivers/hwmon/Kconfig +13 −0
  3. drivers/hwmon/Makefile +1 −0
  4. drivers/hwmon/ina2xx.c +368 −0
  5. include/linux/platform_data/ina2xx.h +19 −0
Documentation/hwmon/ina2xx+29−0
@@ -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.
drivers/hwmon/Kconfig+13−0
@@ -1102,6 +1102,19 @@ config SENSORS_AMC6821 · 1101 unmodified lines
11021102 This driver can also be build as a module. If so, the module
11031103 will be called amc6821.
11041104
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+
11051118 config SENSORS_THMC50
11061119 tristate "Texas Instruments THMC50 / Analog Devices ADM1022"
11071120 depends on I2C
drivers/hwmon/Makefile+1−0
@@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o · 61 unmodified lines
6262 obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
6363 obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
6464 obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o
65+obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o
6566 obj-$(CONFIG_SENSORS_IT87) += it87.o
6667 obj-$(CONFIG_SENSORS_JC42) += jc42.o
6768 obj-$(CONFIG_SENSORS_JZ4740) += jz4740-hwmon.o
drivers/hwmon/ina2xx.c+368−0
@@ -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);
include/linux/platform_data/ina2xx.h+19−0
@@ -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+};

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t