]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge tag 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 23 May 2012 21:15:09 +0000 (14:15 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 23 May 2012 21:15:09 +0000 (14:15 -0700)
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

13 files changed:
Documentation/hwmon/ina2xx [new file with mode: 0644]
Documentation/hwmon/it87
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/acpi_power_meter.c
drivers/hwmon/ad7314.c
drivers/hwmon/fam15h_power.c
drivers/hwmon/ina2xx.c [new file with mode: 0644]
drivers/hwmon/it87.c
drivers/hwmon/k10temp.c
drivers/hwmon/k8temp.c
drivers/hwmon/ntc_thermistor.c
include/linux/platform_data/ina2xx.h [new file with mode: 0644]

diff --git a/Documentation/hwmon/ina2xx b/Documentation/hwmon/ina2xx
new file mode 100644 (file)
index 0000000..f50a6cc
--- /dev/null
@@ -0,0 +1,29 @@
+Kernel driver ina2xx
+====================
+
+Supported chips:
+  * Texas Instruments INA219
+    Prefix: 'ina219'
+    Addresses: I2C 0x40 - 0x4f
+    Datasheet: Publicly available at the Texas Instruments website
+               http://www.ti.com/
+
+  * Texas Instruments INA226
+    Prefix: 'ina226'
+    Addresses: I2C 0x40 - 0x4f
+    Datasheet: Publicly available at the Texas Instruments website
+               http://www.ti.com/
+
+Author: Lothar Felten <l-felten@ti.com>
+
+Description
+-----------
+
+The INA219 is a high-side current shunt and power monitor with an I2C
+interface. The INA219 monitors both shunt drop and supply voltage, with
+programmable conversion times and filtering.
+
+The INA226 is a current shunt and power monitor with an I2C interface.
+The INA226 monitors both a shunt voltage drop and bus supply voltage.
+
+The shunt value in micro-ohms can be set via platform data.
index fba74557168491515001122baa170613f0658bca..87850d86c55995fe895524cf9550c38f8fd90bb0 100644 (file)
@@ -30,6 +30,14 @@ Supported chips:
     Prefix: 'it8728'
     Addresses scanned: from Super I/O config space (8 I/O ports)
     Datasheet: Not publicly available
+  * IT8782F
+    Prefix: 'it8782'
+    Addresses scanned: from Super I/O config space (8 I/O ports)
+    Datasheet: Not publicly available
+  * IT8783E/F
+    Prefix: 'it8783'
+    Addresses scanned: from Super I/O config space (8 I/O ports)
+    Datasheet: Not publicly available
   * SiS950   [clone of IT8705F]
     Prefix: 'it87'
     Addresses scanned: from Super I/O config space (8 I/O ports)
@@ -75,7 +83,8 @@ Description
 -----------
 
 This driver implements support for the IT8705F, IT8712F, IT8716F,
-IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E and SiS950 chips.
+IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E, IT8781F, IT8782F,
+IT8783E/F, and SiS950 chips.
 
 These chips are 'Super I/O chips', supporting floppy disks, infrared ports,
 joysticks and other miscellaneous stuff. For hardware monitoring, they
@@ -99,11 +108,11 @@ The IT8716F, IT8718F, IT8720F, IT8721F/IT8758E and later IT8712F revisions
 have support for 2 additional fans. The additional fans are supported by the
 driver.
 
-The IT8716F, IT8718F, IT8720F and IT8721F/IT8758E, and late IT8712F and
-IT8705F also have optional 16-bit tachometer counters for fans 1 to 3. This
-is better (no more fan clock divider mess) but not compatible with the older
-chips and revisions. The 16-bit tachometer mode is enabled by the driver when
-one of the above chips is detected.
+The IT8716F, IT8718F, IT8720F, IT8721F/IT8758E, IT8782F, IT8783E/F, and late
+IT8712F and IT8705F also have optional 16-bit tachometer counters for fans 1 to
+3. This is better (no more fan clock divider mess) but not compatible with the
+older chips and revisions. The 16-bit tachometer mode is enabled by the driver
+when one of the above chips is detected.
 
 The IT8726F is just bit enhanced IT8716F with additional hardware
 for AMD power sequencing. Therefore the chip will appear as IT8716F
@@ -131,9 +140,10 @@ inputs can measure voltages between 0 and 4.08 volts, with a resolution of
 0.016 volt (except IT8721F/IT8758E and IT8728F: 0.012 volt.) The battery
 voltage in8 does not have limit registers.
 
-On the IT8721F/IT8758E, some voltage inputs are internal and scaled inside
-the chip (in7, in8 and optionally in3). The driver handles this transparently
-so user-space doesn't have to care.
+On the IT8721F/IT8758E, IT8782F, and IT8783E/F, some voltage inputs are
+internal and scaled inside the chip (in7 (optional for IT8782F and IT8783E/F),
+in8 and optionally in3). The driver handles this transparently so user-space
+doesn't have to care.
 
 The VID lines (IT8712F/IT8716F/IT8718F/IT8720F) encode the core voltage value:
 the voltage level your processor should work with. This is hardcoded by
index e466ecba8dc1f7707e022f56973a1fc2d3754289..7cd9bf42108b7368803322d7116c3a8951d30a1b 100644 (file)
@@ -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
index 6d3f11f71815356ba3e7f777a4fb4d48d4d6aeca..e1eeac13b85182389d108c3e65a94aa64fa76222 100644 (file)
@@ -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
index 9140236a0182b4f73d80efbab7d6fdac11aca539..34ad5a27a7e9fcd68b7597f799a99387f421c835 100644 (file)
@@ -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;
 
index f85ce70d96779b5dcd366983f48d4458cc782704..cfec802cf9ca949b705d24a1d862a702af223193 100644 (file)
 #include <linux/hwmon.h>
 #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);
index e8e18cab1fb8c34d63c259a64afb461833df8be7..6b13f1a4dc27c1d9fb48a6d0edc5fb46de83918b 100644 (file)
@@ -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 (file)
index 0000000..7f3f4a3
--- /dev/null
@@ -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);
index 0b204e4cf51c5303836c9254d64f808fd8364115..e7701d99f8e8598207a18de6b6ad7f48a0796073 100644 (file)
@@ -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_inb(IT87_SIO_GPIO3_REG);
+               reg2A = superio_inb(IT87_SIO_PINX1_REG);
+               reg2C = superio_inb(IT87_SIO_PINX2_REG);
+               regEF = superio_inb(IT87_SIO_SPI_REG);
+
+               /* Check if fan3 is there or not */
+               if ((reg27 & (1 << 0)) || !(reg2C & (1 << 2)))
+                       sio_data->skip_fan |= (1 << 2);
+               if ((reg25 & (1 << 4))
+                   || (!(reg2A & (1 << 1)) && (regEF & (1 << 0))))
+                       sio_data->skip_pwm |= (1 << 2);
+
+               /* Check if fan2 is there or not */
+               if (reg27 & (1 << 7))
+                       sio_data->skip_fan |= (1 << 1);
+               if (reg27 & (1 << 3))
+                       sio_data->skip_pwm |= (1 << 1);
+
+               /* VIN5 */
+               if ((reg27 & (1 << 0)) || (reg2C & (1 << 2)))
+                       sio_data->skip_in |= (1 << 5); /* No VIN5 */
+
+               /* VIN6 */
+               if (reg27 & (1 << 1))
+                       sio_data->skip_in |= (1 << 6); /* No VIN6 */
+
+               /*
+                * VIN7
+                * Does not depend on bit 2 of Reg2C, contrary to datasheet.
+                */
+               if (reg27 & (1 << 2)) {
+                       /*
+                        * The data sheet is a bit unclear regarding the
+                        * internal voltage divider for VCCH5V. It says
+                        * "This bit enables and switches VIN7 (pin 91) to the
+                        * internal voltage divider for VCCH5V".
+                        * This is different to other chips, where the internal
+                        * voltage divider would connect VIN7 to an internal
+                        * voltage source. Maybe that is the case here as well.
+                        *
+                        * Since we don't know for sure, re-route it if that is
+                        * not the case, and ask the user to report if the
+                        * resulting voltage is sane.
+                        */
+                       if (!(reg2C & (1 << 1))) {
+                               reg2C |= (1 << 1);
+                               superio_outb(IT87_SIO_PINX2_REG, reg2C);
+                               pr_notice("Routing internal VCCH5V to in7.\n");
+                       }
+                       pr_notice("in7 routed to internal voltage divider, with external pin disabled.\n");
+                       pr_notice("Please report if it displays a reasonable voltage.\n");
+               }
+
+               if (reg2C & (1 << 0))
+                       sio_data->internal |= (1 << 0);
+               if (reg2C & (1 << 1))
+                       sio_data->internal |= (1 << 1);
+
+               sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
+
        } else {
                int reg;
+               bool uart6;
 
                superio_select(GPIO);
 
                reg = superio_inb(IT87_SIO_GPIO3_REG);
-               if (sio_data->type == it8721 || sio_data->type == it8728) {
+               if (sio_data->type == it8721 || sio_data->type == it8728 ||
+                   sio_data->type == it8782) {
                        /*
-                        * The IT8721F/IT8758E doesn't have VID pins at all,
-                        * not sure about the IT8728F.
+                        * IT8721F/IT8758E, and IT8782F don't have VID pins
+                        * at all, not sure about the IT8728F.
                         */
                        sio_data->skip_vid = 1;
                } else {
@@ -1724,6 +1850,9 @@ static int __init it87_find(unsigned short *address,
                        sio_data->vid_value = superio_inb(IT87_SIO_VID_REG);
 
                reg = superio_inb(IT87_SIO_PINX2_REG);
+
+               uart6 = sio_data->type == it8782 && (reg & (1 << 2));
+
                /*
                 * The IT8720F has no VIN7 pin, so VCCH should always be
                 * routed internally to VIN7 with an internal divider.
@@ -1733,8 +1862,12 @@ static int __init it87_find(unsigned short *address,
                 * configured, even though the IT8720F datasheet claims
                 * that the internal routing of VCCH to VIN7 is the default
                 * setting. So we force the internal routing in this case.
+                *
+                * On IT8782F, VIN7 is multiplexed with one of the UART6 pins.
+                * If UART6 is enabled, re-route VIN7 to the internal divider
+                * if that is not already the case.
                 */
-               if (sio_data->type == it8720 && !(reg & (1 << 1))) {
+               if ((sio_data->type == it8720 || uart6) && !(reg & (1 << 1))) {
                        reg |= (1 << 1);
                        superio_outb(IT87_SIO_PINX2_REG, reg);
                        pr_notice("Routing internal VCCH to in7\n");
@@ -1745,6 +1878,20 @@ static int __init it87_find(unsigned short *address,
                    sio_data->type == it8728)
                        sio_data->internal |= (1 << 1);
 
+               /*
+                * On IT8782F, UART6 pins overlap with VIN5, VIN6, and VIN7.
+                * While VIN7 can be routed to the internal voltage divider,
+                * VIN5 and VIN6 are not available if UART6 is enabled.
+                *
+                * Also, temp3 is not available if UART6 is enabled and TEMPIN3
+                * is the temperature source. Since we can not read the
+                * temperature source here, skip_temp is preliminary.
+                */
+               if (uart6) {
+                       sio_data->skip_in |= (1 << 5) | (1 << 6);
+                       sio_data->skip_temp |= (1 << 2);
+               }
+
                sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
        }
        if (sio_data->beep_pin)
@@ -1782,8 +1929,22 @@ static void it87_remove_files(struct device *dev)
        int i;
 
        sysfs_remove_group(&dev->kobj, &it87_group);
-       if (sio_data->beep_pin)
-               sysfs_remove_group(&dev->kobj, &it87_group_beep);
+       for (i = 0; i < 9; i++) {
+               if (sio_data->skip_in & (1 << i))
+                       continue;
+               sysfs_remove_group(&dev->kobj, &it87_group_in[i]);
+               if (it87_attributes_in_beep[i])
+                       sysfs_remove_file(&dev->kobj,
+                                         it87_attributes_in_beep[i]);
+       }
+       for (i = 0; i < 3; i++) {
+               if (!(data->has_temp & (1 << i)))
+                       continue;
+               sysfs_remove_group(&dev->kobj, &it87_group_temp[i]);
+               if (sio_data->beep_pin)
+                       sysfs_remove_file(&dev->kobj,
+                                         it87_attributes_temp_beep[i]);
+       }
        for (i = 0; i < 5; i++) {
                if (!(data->has_fan & (1 << i)))
                        continue;
@@ -1823,22 +1984,22 @@ static int __devinit it87_probe(struct platform_device *pdev)
                "it8720",
                "it8721",
                "it8728",
+               "it8782",
+               "it8783",
        };
 
        res = platform_get_resource(pdev, IORESOURCE_IO, 0);
-       if (!request_region(res->start, IT87_EC_EXTENT, DRVNAME)) {
+       if (!devm_request_region(&pdev->dev, res->start, IT87_EC_EXTENT,
+                                DRVNAME)) {
                dev_err(dev, "Failed to request region 0x%lx-0x%lx\n",
                        (unsigned long)res->start,
                        (unsigned long)(res->start + IT87_EC_EXTENT - 1));
-               err = -EBUSY;
-               goto ERROR0;
+               return -EBUSY;
        }
 
-       data = kzalloc(sizeof(struct it87_data), GFP_KERNEL);
-       if (!data) {
-               err = -ENOMEM;
-               goto ERROR1;
-       }
+       data = devm_kzalloc(&pdev->dev, sizeof(struct it87_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
 
        data->addr = res->start;
        data->type = sio_data->type;
@@ -1847,10 +2008,8 @@ static int __devinit it87_probe(struct platform_device *pdev)
 
        /* Now, we do the remaining detection. */
        if ((it87_read_value(data, IT87_REG_CONFIG) & 0x80)
-        || it87_read_value(data, IT87_REG_CHIPID) != 0x90) {
-               err = -ENODEV;
-               goto ERROR2;
-       }
+        || it87_read_value(data, IT87_REG_CHIPID) != 0x90)
+               return -ENODEV;
 
        platform_set_drvdata(pdev, data);
 
@@ -1867,6 +2026,18 @@ static int __devinit it87_probe(struct platform_device *pdev)
                        data->in_scaled |= (1 << 7);    /* in7 is VSB */
                if (sio_data->internal & (1 << 2))
                        data->in_scaled |= (1 << 8);    /* in8 is Vbat */
+       } else if (sio_data->type == it8782 || sio_data->type == it8783) {
+               if (sio_data->internal & (1 << 0))
+                       data->in_scaled |= (1 << 3);    /* in3 is VCC5V */
+               if (sio_data->internal & (1 << 1))
+                       data->in_scaled |= (1 << 7);    /* in7 is VCCH5V */
+       }
+
+       data->has_temp = 0x07;
+       if (sio_data->skip_temp & (1 << 2)) {
+               if (sio_data->type == it8782
+                   && !(it87_read_value(data, IT87_REG_TEMP_EXTRA) & 0x80))
+                       data->has_temp &= ~(1 << 2);
        }
 
        /* Initialize the IT87 chip */
@@ -1875,12 +2046,34 @@ static int __devinit it87_probe(struct platform_device *pdev)
        /* Register sysfs hooks */
        err = sysfs_create_group(&dev->kobj, &it87_group);
        if (err)
-               goto ERROR2;
+               return err;
 
-       if (sio_data->beep_pin) {
-               err = sysfs_create_group(&dev->kobj, &it87_group_beep);
+       for (i = 0; i < 9; i++) {
+               if (sio_data->skip_in & (1 << i))
+                       continue;
+               err = sysfs_create_group(&dev->kobj, &it87_group_in[i]);
                if (err)
-                       goto ERROR4;
+                       goto error;
+               if (sio_data->beep_pin && it87_attributes_in_beep[i]) {
+                       err = sysfs_create_file(&dev->kobj,
+                                               it87_attributes_in_beep[i]);
+                       if (err)
+                               goto error;
+               }
+       }
+
+       for (i = 0; i < 3; i++) {
+               if (!(data->has_temp & (1 << i)))
+                       continue;
+               err = sysfs_create_group(&dev->kobj, &it87_group_temp[i]);
+               if (err)
+                       goto error;
+               if (sio_data->beep_pin) {
+                       err = sysfs_create_file(&dev->kobj,
+                                               it87_attributes_temp_beep[i]);
+                       if (err)
+                               goto error;
+               }
        }
 
        /* Do not create fan files for disabled fans */
@@ -1891,13 +2084,13 @@ static int __devinit it87_probe(struct platform_device *pdev)
                        continue;
                err = sysfs_create_group(&dev->kobj, &fan_group[i]);
                if (err)
-                       goto ERROR4;
+                       goto error;
 
                if (sio_data->beep_pin) {
                        err = sysfs_create_file(&dev->kobj,
                                                it87_attributes_fan_beep[i]);
                        if (err)
-                               goto ERROR4;
+                               goto error;
                        if (!fan_beep_need_rw)
                                continue;
 
@@ -1922,14 +2115,14 @@ static int __devinit it87_probe(struct platform_device *pdev)
                        err = sysfs_create_group(&dev->kobj,
                                                 &it87_group_pwm[i]);
                        if (err)
-                               goto ERROR4;
+                               goto error;
 
                        if (!has_old_autopwm(data))
                                continue;
                        err = sysfs_create_group(&dev->kobj,
                                                 &it87_group_autopwm[i]);
                        if (err)
-                               goto ERROR4;
+                               goto error;
                }
        }
 
@@ -1939,7 +2132,7 @@ static int __devinit it87_probe(struct platform_device *pdev)
                data->vid = sio_data->vid_value;
                err = sysfs_create_group(&dev->kobj, &it87_group_vid);
                if (err)
-                       goto ERROR4;
+                       goto error;
        }
 
        /* Export labels for internal sensors */
@@ -1949,25 +2142,19 @@ static int __devinit it87_probe(struct platform_device *pdev)
                err = sysfs_create_file(&dev->kobj,
                                        it87_attributes_label[i]);
                if (err)
-                       goto ERROR4;
+                       goto error;
        }
 
        data->hwmon_dev = hwmon_device_register(dev);
        if (IS_ERR(data->hwmon_dev)) {
                err = PTR_ERR(data->hwmon_dev);
-               goto ERROR4;
+               goto error;
        }
 
        return 0;
 
-ERROR4:
+error:
        it87_remove_files(dev);
-ERROR2:
-       platform_set_drvdata(pdev, NULL);
-       kfree(data);
-ERROR1:
-       release_region(res->start, IT87_EC_EXTENT);
-ERROR0:
        return err;
 }
 
@@ -1978,10 +2165,6 @@ static int __devexit it87_remove(struct platform_device *pdev)
        hwmon_device_unregister(data->hwmon_dev);
        it87_remove_files(&pdev->dev);
 
-       release_region(data->addr, IT87_EC_EXTENT);
-       platform_set_drvdata(pdev, NULL);
-       kfree(data);
-
        return 0;
 }
 
@@ -2143,8 +2326,9 @@ static void __devinit it87_init_device(struct platform_device *pdev)
                        it87_write_value(data, IT87_REG_FAN_16BIT,
                                         tmp | 0x07);
                }
-               /* IT8705F only supports three fans. */
-               if (data->type != it87) {
+               /* IT8705F, IT8782F, and IT8783E/F only support three fans. */
+               if (data->type != it87 && data->type != it8782 &&
+                   data->type != it8783) {
                        if (tmp & (1 << 4))
                                data->has_fan |= (1 << 3); /* fan4 enabled */
                        if (tmp & (1 << 5))
@@ -2233,6 +2417,8 @@ static struct it87_data *it87_update_device(struct device *dev)
                        }
                }
                for (i = 0; i < 3; i++) {
+                       if (!(data->has_temp & (1 << i)))
+                               continue;
                        data->temp[i] =
                                it87_read_value(data, IT87_REG_TEMP(i));
                        data->temp_high[i] =
index 307bb325dde952bcc6929ed39d4dd96fc9eca70d..7356b5ec8f6733bd9b4cc9a96f6f790831c60f6b 100644 (file)
@@ -225,15 +225,4 @@ static struct pci_driver k10temp_driver = {
        .remove = __devexit_p(k10temp_remove),
 };
 
-static int __init k10temp_init(void)
-{
-       return pci_register_driver(&k10temp_driver);
-}
-
-static void __exit k10temp_exit(void)
-{
-       pci_unregister_driver(&k10temp_driver);
-}
-
-module_init(k10temp_init)
-module_exit(k10temp_exit)
+module_pci_driver(k10temp_driver);
index 575101988751061eb166f962e222b70ab386ae2f..35aac82ee8eb340e726e696a98ca34a156d76607 100644 (file)
@@ -339,19 +339,8 @@ static struct pci_driver k8temp_driver = {
        .remove = __devexit_p(k8temp_remove),
 };
 
-static int __init k8temp_init(void)
-{
-       return pci_register_driver(&k8temp_driver);
-}
-
-static void __exit k8temp_exit(void)
-{
-       pci_unregister_driver(&k8temp_driver);
-}
+module_pci_driver(k8temp_driver);
 
 MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>");
 MODULE_DESCRIPTION("AMD K8 core temperature monitor");
 MODULE_LICENSE("GPL");
-
-module_init(k8temp_init)
-module_exit(k8temp_exit)
index 9b382ec2c3bd4de978d98a6d9f24f797684fe534..6da9696e182730ac9b7980ad356b2cc19cc01975 100644 (file)
@@ -134,8 +134,7 @@ static inline u64 div64_u64_safe(u64 dividend, u64 divisor)
        return div64_u64(dividend, divisor);
 }
 
-static unsigned int get_ohm_of_thermistor(struct ntc_data *data,
-               unsigned int uV)
+static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uV)
 {
        struct ntc_thermistor_platform_data *pdata = data->pdata;
        u64 mV = uV / 1000;
@@ -146,12 +145,12 @@ static unsigned int get_ohm_of_thermistor(struct ntc_data *data,
 
        if (mV == 0) {
                if (pdata->connect == NTC_CONNECTED_POSITIVE)
-                       return UINT_MAX;
+                       return INT_MAX;
                return 0;
        }
        if (mV >= pmV)
                return (pdata->connect == NTC_CONNECTED_POSITIVE) ?
-                       0 : UINT_MAX;
+                       0 : INT_MAX;
 
        if (pdata->connect == NTC_CONNECTED_POSITIVE && puO == 0)
                N = div64_u64_safe(pdO * (pmV - mV), mV);
@@ -163,113 +162,109 @@ static unsigned int get_ohm_of_thermistor(struct ntc_data *data,
        else
                N = div64_u64_safe(pdO * puO * mV, pdO * (pmV - mV) - puO * mV);
 
-       return (unsigned int) N;
+       if (N > INT_MAX)
+               N = INT_MAX;
+       return N;
 }
 
-static int lookup_comp(struct ntc_data *data,
-               unsigned int ohm, int *i_low, int *i_high)
+static void lookup_comp(struct ntc_data *data, unsigned int ohm,
+                       int *i_low, int *i_high)
 {
-       int start, end, mid = -1;
+       int start, end, mid;
+
+       /*
+        * Handle special cases: Resistance is higher than or equal to
+        * resistance in first table entry, or resistance is lower or equal
+        * to resistance in last table entry.
+        * In these cases, return i_low == i_high, either pointing to the
+        * beginning or to the end of the table depending on the condition.
+        */
+       if (ohm >= data->comp[0].ohm) {
+               *i_low = 0;
+               *i_high = 0;
+               return;
+       }
+       if (ohm <= data->comp[data->n_comp - 1].ohm) {
+               *i_low = data->n_comp - 1;
+               *i_high = data->n_comp - 1;
+               return;
+       }
 
        /* Do a binary search on compensation table */
        start = 0;
        end = data->n_comp;
-
-       while (end > start) {
+       while (start < end) {
                mid = start + (end - start) / 2;
-               if (data->comp[mid].ohm < ohm)
+               /*
+                * start <= mid < end
+                * data->comp[start].ohm > ohm >= data->comp[end].ohm
+                *
+                * We could check for "ohm == data->comp[mid].ohm" here, but
+                * that is a quite unlikely condition, and we would have to
+                * check again after updating start. Check it at the end instead
+                * for simplicity.
+                */
+               if (ohm >= data->comp[mid].ohm) {
                        end = mid;
-               else if (data->comp[mid].ohm > ohm)
-                       start = mid + 1;
-               else
-                       break;
-       }
-
-       if (mid == 0) {
-               if (data->comp[mid].ohm > ohm) {
-                       *i_high = mid;
-                       *i_low = mid + 1;
-                       return 0;
-               } else {
-                       *i_low = mid;
-                       *i_high = -1;
-                       return -EINVAL;
-               }
-       }
-       if (mid == (data->n_comp - 1)) {
-               if (data->comp[mid].ohm <= ohm) {
-                       *i_low = mid;
-                       *i_high = mid - 1;
-                       return 0;
                } else {
-                       *i_low = -1;
-                       *i_high = mid;
-                       return -EINVAL;
+                       start = mid + 1;
+                       /*
+                        * ohm >= data->comp[start].ohm might be true here,
+                        * since we set start to mid + 1. In that case, we are
+                        * done. We could keep going, but the condition is quite
+                        * likely to occur, so it is worth checking for it.
+                        */
+                       if (ohm >= data->comp[start].ohm)
+                               end = start;
                }
+               /*
+                * start <= end
+                * data->comp[start].ohm >= ohm >= data->comp[end].ohm
+                */
        }
-
-       if (data->comp[mid].ohm <= ohm) {
-               *i_low = mid;
-               *i_high = mid - 1;
-       } else {
-               *i_low = mid + 1;
-               *i_high = mid;
-       }
-
-       return 0;
+       /*
+        * start == end
+        * ohm >= data->comp[end].ohm
+        */
+       *i_low = end;
+       if (ohm == data->comp[end].ohm)
+               *i_high = end;
+       else
+               *i_high = end - 1;
 }
 
-static int get_temp_mC(struct ntc_data *data, unsigned int ohm, int *temp)
+static int get_temp_mC(struct ntc_data *data, unsigned int ohm)
 {
        int low, high;
-       int ret;
+       int temp;
 
-       ret = lookup_comp(data, ohm, &low, &high);
-       if (ret) {
+       lookup_comp(data, ohm, &low, &high);
+       if (low == high) {
                /* Unable to use linear approximation */
-               if (low != -1)
-                       *temp = data->comp[low].temp_C * 1000;
-               else if (high != -1)
-                       *temp = data->comp[high].temp_C * 1000;
-               else
-                       return ret;
+               temp = data->comp[low].temp_C * 1000;
        } else {
-               *temp = data->comp[low].temp_C * 1000 +
+               temp = data->comp[low].temp_C * 1000 +
                        ((data->comp[high].temp_C - data->comp[low].temp_C) *
                         1000 * ((int)ohm - (int)data->comp[low].ohm)) /
                        ((int)data->comp[high].ohm - (int)data->comp[low].ohm);
        }
-
-       return 0;
+       return temp;
 }
 
-static int ntc_thermistor_read(struct ntc_data *data, int *temp)
+static int ntc_thermistor_get_ohm(struct ntc_data *data)
 {
-       int ret;
-       int read_ohm, read_uV;
-       unsigned int ohm = 0;
-
-       if (data->pdata->read_ohm) {
-               read_ohm = data->pdata->read_ohm();
-               if (read_ohm < 0)
-                       return read_ohm;
-               ohm = (unsigned int)read_ohm;
-       }
+       int read_uV;
+
+       if (data->pdata->read_ohm)
+               return data->pdata->read_ohm();
 
        if (data->pdata->read_uV) {
                read_uV = data->pdata->read_uV();
                if (read_uV < 0)
                        return read_uV;
-               ohm = get_ohm_of_thermistor(data, (unsigned int)read_uV);
-       }
-
-       ret = get_temp_mC(data, ohm, temp);
-       if (ret) {
-               dev_dbg(data->dev, "Sensor reading function not available.\n");
-               return ret;
+               return get_ohm_of_thermistor(data, read_uV);
        }
-
-       return 0;
+       return -EINVAL;
 }
 
 static ssize_t ntc_show_name(struct device *dev,
@@ -290,12 +285,13 @@ static ssize_t ntc_show_temp(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
        struct ntc_data *data = dev_get_drvdata(dev);
-       int temp, ret;
+       int ohm;
 
-       ret = ntc_thermistor_read(data, &temp);
-       if (ret)
-               return ret;
-       return sprintf(buf, "%d\n", temp);
+       ohm = ntc_thermistor_get_ohm(data);
+       if (ohm < 0)
+               return ohm;
+
+       return sprintf(buf, "%d\n", get_temp_mC(data, ohm));
 }
 
 static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0);
@@ -326,14 +322,14 @@ static int __devinit ntc_thermistor_probe(struct platform_device *pdev)
 
        /* Either one of the two is required. */
        if (!pdata->read_uV && !pdata->read_ohm) {
-               dev_err(&pdev->dev, "Both read_uV and read_ohm missing."
-                               "Need either one of the two.\n");
+               dev_err(&pdev->dev,
+                       "Both read_uV and read_ohm missing. Need either one of the two.\n");
                return -EINVAL;
        }
 
        if (pdata->read_uV && pdata->read_ohm) {
-               dev_warn(&pdev->dev, "Only one of read_uV and read_ohm "
-                               "is needed; ignoring read_uV.\n");
+               dev_warn(&pdev->dev,
+                        "Only one of read_uV and read_ohm is needed; ignoring read_uV.\n");
                pdata->read_uV = NULL;
        }
 
@@ -344,12 +340,12 @@ static int __devinit ntc_thermistor_probe(struct platform_device *pdev)
                                 NTC_CONNECTED_POSITIVE) ||
                                (pdata->connect != NTC_CONNECTED_POSITIVE &&
                                 pdata->connect != NTC_CONNECTED_GROUND))) {
-               dev_err(&pdev->dev, "Required data to use read_uV not "
-                               "supplied.\n");
+               dev_err(&pdev->dev,
+                       "Required data to use read_uV not supplied.\n");
                return -EINVAL;
        }
 
-       data = kzalloc(sizeof(struct ntc_data), GFP_KERNEL);
+       data = devm_kzalloc(&pdev->dev, sizeof(struct ntc_data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
@@ -370,8 +366,7 @@ static int __devinit ntc_thermistor_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n",
                                pdev->id_entry->driver_data,
                                pdev->id_entry->name);
-               ret = -EINVAL;
-               goto err;
+               return -EINVAL;
        }
 
        platform_set_drvdata(pdev, data);
@@ -379,13 +374,13 @@ static int __devinit ntc_thermistor_probe(struct platform_device *pdev)
        ret = sysfs_create_group(&data->dev->kobj, &ntc_attr_group);
        if (ret) {
                dev_err(data->dev, "unable to create sysfs files\n");
-               goto err;
+               return ret;
        }
 
        data->hwmon_dev = hwmon_device_register(data->dev);
-       if (IS_ERR_OR_NULL(data->hwmon_dev)) {
+       if (IS_ERR(data->hwmon_dev)) {
                dev_err(data->dev, "unable to register as hwmon device.\n");
-               ret = -EINVAL;
+               ret = PTR_ERR(data->hwmon_dev);
                goto err_after_sysfs;
        }
 
@@ -395,8 +390,6 @@ static int __devinit ntc_thermistor_probe(struct platform_device *pdev)
        return 0;
 err_after_sysfs:
        sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
-err:
-       kfree(data);
        return ret;
 }
 
@@ -408,8 +401,6 @@ static int __devexit ntc_thermistor_remove(struct platform_device *pdev)
        sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
        platform_set_drvdata(pdev, NULL);
 
-       kfree(data);
-
        return 0;
 }
 
diff --git a/include/linux/platform_data/ina2xx.h b/include/linux/platform_data/ina2xx.h
new file mode 100644 (file)
index 0000000..9abc0ca
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Driver for Texas Instruments INA219, INA226 power monitor chips
+ *
+ * Copyright (C) 2012 Lothar Felten <l-felten@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * For further information, see the Documentation/hwmon/ina2xx file.
+ */
+
+/**
+ * struct ina2xx_platform_data - ina2xx info
+ * @shunt_uohms                shunt resistance in microohms
+ */
+struct ina2xx_platform_data {
+       long shunt_uohms;
+};