summaryrefslogtreecommitdiffstats
path: root/drivers/hwmon
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-05-23 14:15:09 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2012-05-23 14:15:09 -0700
commit1259f6ee15c1603dcae41eb6af5a5f9cf932d4d6 (patch)
treebf85658f7c5a2c86da3bc613d7fea13bbffe4e72 /drivers/hwmon
parent468f4d1a855f8039dabf441b8bf68cae264033ff (diff)
parent4573acbc461b8089198500cee06ef0cdc5b70e82 (diff)
Merge tag 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck: "New driver for INA219 and INA226, added support for IT8782F and IT8783E/F to it87 driver, plus cleanups in a couple of drivers." * tag 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: hwmon: (it87) Make temp3 attribute conditional for IT8782F hwmon: (it87) Convert to use devm_kzalloc and devm_request_region hwmon: INA219 and INA226 support hwmon: (it87) Create voltage attributes only if voltage is enabled hwmon: (ntc_thermistor) Fix checkpatch warning hwmon: (ntc_thermistor) Optimize and fix build warning hwmon: (ntc_thermistor) Return error code from hwmon_device_register hwmon: (ntc_thermistor) Convert to devm_kzalloc hwmon: (ad7314) Remove unused defines, and rename OFFSET to SHIFT acpi_power_meter: clean up code around setup_attrs acpi_power_meter: drop meter_rw_attrs, use common meter_attrs acpi_power_meter: remove duplicate code between register_{ro,rw}_attrs acpi_power_meter: use a {RW,RO}_SENSOR_TEMPLATE macro to clean things up acpi_power_meter: use the same struct {rw,ro}_sensor_template for both hwmon: use module_pci_driver hwmon: (it87) Add support for IT8782F and IT8783E/F
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/Kconfig13
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/acpi_power_meter.c166
-rw-r--r--drivers/hwmon/ad7314.c11
-rw-r--r--drivers/hwmon/fam15h_power.c13
-rw-r--r--drivers/hwmon/ina2xx.c368
-rw-r--r--drivers/hwmon/it87.c384
-rw-r--r--drivers/hwmon/k10temp.c13
-rw-r--r--drivers/hwmon/k8temp.c13
-rw-r--r--drivers/hwmon/ntc_thermistor.c191
10 files changed, 835 insertions, 338 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index e466ecba8dc1..7cd9bf42108b 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1102,6 +1102,19 @@ config SENSORS_AMC6821
This driver can also be build as a module. If so, the module
will be called amc6821.
+config SENSORS_INA2XX
+ tristate "Texas Instruments INA219, INA226"
+ depends on I2C && EXPERIMENTAL
+ help
+ If you say yes here you get support for INA219 and INA226 power
+ monitor chips.
+
+ The INA2xx driver is configured for the default configuration of
+ the part as described in the datasheet.
+ Default value for Rshunt is 10 mOhms.
+ This driver can also be built as a module. If so, the module
+ will be called ina2xx.
+
config SENSORS_THMC50
tristate "Texas Instruments THMC50 / Analog Devices ADM1022"
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
obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o
+obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o
obj-$(CONFIG_SENSORS_IT87) += it87.o
obj-$(CONFIG_SENSORS_JC42) += jc42.o
obj-$(CONFIG_SENSORS_JZ4740) += jz4740-hwmon.o
diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c
index 9140236a0182..34ad5a27a7e9 100644
--- a/drivers/hwmon/acpi_power_meter.c
+++ b/drivers/hwmon/acpi_power_meter.c
@@ -107,15 +107,7 @@ struct acpi_power_meter_resource {
struct kobject *holders_dir;
};
-struct ro_sensor_template {
- char *label;
- ssize_t (*show)(struct device *dev,
- struct device_attribute *devattr,
- char *buf);
- int index;
-};
-
-struct rw_sensor_template {
+struct sensor_template {
char *label;
ssize_t (*show)(struct device *dev,
struct device_attribute *devattr,
@@ -469,52 +461,67 @@ static ssize_t show_name(struct device *dev,
return sprintf(buf, "%s\n", ACPI_POWER_METER_NAME);
}
+#define RO_SENSOR_TEMPLATE(_label, _show, _index) \
+ { \
+ .label = _label, \
+ .show = _show, \
+ .index = _index, \
+ }
+
+#define RW_SENSOR_TEMPLATE(_label, _show, _set, _index) \
+ { \
+ .label = _label, \
+ .show = _show, \
+ .set = _set, \
+ .index = _index, \
+ }
+
/* Sensor descriptions. If you add a sensor, update NUM_SENSORS above! */
-static struct ro_sensor_template meter_ro_attrs[] = {
-{POWER_AVERAGE_NAME, show_power, 0},
-{"power1_accuracy", show_accuracy, 0},
-{"power1_average_interval_min", show_val, 0},
-{"power1_average_interval_max", show_val, 1},
-{"power1_is_battery", show_val, 5},
-{NULL, NULL, 0},
+static struct sensor_template meter_attrs[] = {
+ RO_SENSOR_TEMPLATE(POWER_AVERAGE_NAME, show_power, 0),
+ RO_SENSOR_TEMPLATE("power1_accuracy", show_accuracy, 0),
+ RO_SENSOR_TEMPLATE("power1_average_interval_min", show_val, 0),
+ RO_SENSOR_TEMPLATE("power1_average_interval_max", show_val, 1),
+ RO_SENSOR_TEMPLATE("power1_is_battery", show_val, 5),
+ RW_SENSOR_TEMPLATE(POWER_AVG_INTERVAL_NAME, show_avg_interval,
+ set_avg_interval, 0),
+ {},
};
-static struct rw_sensor_template meter_rw_attrs[] = {
-{POWER_AVG_INTERVAL_NAME, show_avg_interval, set_avg_interval, 0},
-{NULL, NULL, NULL, 0},
+static struct sensor_template misc_cap_attrs[] = {
+ RO_SENSOR_TEMPLATE("power1_cap_min", show_val, 2),
+ RO_SENSOR_TEMPLATE("power1_cap_max", show_val, 3),
+ RO_SENSOR_TEMPLATE("power1_cap_hyst", show_val, 4),
+ RO_SENSOR_TEMPLATE(POWER_ALARM_NAME, show_val, 6),
+ {},
};
-static struct ro_sensor_template misc_cap_attrs[] = {
-{"power1_cap_min", show_val, 2},
-{"power1_cap_max", show_val, 3},
-{"power1_cap_hyst", show_val, 4},
-{POWER_ALARM_NAME, show_val, 6},
-{NULL, NULL, 0},
+static struct sensor_template ro_cap_attrs[] = {
+ RO_SENSOR_TEMPLATE(POWER_CAP_NAME, show_cap, 0),
+ {},
};
-static struct ro_sensor_template ro_cap_attrs[] = {
-{POWER_CAP_NAME, show_cap, 0},
-{NULL, NULL, 0},
+static struct sensor_template rw_cap_attrs[] = {
+ RW_SENSOR_TEMPLATE(POWER_CAP_NAME, show_cap, set_cap, 0),
+ {},
};
-static struct rw_sensor_template rw_cap_attrs[] = {
-{POWER_CAP_NAME, show_cap, set_cap, 0},
-{NULL, NULL, NULL, 0},
+static struct sensor_template trip_attrs[] = {
+ RW_SENSOR_TEMPLATE("power1_average_min", show_val, set_trip, 7),
+ RW_SENSOR_TEMPLATE("power1_average_max", show_val, set_trip, 8),
+ {},
};
-static struct rw_sensor_template trip_attrs[] = {
-{"power1_average_min", show_val, set_trip, 7},
-{"power1_average_max", show_val, set_trip, 8},
-{NULL, NULL, NULL, 0},
+static struct sensor_template misc_attrs[] = {
+ RO_SENSOR_TEMPLATE("name", show_name, 0),
+ RO_SENSOR_TEMPLATE("power1_model_number", show_str, 0),
+ RO_SENSOR_TEMPLATE("power1_oem_info", show_str, 2),
+ RO_SENSOR_TEMPLATE("power1_serial_number", show_str, 1),
+ {},
};
-static struct ro_sensor_template misc_attrs[] = {
-{"name", show_name, 0},
-{"power1_model_number", show_str, 0},
-{"power1_oem_info", show_str, 2},
-{"power1_serial_number", show_str, 1},
-{NULL, NULL, 0},
-};
+#undef RO_SENSOR_TEMPLATE
+#undef RW_SENSOR_TEMPLATE
/* Read power domain data */
static void remove_domain_devices(struct acpi_power_meter_resource *resource)
@@ -619,49 +626,24 @@ end:
}
/* Registration and deregistration */
-static int register_ro_attrs(struct acpi_power_meter_resource *resource,
- struct ro_sensor_template *ro)
+static int register_attrs(struct acpi_power_meter_resource *resource,
+ struct sensor_template *attrs)
{
struct device *dev = &resource->acpi_dev->dev;
struct sensor_device_attribute *sensors =
&resource->sensors[resource->num_sensors];
int res = 0;
- while (ro->label) {
- sensors->dev_attr.attr.name = ro->label;
+ while (attrs->label) {
+ sensors->dev_attr.attr.name = attrs->label;
sensors->dev_attr.attr.mode = S_IRUGO;
- sensors->dev_attr.show = ro->show;
- sensors->index = ro->index;
+ sensors->dev_attr.show = attrs->show;
+ sensors->index = attrs->index;
- sysfs_attr_init(&sensors->dev_attr.attr);
- res = device_create_file(dev, &sensors->dev_attr);
- if (res) {
- sensors->dev_attr.attr.name = NULL;
- goto error;
+ if (attrs->set) {
+ sensors->dev_attr.attr.mode |= S_IWUSR;
+ sensors->dev_attr.store = attrs->set;
}
- sensors++;
- resource->num_sensors++;
- ro++;
- }
-
-error:
- return res;
-}
-
-static int register_rw_attrs(struct acpi_power_meter_resource *resource,
- struct rw_sensor_template *rw)
-{
- struct device *dev = &resource->acpi_dev->dev;
- struct sensor_device_attribute *sensors =
- &resource->sensors[resource->num_sensors];
- int res = 0;
-
- while (rw->label) {
- sensors->dev_attr.attr.name = rw->label;
- sensors->dev_attr.attr.mode = S_IRUGO | S_IWUSR;
- sensors->dev_attr.show = rw->show;
- sensors->dev_attr.store = rw->set;
- sensors->index = rw->index;
sysfs_attr_init(&sensors->dev_attr.attr);
res = device_create_file(dev, &sensors->dev_attr);
@@ -671,7 +653,7 @@ static int register_rw_attrs(struct acpi_power_meter_resource *resource,
}
sensors++;
resource->num_sensors++;
- rw++;
+ attrs++;
}
error:
@@ -703,10 +685,7 @@ static int setup_attrs(struct acpi_power_meter_resource *resource)
return res;
if (resource->caps.flags & POWER_METER_CAN_MEASURE) {
- res = register_ro_attrs(resource, meter_ro_attrs);
- if (res)
- goto error;
- res = register_rw_attrs(resource, meter_rw_attrs);
+ res = register_attrs(resource, meter_attrs);
if (res)
goto error;
}
@@ -718,28 +697,27 @@ static int setup_attrs(struct acpi_power_meter_resource *resource)
goto skip_unsafe_cap;
}
- if (resource->caps.configurable_cap) {
- res = register_rw_attrs(resource, rw_cap_attrs);
- if (res)
- goto error;
- } else {
- res = register_ro_attrs(resource, ro_cap_attrs);
- if (res)
- goto error;
- }
- res = register_ro_attrs(resource, misc_cap_attrs);
+ if (resource->caps.configurable_cap)
+ res = register_attrs(resource, rw_cap_attrs);
+ else
+ res = register_attrs(resource, ro_cap_attrs);
+
+ if (res)
+ goto error;
+
+ res = register_attrs(resource, misc_cap_attrs);
if (res)
goto error;
}
-skip_unsafe_cap:
+skip_unsafe_cap:
if (resource->caps.flags & POWER_METER_CAN_TRIP) {
- res = register_rw_attrs(resource, trip_attrs);
+ res = register_attrs(resource, trip_attrs);
if (res)
goto error;
}
- res = register_ro_attrs(resource, misc_attrs);
+ res = register_attrs(resource, misc_attrs);
if (res)
goto error;
diff --git a/drivers/hwmon/ad7314.c b/drivers/hwmon/ad7314.c
index f85ce70d9677..cfec802cf9ca 100644
--- a/drivers/hwmon/ad7314.c
+++ b/drivers/hwmon/ad7314.c
@@ -18,21 +18,14 @@
#include <linux/hwmon-sysfs.h>
/*
- * AD7314 power mode
- */
-#define AD7314_PD 0x2000
-
-/*
* AD7314 temperature masks
*/
-#define AD7314_TEMP_SIGN 0x200
#define AD7314_TEMP_MASK 0x7FE0
-#define AD7314_TEMP_OFFSET 5
+#define AD7314_TEMP_SHIFT 5
/*
* ADT7301 and ADT7302 temperature masks
*/
-#define ADT7301_TEMP_SIGN 0x2000
#define ADT7301_TEMP_MASK 0x3FFF
enum ad7314_variant {
@@ -73,7 +66,7 @@ static ssize_t ad7314_show_temperature(struct device *dev,
return ret;
switch (spi_get_device_id(chip->spi_dev)->driver_data) {
case ad7314:
- data = (ret & AD7314_TEMP_MASK) >> AD7314_TEMP_OFFSET;
+ data = (ret & AD7314_TEMP_MASK) >> AD7314_TEMP_SHIFT;
data = (data << 6) >> 6;
return sprintf(buf, "%d\n", 250 * data);
diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
index e8e18cab1fb8..6b13f1a4dc27 100644
--- a/drivers/hwmon/fam15h_power.c
+++ b/drivers/hwmon/fam15h_power.c
@@ -257,15 +257,4 @@ static struct pci_driver fam15h_power_driver = {
.remove = __devexit_p(fam15h_power_remove),
};
-static int __init fam15h_power_init(void)
-{
- return pci_register_driver(&fam15h_power_driver);
-}
-
-static void __exit fam15h_power_exit(void)
-{
- pci_unregister_driver(&fam15h_power_driver);
-}
-
-module_init(fam15h_power_init)
-module_exit(fam15h_power_exit)
+module_pci_driver(fam15h_power_driver);
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 @@
+/*
+ * Driver for Texas Instruments INA219, INA226 power monitor chips
+ *
+ * INA219:
+ * Zero Drift Bi-Directional Current/Power Monitor with I2C Interface
+ * Datasheet: http://www.ti.com/product/ina219
+ *
+ * INA226:
+ * Bi-Directional Current/Power Monitor with I2C Interface
+ * Datasheet: http://www.ti.com/product/ina226
+ *
+ * Copyright (C) 2012 Lothar Felten <l-felten@ti.com>
+ * Thanks to Jan Volkering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include <linux/platform_data/ina2xx.h>
+
+/* common register definitions */
+#define INA2XX_CONFIG 0x00
+#define INA2XX_SHUNT_VOLTAGE 0x01 /* readonly */
+#define INA2XX_BUS_VOLTAGE 0x02 /* readonly */
+#define INA2XX_POWER 0x03 /* readonly */
+#define INA2XX_CURRENT 0x04 /* readonly */
+#define INA2XX_CALIBRATION 0x05
+
+/* INA226 register definitions */
+#define INA226_MASK_ENABLE 0x06
+#define INA226_ALERT_LIMIT 0x07
+#define INA226_DIE_ID 0xFF
+
+
+/* register count */
+#define INA219_REGISTERS 6
+#define INA226_REGISTERS 8
+
+#define INA2XX_MAX_REGISTERS 8
+
+/* settings - depend on use case */
+#define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */
+#define INA226_CONFIG_DEFAULT 0x4527 /* averages=16 */
+
+/* worst case is 68.10 ms (~14.6Hz, ina219) */
+#define INA2XX_CONVERSION_RATE 15
+
+enum ina2xx_ids { ina219, ina226 };
+
+struct ina2xx_data {
+ struct device *hwmon_dev;
+
+ struct mutex update_lock;
+ bool valid;
+ unsigned long last_updated;
+
+ int kind;
+ int registers;
+ u16 regs[INA2XX_MAX_REGISTERS];
+};
+
+int ina2xx_read_word(struct i2c_client *client, int reg)
+{
+ int val = i2c_smbus_read_word_data(client, reg);
+ if (unlikely(val < 0)) {
+ dev_dbg(&client->dev,
+ "Failed to read register: %d\n", reg);
+ return val;
+ }
+ return be16_to_cpu(val);
+}
+
+void ina2xx_write_word(struct i2c_client *client, int reg, int data)
+{
+ i2c_smbus_write_word_data(client, reg, cpu_to_be16(data));
+}
+
+static struct ina2xx_data *ina2xx_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ina2xx_data *data = i2c_get_clientdata(client);
+ struct ina2xx_data *ret = data;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated +
+ HZ / INA2XX_CONVERSION_RATE) || !data->valid) {
+
+ int i;
+
+ dev_dbg(&client->dev, "Starting ina2xx update\n");
+
+ /* Read all registers */
+ for (i = 0; i < data->registers; i++) {
+ int rv = ina2xx_read_word(client, i);
+ if (rv < 0) {
+ ret = ERR_PTR(rv);
+ goto abort;
+ }
+ data->regs[i] = rv;
+ }
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+abort:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static int ina219_get_value(struct ina2xx_data *data, u8 reg)
+{
+ /*
+ * calculate exact value for the given register
+ * we assume default power-on reset settings:
+ * bus voltage range 32V
+ * gain = /8
+ * adc 1 & 2 -> conversion time 532uS
+ * mode is continuous shunt and bus
+ * calibration value is INA219_CALIBRATION_VALUE
+ */
+ int val = data->regs[reg];
+
+ switch (reg) {
+ case INA2XX_SHUNT_VOLTAGE:
+ /* LSB=10uV. Convert to mV. */
+ val = DIV_ROUND_CLOSEST(val, 100);
+ break;
+ case INA2XX_BUS_VOLTAGE:
+ /* LSB=4mV. Register is not right aligned, convert to mV. */
+ val = (val >> 3) * 4;
+ break;
+ case INA2XX_POWER:
+ /* LSB=20mW. Convert to uW */
+ val = val * 20 * 1000;
+ break;
+ case INA2XX_CURRENT:
+ /* LSB=1mA (selected). Is in mA */
+ break;
+ default:
+ /* programmer goofed */
+ WARN_ON_ONCE(1);
+ val = 0;
+ break;
+ }
+
+ return val;
+}
+
+static int ina226_get_value(struct ina2xx_data *data, u8 reg)
+{
+ /*
+ * calculate exact value for the given register
+ * we assume default power-on reset settings:
+ * bus voltage range 32V
+ * gain = /8
+ * adc 1 & 2 -> conversion time 532uS
+ * mode is continuous shunt and bus
+ * calibration value is INA226_CALIBRATION_VALUE
+ */
+ int val = data->regs[reg];
+
+ switch (reg) {
+ case INA2XX_SHUNT_VOLTAGE:
+ /* LSB=2.5uV. Convert to mV. */
+ val = DIV_ROUND_CLOSEST(val, 400);
+ break;
+ case INA2XX_BUS_VOLTAGE:
+ /* LSB=1.25mV. Convert to mV. */
+ val = val + DIV_ROUND_CLOSEST(val, 4);
+ break;
+ case INA2XX_POWER:
+ /* LSB=25mW. Convert to uW */
+ val = val * 25 * 1000;
+ break;
+ case INA2XX_CURRENT:
+ /* LSB=1mA (selected). Is in mA */
+ break;
+ default:
+ /* programmer goofed */
+ WARN_ON_ONCE(1);
+ val = 0;
+ break;
+ }
+
+ return val;
+}
+
+static ssize_t ina2xx_show_value(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct ina2xx_data *data = ina2xx_update_device(dev);
+ int value = 0;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ switch (data->kind) {
+ case ina219:
+ value = ina219_get_value(data, attr->index);
+ break;
+ case ina226:
+ value = ina226_get_value(data, attr->index);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ break;
+ }
+ return snprintf(buf, PAGE_SIZE, "%d\n", value);
+}
+
+/* shunt voltage */
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, \
+ ina2xx_show_value, NULL, INA2XX_SHUNT_VOLTAGE);
+
+/* bus voltage */
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, \
+ ina2xx_show_value, NULL, INA2XX_BUS_VOLTAGE);
+
+/* calculated current */
+static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, \
+ ina2xx_show_value, NULL, INA2XX_CURRENT);
+
+/* calculated power */
+static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, \
+ ina2xx_show_value, NULL, INA2XX_POWER);
+
+/* pointers to created device attributes */
+static struct attribute *ina2xx_attributes[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+ &sensor_dev_attr_power1_input.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ina2xx_group = {
+ .attrs = ina2xx_attributes,
+};
+
+static int ina2xx_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct ina2xx_data *data;
+ struct ina2xx_platform_data *pdata;
+ int ret = 0;
+ long shunt = 10000; /* default shunt value 10mOhms */
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (client->dev.platform_data) {
+ pdata =
+ (struct ina2xx_platform_data *)client->dev.platform_data;
+ shunt = pdata->shunt_uohms;
+ }
+
+ if (shunt <= 0)
+ return -ENODEV;
+
+ /* set the device type */
+ data->kind = id->driver_data;
+
+ switch (data->kind) {
+ case ina219:
+ /* device configuration */
+ ina2xx_write_word(client, INA2XX_CONFIG, INA219_CONFIG_DEFAULT);
+
+ /* set current LSB to 1mA, shunt is in uOhms */
+ /* (equation 13 in datasheet) */
+ ina2xx_write_word(client, INA2XX_CALIBRATION, 40960000 / shunt);
+ dev_info(&client->dev,
+ "power monitor INA219 (Rshunt = %li uOhm)\n", shunt);
+ data->registers = INA219_REGISTERS;
+ break;
+ case ina226:
+ /* device configuration */
+ ina2xx_write_word(client, INA2XX_CONFIG, INA226_CONFIG_DEFAULT);
+
+ /* set current LSB to 1mA, shunt is in uOhms */
+ /* (equation 1 in datasheet)*/
+ ina2xx_write_word(client, INA2XX_CALIBRATION, 5120000 / shunt);
+ dev_info(&client->dev,
+ "power monitor INA226 (Rshunt = %li uOhm)\n", shunt);
+ data->registers = INA226_REGISTERS;
+ break;
+ default:
+ /* unknown device id */
+ return -ENODEV;
+ }
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ ret = sysfs_create_group(&client->dev.kobj, &ina2xx_group);
+ if (ret)
+ return ret;
+
+ data->hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ ret = PTR_ERR(data->hwmon_dev);
+ goto out_err_hwmon;
+ }
+
+ return 0;
+
+out_err_hwmon:
+ sysfs_remove_group(&client->dev.kobj, &ina2xx_group);
+ return ret;
+}
+
+static int ina2xx_remove(struct i2c_client *client)
+{
+ struct ina2xx_data *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&client->dev.kobj, &ina2xx_group);
+
+ return 0;
+}
+
+static const struct i2c_device_id ina2xx_id[] = {
+ { "ina219", ina219 },
+ { "ina226", ina226 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ina2xx_id);
+
+static struct i2c_driver ina2xx_driver = {
+ .driver = {
+ .name = "ina2xx",
+ },
+ .probe = ina2xx_probe,
+ .remove = ina2xx_remove,
+ .id_table = ina2xx_id,
+};
+
+static int __init ina2xx_init(void)
+{
+ return i2c_add_driver(&ina2xx_driver);
+}
+
+static void __exit ina2xx_exit(void)
+{
+ i2c_del_driver(&ina2xx_driver);
+}
+
+MODULE_AUTHOR("Lothar Felten <l-felten@ti.com>");
+MODULE_DESCRIPTION("ina2xx driver");
+MODULE_LICENSE("GPL");
+
+module_init(ina2xx_init);
+module_exit(ina2xx_exit);
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index 0b204e4cf51c..e7701d99f8e8 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -19,6 +19,8 @@
* IT8726F Super I/O chip w/LPC interface
* IT8728F Super I/O chip w/LPC interface
* IT8758E Super I/O chip w/LPC interface
+ * IT8782F Super I/O chip w/LPC interface
+ * IT8783E/F Super I/O chip w/LPC interface
* Sis950 A clone of the IT8705F
*
* Copyright (C) 2001 Chris Gauthron
@@ -59,7 +61,8 @@
#define DRVNAME "it87"
-enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728 };
+enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8782,
+ it8783 };
static unsigned short force_id;
module_param(force_id, ushort, 0);
@@ -137,13 +140,18 @@ static inline void superio_exit(void)
#define IT8721F_DEVID 0x8721
#define IT8726F_DEVID 0x8726
#define IT8728F_DEVID 0x8728
+#define IT8782F_DEVID 0x8782
+#define IT8783E_DEVID 0x8783
#define IT87_ACT_REG 0x30
#define IT87_BASE_REG 0x60
/* Logical device 7 registers (IT8712F and later) */
+#define IT87_SIO_GPIO1_REG 0x25
#define IT87_SIO_GPIO3_REG 0x27
#define IT87_SIO_GPIO5_REG 0x29
+#define IT87_SIO_PINX1_REG 0x2a /* Pin selection */
#define IT87_SIO_PINX2_REG 0x2c /* Pin selection */
+#define IT87_SIO_SPI_REG 0xef /* SPI function pin select */
#define IT87_SIO_VID_REG 0xfc /* VID value */
#define IT87_SIO_BEEP_PIN_REG 0xf6 /* Beep pin mapping */
@@ -210,6 +218,7 @@ static const u8 IT87_REG_FANX_MIN[] = { 0x1b, 0x1c, 0x1d, 0x85, 0x87 };
#define IT87_REG_VIN_ENABLE 0x50
#define IT87_REG_TEMP_ENABLE 0x51
+#define IT87_REG_TEMP_EXTRA 0x55
#define IT87_REG_BEEP_ENABLE 0x5c
#define IT87_REG_CHIPID 0x58
@@ -226,9 +235,11 @@ struct it87_sio_data {
u8 beep_pin;
u8 internal; /* Internal sensors can be labeled */
/* Features skipped based on config or DMI */
+ u16 skip_in;
u8 skip_vid;
u8 skip_fan;
u8 skip_pwm;
+ u8 skip_temp;
};
/*
@@ -253,6 +264,7 @@ struct it87_data {
u8 has_fan; /* Bitfield, fans enabled */
u16 fan[5]; /* Register values, possibly combined */
u16 fan_min[5]; /* Register values, possibly combined */
+ u8 has_temp; /* Bitfield, temp sensors enabled */
s8 temp[3]; /* Register value */
s8 temp_high[3]; /* Register value */
s8 temp_low[3]; /* Register value */
@@ -304,31 +316,23 @@ static inline int has_newer_autopwm(const struct it87_data *data)
|| data->type == it8728;
}
-static u8 in_to_reg(const struct it87_data *data, int nr, long val)
+static int adc_lsb(const struct it87_data *data, int nr)
{
- long lsb;
-
- if (has_12mv_adc(data)) {
- if (data->in_scaled & (1 << nr))
- lsb = 24;
- else
- lsb = 12;
- } else
- lsb = 16;
+ int lsb = has_12mv_adc(data) ? 12 : 16;
+ if (data->in_scaled & (1 << nr))
+ lsb <<= 1;
+ return lsb;
+}
- val = DIV_ROUND_CLOSEST(val, lsb);
+static u8 in_to_reg(const struct it87_data *data, int nr, long val)
+{
+ val = DIV_ROUND_CLOSEST(val, adc_lsb(data, nr));
return SENSORS_LIMIT(val, 0, 255);
}
static int in_from_reg(const struct it87_data *data, int nr, int val)
{
- if (has_12mv_adc(data)) {
- if (data->in_scaled & (1 << nr))
- return val * 24;
- else
- return val * 12;
- } else
- return val * 16;
+ return val * adc_lsb(data, nr);
}
static inline u8 FAN_TO_REG(long rpm, int div)
@@ -407,7 +411,9 @@ static inline int has_16bit_fans(const struct it87_data *data)
|| data->type == it8718
|| data->type == it8720
|| data->type == it8721
- || data->type == it8728;
+ || data->type == it8728
+ || data->type == it8782
+ || data->type == it8783;
}
static inline int has_old_autopwm(const struct it87_data *data)
@@ -1369,57 +1375,103 @@ static ssize_t show_name(struct device *dev, struct device_attribute
}
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
-static struct attribute *it87_attributes[] = {
+static struct attribute *it87_attributes_in[9][5] = {
+{
&sensor_dev_attr_in0_input.dev_attr.attr,
- &sensor_dev_attr_in1_input.dev_attr.attr,
- &sensor_dev_attr_in2_input.dev_attr.attr,
- &sensor_dev_attr_in3_input.dev_attr.attr,
- &sensor_dev_attr_in4_input.dev_attr.attr,
- &sensor_dev_attr_in5_input.dev_attr.attr,
- &sensor_dev_attr_in6_input.dev_attr.attr,
- &sensor_dev_attr_in7_input.dev_attr.attr,
- &sensor_dev_attr_in8_input.dev_attr.attr,
&sensor_dev_attr_in0_min.dev_attr.attr,
- &sensor_dev_attr_in1_min.dev_attr.attr,
- &sensor_dev_attr_in2_min.dev_attr.attr,
- &sensor_dev_attr_in3_min.dev_attr.attr,
- &sensor_dev_attr_in4_min.dev_attr.attr,
- &sensor_dev_attr_in5_min.dev_attr.attr,
- &sensor_dev_attr_in6_min.dev_attr.attr,
- &sensor_dev_attr_in7_min.dev_attr.attr,
&sensor_dev_attr_in0_max.dev_attr.attr,
- &sensor_dev_attr_in1_max.dev_attr.attr,
- &sensor_dev_attr_in2_max.dev_attr.attr,
- &sensor_dev_attr_in3_max.dev_attr.attr,
- &sensor_dev_attr_in4_max.dev_attr.attr,
- &sensor_dev_attr_in5_max.dev_attr.attr,
- &sensor_dev_attr_in6_max.dev_attr.attr,
- &sensor_dev_attr_in7_max.dev_attr.attr,
&sensor_dev_attr_in0_alarm.dev_attr.attr,
+ NULL
+}, {
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
&sensor_dev_attr_in1_alarm.dev_attr.attr,
+ NULL
+}, {
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
&sensor_dev_attr_in2_alarm.dev_attr.attr,
+ NULL
+}, {
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
&sensor_dev_attr_in3_alarm.dev_attr.attr,
+ NULL
+}, {
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
&sensor_dev_attr_in4_alarm.dev_attr.attr,
+ NULL
+}, {
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
&sensor_dev_attr_in5_alarm.dev_attr.attr,
+ NULL
+}, {
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
&sensor_dev_attr_in6_alarm.dev_attr.attr,
+ NULL
+}, {
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
&sensor_dev_attr_in7_alarm.dev_attr.attr,
+ NULL
+}, {
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ NULL
+} };
+static const struct attribute_group it87_group_in[9] = {
+ { .attrs = it87_attributes_in[0] },
+ { .attrs = it87_attributes_in[1] },
+ { .attrs = it87_attributes_in[2] },
+ { .attrs = it87_attributes_in[3] },
+ { .attrs = it87_attributes_in[4] },
+ { .attrs = it87_attributes_in[5] },
+ { .attrs = it87_attributes_in[6] },
+ { .attrs = it87_attributes_in[7] },
+ { .attrs = it87_attributes_in[8] },
+};
+
+static struct attribute *it87_attributes_temp[3][6] = {
+{
&sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp2_input.dev_attr.attr,
- &sensor_dev_attr_temp3_input.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
- &sensor_dev_attr_temp2_max.dev_attr.attr,
- &sensor_dev_attr_temp3_max.dev_attr.attr,
&sensor_dev_attr_temp1_min.dev_attr.attr,
- &sensor_dev_attr_temp2_min.dev_attr.attr,
- &sensor_dev_attr_temp3_min.dev_attr.attr,
&sensor_dev_attr_temp1_type.dev_attr.attr,
- &sensor_dev_attr_temp2_type.dev_attr.attr,
- &sensor_dev_attr_temp3_type.dev_attr.attr,
&sensor_dev_attr_temp1_alarm.dev_attr.attr,
+ NULL
+} , {
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_type.dev_attr.attr,
&sensor_dev_attr_temp2_alarm.dev_attr.attr,
+ NULL
+} , {
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_type.dev_attr.attr,
&sensor_dev_attr_temp3_alarm.dev_attr.attr,
+ NULL
+} };
+
+static const struct attribute_group it87_group_temp[3] = {
+ { .attrs = it87_attributes_temp[0] },
+ { .attrs = it87_attributes_temp[1] },
+ { .attrs = it87_attributes_temp[2] },
+};
+static struct attribute *it87_attributes[] = {
&dev_attr_alarms.attr,
&sensor_dev_attr_intrusion0_alarm.dev_attr.attr,
&dev_attr_name.attr,
@@ -1430,7 +1482,7 @@ static const struct attribute_group it87_group = {
.attrs = it87_attributes,
};
-static struct attribute *it87_attributes_beep[] = {
+static struct attribute *it87_attributes_in_beep[] = {
&sensor_dev_attr_in0_beep.dev_attr.attr,
&sensor_dev_attr_in1_beep.dev_attr.attr,
&sensor_dev_attr_in2_beep.dev_attr.attr,
@@ -1439,15 +1491,13 @@ static struct attribute *it87_attributes_beep[] = {
&sensor_dev_attr_in5_beep.dev_attr.attr,
&sensor_dev_attr_in6_beep.dev_attr.attr,
&sensor_dev_attr_in7_beep.dev_attr.attr,
+ NULL
+};
+static struct attribute *it87_attributes_temp_beep[] = {
&sensor_dev_attr_temp1_beep.dev_attr.attr,
&sensor_dev_attr_temp2_beep.dev_attr.attr,
&sensor_dev_attr_temp3_beep.dev_attr.attr,
- NULL
-};
-
-static const struct attribute_group it87_group_beep = {
- .attrs = it87_attributes_beep,
};
static struct attribute *it87_attributes_fan16[5][3+1] = { {
@@ -1651,6 +1701,12 @@ static int __init it87_find(unsigned short *address,
case IT8728F_DEVID:
sio_data->type = it8728;
break;
+ case IT8782F_DEVID:
+ sio_data->type = it8782;
+ break;
+ case IT8783E_DEVID:
+ sio_data->type = it8783;
+ break;
case 0xffff: /* No device at all */
goto exit;
default:
@@ -1686,16 +1742,86 @@ static int __init it87_find(unsigned short *address,
/* The IT8705F has a different LD number for GPIO */
superio_select(5);
sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
+ } else if (sio_data->type == it8783) {
+ int reg25, reg27, reg2A, reg2C, regEF;
+
+ sio_data->skip_vid = 1; /* No VID */
+
+ superio_select(GPIO);
+
+ reg25 = superio_inb(IT87_SIO_GPIO1_REG);
+ reg27 = superio_in