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 | }; | ||
