Merge tag 'hwmon-for-linus-v4.4' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 2 Nov 2015 19:32:24 +0000 (11:32 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 2 Nov 2015 19:32:24 +0000 (11:32 -0800)
Pull hwmon updates from Guenter Roeck:
 "New driver for MAX31790, added support for TMP75C, as well as cleanups
  and minor improvements in various drivers"

* tag 'hwmon-for-linus-v4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging:
  hwmon: (fam15h_power) Add max compute unit accumulated power
  hwmon: (fam15h_power) Enable power1_input on AMD Carrizo
  hwmon: (fam15h_power) Refactor attributes for dynamically added
  hwmon: (ina2xx) remove no longer used variable 'kind'
  hwmon: (nct6775) Introduce separate temperature labels for NCT6792 and NCT6793
  hwmon: (nct6775) NCT6791D and NCT6792D have an additional temperature source
  hwmon: (ina2xx) give precedence to DT over checking for platform data.
  hwmon: (ina2xx) convert driver to using regmap
  hwmon: (coretemp) Increase limit of maximum core ID from 32 to 128.
  hwmon: (lm75) Add support for TMP75C
  hwmon: (ibmpowernv) Add OF compatibility table entry
  hwmon: (abx500) drop the use of IRQF_NO_SUSPEND
  hwmon: (max31790) Fix dereference of ERR_PTR
  hwmon: Driver for Maxim MAX31790

12 files changed:
Documentation/hwmon/lm75
Documentation/hwmon/max31790 [new file with mode: 0644]
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/abx500.c
drivers/hwmon/coretemp.c
drivers/hwmon/fam15h_power.c
drivers/hwmon/ibmpowernv.c
drivers/hwmon/ina2xx.c
drivers/hwmon/lm75.c
drivers/hwmon/max31790.c [new file with mode: 0644]
drivers/hwmon/nct6775.c

index 67691a0..ac95edf 100644 (file)
@@ -42,8 +42,8 @@ Supported chips:
     Addresses scanned: none
     Datasheet: Publicly available at the ST website
                http://www.st.com/internet/analog/product/121769.jsp
-  * Texas Instruments TMP100, TMP101, TMP105, TMP112, TMP75, TMP175, TMP275
-    Prefixes: 'tmp100', 'tmp101', 'tmp105', 'tmp112', 'tmp175', 'tmp75', 'tmp275'
+  * Texas Instruments TMP100, TMP101, TMP105, TMP112, TMP75, TMP75C, TMP175, TMP275
+    Prefixes: 'tmp100', 'tmp101', 'tmp105', 'tmp112', 'tmp175', 'tmp75', 'tmp75c', 'tmp275'
     Addresses scanned: none
     Datasheet: Publicly available at the Texas Instruments website
                http://www.ti.com/product/tmp100
@@ -51,6 +51,7 @@ Supported chips:
                http://www.ti.com/product/tmp105
                http://www.ti.com/product/tmp112
                http://www.ti.com/product/tmp75
+               http://www.ti.com/product/tmp75c
                http://www.ti.com/product/tmp175
                http://www.ti.com/product/tmp275
   * NXP LM75B
diff --git a/Documentation/hwmon/max31790 b/Documentation/hwmon/max31790
new file mode 100644 (file)
index 0000000..855e624
--- /dev/null
@@ -0,0 +1,37 @@
+Kernel driver max31790
+======================
+
+Supported chips:
+  * Maxim MAX31790
+    Prefix: 'max31790'
+    Addresses scanned: -
+    Datasheet: http://pdfserv.maximintegrated.com/en/ds/MAX31790.pdf
+
+Author: Il Han <corone.il.han@gmail.com>
+
+
+Description
+-----------
+
+This driver implements support for the Maxim MAX31790 chip.
+
+The MAX31790 controls the speeds of up to six fans using six independent
+PWM outputs. The desired fan speeds (or PWM duty cycles) are written
+through the I2C interface. The outputs drive "4-wire" fans directly,
+or can be used to modulate the fan's power terminals using an external
+pass transistor.
+
+Tachometer inputs monitor fan tachometer logic outputs for precise (+/-1%)
+monitoring and control of fan RPM as well as detection of fan failure.
+Six pins are dedicated tachometer inputs. Any of the six PWM outputs can
+also be configured to serve as tachometer inputs.
+
+
+Sysfs entries
+-------------
+
+fan[1-12]_input    RO  fan tachometer speed in RPM
+fan[1-12]_fault    RO  fan experienced fault
+fan[1-6]_target    RW  desired fan speed in RPM
+pwm[1-6]_enable    RW  regulator mode, 0=disabled, 1=manual mode, 2=rpm mode
+pwm[1-6]           RW  fan target duty cycle (0-255)
index e13c902..796569e 100644 (file)
@@ -840,6 +840,16 @@ config SENSORS_MAX6697
          This driver can also be built as a module.  If so, the module
          will be called max6697.
 
+config SENSORS_MAX31790
+       tristate "Maxim MAX31790 sensor chip"
+       depends on I2C
+       help
+         If you say yes here you get support for 6-Channel PWM-Output
+         Fan RPM Controller.
+
+         This driver can also be built as a module.  If so, the module
+         will be called max31790.
+
 config SENSORS_HTU21
        tristate "Measurement Specialties HTU21D humidity/temperature sensors"
        depends on I2C
index 9e0f3dd..01855ee 100644 (file)
@@ -115,6 +115,7 @@ obj-$(CONFIG_SENSORS_MAX6639)       += max6639.o
 obj-$(CONFIG_SENSORS_MAX6642)  += max6642.o
 obj-$(CONFIG_SENSORS_MAX6650)  += max6650.o
 obj-$(CONFIG_SENSORS_MAX6697)  += max6697.o
+obj-$(CONFIG_SENSORS_MAX31790) += max31790.o
 obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
 obj-$(CONFIG_SENSORS_MCP3021)  += mcp3021.o
 obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
index 1fd4685..d87cae8 100644 (file)
@@ -377,7 +377,7 @@ static int setup_irqs(struct platform_device *pdev)
        }
 
        ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
-               abx500_temp_irq_handler, IRQF_NO_SUSPEND, "abx500-temp", pdev);
+               abx500_temp_irq_handler, 0, "abx500-temp", pdev);
        if (ret < 0)
                dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret);
 
index 3e03379..6a27eb2 100644 (file)
@@ -52,7 +52,7 @@ module_param_named(tjmax, force_tjmax, int, 0444);
 MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
 
 #define BASE_SYSFS_ATTR_NO     2       /* Sysfs Base attr no for coretemp */
-#define NUM_REAL_CORES         32      /* Number of Real cores per cpu */
+#define NUM_REAL_CORES         128     /* Number of Real cores per cpu */
 #define CORETEMP_NAME_LENGTH   19      /* String Length of attrs */
 #define MAX_CORE_ATTRS         4       /* Maximum no of basic attrs */
 #define TOTAL_ATTRS            (MAX_CORE_ATTRS + 1)
index e80ee23..5f7067d 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/pci.h>
 #include <linux/bitops.h>
 #include <asm/processor.h>
+#include <asm/msr.h>
 
 MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
 MODULE_AUTHOR("Andreas Herrmann <herrmann.der.user@googlemail.com>");
@@ -41,12 +42,21 @@ MODULE_LICENSE("GPL");
 #define REG_TDP_RUNNING_AVERAGE                0xe0
 #define REG_TDP_LIMIT3                 0xe8
 
+#define FAM15H_MIN_NUM_ATTRS           2
+#define FAM15H_NUM_GROUPS              2
+
+#define MSR_F15H_CU_MAX_PWR_ACCUMULATOR        0xc001007b
+
 struct fam15h_power_data {
        struct pci_dev *pdev;
        unsigned int tdp_to_watts;
        unsigned int base_tdp;
        unsigned int processor_pwr_watts;
        unsigned int cpu_pwr_sample_ratio;
+       const struct attribute_group *groups[FAM15H_NUM_GROUPS];
+       struct attribute_group group;
+       /* maximum accumulated power of a compute unit */
+       u64 max_cu_acc_power;
 };
 
 static ssize_t show_power(struct device *dev,
@@ -105,29 +115,36 @@ static ssize_t show_power_crit(struct device *dev,
 }
 static DEVICE_ATTR(power1_crit, S_IRUGO, show_power_crit, NULL);
 
-static umode_t fam15h_power_is_visible(struct kobject *kobj,
-                                      struct attribute *attr,
-                                      int index)
+static int fam15h_power_init_attrs(struct pci_dev *pdev,
+                                  struct fam15h_power_data *data)
 {
-       /* power1_input is only reported for Fam15h, Models 00h-0fh */
-       if (attr == &dev_attr_power1_input.attr &&
-          (boot_cpu_data.x86 != 0x15 || boot_cpu_data.x86_model > 0xf))
-               return 0;
+       int n = FAM15H_MIN_NUM_ATTRS;
+       struct attribute **fam15h_power_attrs;
+       struct cpuinfo_x86 *c = &boot_cpu_data;
 
-       return attr->mode;
-}
+       if (c->x86 == 0x15 &&
+           (c->x86_model <= 0xf ||
+            (c->x86_model >= 0x60 && c->x86_model <= 0x6f)))
+               n += 1;
 
-static struct attribute *fam15h_power_attrs[] = {
-       &dev_attr_power1_input.attr,
-       &dev_attr_power1_crit.attr,
-       NULL
-};
+       fam15h_power_attrs = devm_kcalloc(&pdev->dev, n,
+                                         sizeof(*fam15h_power_attrs),
+                                         GFP_KERNEL);
 
-static const struct attribute_group fam15h_power_group = {
-       .attrs = fam15h_power_attrs,
-       .is_visible = fam15h_power_is_visible,
-};
-__ATTRIBUTE_GROUPS(fam15h_power);
+       if (!fam15h_power_attrs)
+               return -ENOMEM;
+
+       n = 0;
+       fam15h_power_attrs[n++] = &dev_attr_power1_crit.attr;
+       if (c->x86 == 0x15 &&
+           (c->x86_model <= 0xf ||
+            (c->x86_model >= 0x60 && c->x86_model <= 0x6f)))
+               fam15h_power_attrs[n++] = &dev_attr_power1_input.attr;
+
+       data->group.attrs = fam15h_power_attrs;
+
+       return 0;
+}
 
 static bool should_load_on_this_node(struct pci_dev *f4)
 {
@@ -186,11 +203,12 @@ static int fam15h_power_resume(struct pci_dev *pdev)
 #define fam15h_power_resume NULL
 #endif
 
-static void fam15h_power_init_data(struct pci_dev *f4,
-                                            struct fam15h_power_data *data)
+static int fam15h_power_init_data(struct pci_dev *f4,
+                                 struct fam15h_power_data *data)
 {
        u32 val, eax, ebx, ecx, edx;
        u64 tmp;
+       int ret;
 
        pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
        data->base_tdp = val >> 16;
@@ -211,11 +229,15 @@ static void fam15h_power_init_data(struct pci_dev *f4,
        /* convert to microWatt */
        data->processor_pwr_watts = (tmp * 15625) >> 10;
 
+       ret = fam15h_power_init_attrs(f4, data);
+       if (ret)
+               return ret;
+
        cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
 
        /* CPUID Fn8000_0007:EDX[12] indicates to support accumulated power */
        if (!(edx & BIT(12)))
-               return;
+               return 0;
 
        /*
         * determine the ratio of the compute unit power accumulator
@@ -223,14 +245,24 @@ static void fam15h_power_init_data(struct pci_dev *f4,
         * Fn8000_0007:ECX
         */
        data->cpu_pwr_sample_ratio = ecx;
+
+       if (rdmsrl_safe(MSR_F15H_CU_MAX_PWR_ACCUMULATOR, &tmp)) {
+               pr_err("Failed to read max compute unit power accumulator MSR\n");
+               return -ENODEV;
+       }
+
+       data->max_cu_acc_power = tmp;
+
+       return 0;
 }
 
 static int fam15h_power_probe(struct pci_dev *pdev,
-                                       const struct pci_device_id *id)
+                             const struct pci_device_id *id)
 {
        struct fam15h_power_data *data;
        struct device *dev = &pdev->dev;
        struct device *hwmon_dev;
+       int ret;
 
        /*
         * though we ignore every other northbridge, we still have to
@@ -246,12 +278,17 @@ static int fam15h_power_probe(struct pci_dev *pdev,
        if (!data)
                return -ENOMEM;
 
-       fam15h_power_init_data(pdev, data);
+       ret = fam15h_power_init_data(pdev, data);
+       if (ret)
+               return ret;
+
        data->pdev = pdev;
 
+       data->groups[0] = &data->group;
+
        hwmon_dev = devm_hwmon_device_register_with_groups(dev, "fam15h_power",
                                                           data,
-                                                          fam15h_power_groups);
+                                                          &data->groups[0]);
        return PTR_ERR_OR_ZERO(hwmon_dev);
 }
 
index 4255514..55b5a8f 100644 (file)
@@ -474,11 +474,18 @@ static const struct platform_device_id opal_sensor_driver_ids[] = {
 };
 MODULE_DEVICE_TABLE(platform, opal_sensor_driver_ids);
 
+static const struct of_device_id opal_sensor_match[] = {
+       { .compatible   = "ibm,opal-sensor" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, opal_sensor_match);
+
 static struct platform_driver ibmpowernv_driver = {
        .probe          = ibmpowernv_probe,
        .id_table       = opal_sensor_driver_ids,
        .driver         = {
                .name   = DRVNAME,
+               .of_match_table = opal_sensor_match,
        },
 };
 
index 4d28150..b24f1d3 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/of.h>
 #include <linux/delay.h>
 #include <linux/util_macros.h>
+#include <linux/regmap.h>
 
 #include <linux/platform_data/ina2xx.h>
 
  */
 #define INA226_TOTAL_CONV_TIME_DEFAULT 2200
 
+static struct regmap_config ina2xx_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 16,
+};
+
 enum ina2xx_ids { ina219, ina226 };
 
 struct ina2xx_config {
@@ -97,20 +103,13 @@ struct ina2xx_config {
 };
 
 struct ina2xx_data {
-       struct i2c_client *client;
        const struct ina2xx_config *config;
 
        long rshunt;
-       u16 curr_config;
-
-       struct mutex update_lock;
-       bool valid;
-       unsigned long last_updated;
-       int update_interval; /* in jiffies */
+       struct mutex config_lock;
+       struct regmap *regmap;
 
-       int kind;
        const struct attribute_group *groups[INA2XX_MAX_ATTRIBUTE_GROUPS];
-       u16 regs[INA2XX_MAX_REGISTERS];
 };
 
 static const struct ina2xx_config ina2xx_config[] = {
@@ -153,7 +152,11 @@ static int ina226_reg_to_interval(u16 config)
        return DIV_ROUND_CLOSEST(avg * INA226_TOTAL_CONV_TIME_DEFAULT, 1000);
 }
 
-static u16 ina226_interval_to_reg(int interval, u16 config)
+/*
+ * Return the new, shifted AVG field value of CONFIG register,
+ * to use with regmap_update_bits
+ */
+static u16 ina226_interval_to_reg(int interval)
 {
        int avg, avg_bits;
 
@@ -162,15 +165,7 @@ static u16 ina226_interval_to_reg(int interval, u16 config)
        avg_bits = find_closest(avg, ina226_avg_tab,
                                ARRAY_SIZE(ina226_avg_tab));
 
-       return (config & ~INA226_AVG_RD_MASK) | INA226_SHIFT_AVG(avg_bits);
-}
-
-static void ina226_set_update_interval(struct ina2xx_data *data)
-{
-       int ms;
-
-       ms = ina226_reg_to_interval(data->curr_config);
-       data->update_interval = msecs_to_jiffies(ms);
+       return INA226_SHIFT_AVG(avg_bits);
 }
 
 static int ina2xx_calibrate(struct ina2xx_data *data)
@@ -178,8 +173,7 @@ static int ina2xx_calibrate(struct ina2xx_data *data)
        u16 val = DIV_ROUND_CLOSEST(data->config->calibration_factor,
                                    data->rshunt);
 
-       return i2c_smbus_write_word_swapped(data->client,
-                                           INA2XX_CALIBRATION, val);
+       return regmap_write(data->regmap, INA2XX_CALIBRATION, val);
 }
 
 /*
@@ -187,12 +181,8 @@ static int ina2xx_calibrate(struct ina2xx_data *data)
  */
 static int ina2xx_init(struct ina2xx_data *data)
 {
-       struct i2c_client *client = data->client;
-       int ret;
-
-       /* device configuration */
-       ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG,
-                                          data->curr_config);
+       int ret = regmap_write(data->regmap, INA2XX_CONFIG,
+                              data->config->config_default);
        if (ret < 0)
                return ret;
 
@@ -203,47 +193,52 @@ static int ina2xx_init(struct ina2xx_data *data)
        return ina2xx_calibrate(data);
 }
 
-static int ina2xx_do_update(struct device *dev)
+static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval)
 {
        struct ina2xx_data *data = dev_get_drvdata(dev);
-       struct i2c_client *client = data->client;
-       int i, rv, retry;
+       int ret, retry;
 
-       dev_dbg(&client->dev, "Starting ina2xx update\n");
+       dev_dbg(dev, "Starting register %d read\n", reg);
 
        for (retry = 5; retry; retry--) {
-               /* Read all registers */
-               for (i = 0; i < data->config->registers; i++) {
-                       rv = i2c_smbus_read_word_swapped(client, i);
-                       if (rv < 0)
-                               return rv;
-                       data->regs[i] = rv;
-               }
+
+               ret = regmap_read(data->regmap, reg, regval);
+               if (ret < 0)
+                       return ret;
+
+               dev_dbg(dev, "read %d, val = 0x%04x\n", reg, *regval);
 
                /*
                 * If the current value in the calibration register is 0, the
                 * power and current registers will also remain at 0. In case
                 * the chip has been reset let's check the calibration
                 * register and reinitialize if needed.
+                * We do that extra read of the calibration register if there
+                * is some hint of a chip reset.
                 */
-               if (data->regs[INA2XX_CALIBRATION] == 0) {
-                       dev_warn(dev, "chip not calibrated, reinitializing\n");
-
-                       rv = ina2xx_init(data);
-                       if (rv < 0)
-                               return rv;
-
-                       /*
-                        * Let's make sure the power and current registers
-                        * have been updated before trying again.
-                        */
-                       msleep(INA2XX_MAX_DELAY);
-                       continue;
+               if (*regval == 0) {
+                       unsigned int cal;
+
+                       ret = regmap_read(data->regmap, INA2XX_CALIBRATION,
+                                         &cal);
+                       if (ret < 0)
+                               return ret;
+
+                       if (cal == 0) {
+                               dev_warn(dev, "chip not calibrated, reinitializing\n");
+
+                               ret = ina2xx_init(data);
+                               if (ret < 0)
+                                       return ret;
+                               /*
+                                * Let's make sure the power and current
+                                * registers have been updated before trying
+                                * again.
+                                */
+                               msleep(INA2XX_MAX_DELAY);
+                               continue;
+                       }
                }
-
-               data->last_updated = jiffies;
-               data->valid = 1;
-
                return 0;
        }
 
@@ -256,51 +251,31 @@ static int ina2xx_do_update(struct device *dev)
        return -ENODEV;
 }
 
-static struct ina2xx_data *ina2xx_update_device(struct device *dev)
-{
-       struct ina2xx_data *data = dev_get_drvdata(dev);
-       struct ina2xx_data *ret = data;
-       unsigned long after;
-       int rv;
-
-       mutex_lock(&data->update_lock);
-
-       after = data->last_updated + data->update_interval;
-       if (time_after(jiffies, after) || !data->valid) {
-               rv = ina2xx_do_update(dev);
-               if (rv < 0)
-                       ret = ERR_PTR(rv);
-       }
-
-       mutex_unlock(&data->update_lock);
-       return ret;
-}
-
-static int ina2xx_get_value(struct ina2xx_data *data, u8 reg)
+static int ina2xx_get_value(struct ina2xx_data *data, u8 reg,
+                           unsigned int regval)
 {
        int val;
 
        switch (reg) {
        case INA2XX_SHUNT_VOLTAGE:
                /* signed register */
-               val = DIV_ROUND_CLOSEST((s16)data->regs[reg],
-                                       data->config->shunt_div);
+               val = DIV_ROUND_CLOSEST((s16)regval, data->config->shunt_div);
                break;
        case INA2XX_BUS_VOLTAGE:
-               val = (data->regs[reg] >> data->config->bus_voltage_shift)
+               val = (regval >> data->config->bus_voltage_shift)
                  * data->config->bus_voltage_lsb;
                val = DIV_ROUND_CLOSEST(val, 1000);
                break;
        case INA2XX_POWER:
-               val = data->regs[reg] * data->config->power_lsb;
+               val = regval * data->config->power_lsb;
                break;
        case INA2XX_CURRENT:
                /* signed register, LSB=1mA (selected), in mA */
-               val = (s16)data->regs[reg];
+               val = (s16)regval;
                break;
        case INA2XX_CALIBRATION:
                val = DIV_ROUND_CLOSEST(data->config->calibration_factor,
-                                       data->regs[reg]);
+                                       regval);
                break;
        default:
                /* programmer goofed */
@@ -316,25 +291,25 @@ 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);
+       struct ina2xx_data *data = dev_get_drvdata(dev);
+       unsigned int regval;
+
+       int err = ina2xx_read_reg(dev, attr->index, &regval);
 
-       if (IS_ERR(data))
-               return PTR_ERR(data);
+       if (err < 0)
+               return err;
 
        return snprintf(buf, PAGE_SIZE, "%d\n",
-                       ina2xx_get_value(data, attr->index));
+                       ina2xx_get_value(data, attr->index, regval));
 }
 
 static ssize_t ina2xx_set_shunt(struct device *dev,
                                struct device_attribute *da,
                                const char *buf, size_t count)
 {
-       struct ina2xx_data *data = ina2xx_update_device(dev);
        unsigned long val;
        int status;
-
-       if (IS_ERR(data))
-               return PTR_ERR(data);
+       struct ina2xx_data *data = dev_get_drvdata(dev);
 
        status = kstrtoul(buf, 10, &val);
        if (status < 0)
@@ -345,10 +320,10 @@ static ssize_t ina2xx_set_shunt(struct device *dev,
            val > data->config->calibration_factor)
                return -EINVAL;
 
-       mutex_lock(&data->update_lock);
+       mutex_lock(&data->config_lock);
        data->rshunt = val;
        status = ina2xx_calibrate(data);
-       mutex_unlock(&data->update_lock);
+       mutex_unlock(&data->config_lock);
        if (status < 0)
                return status;
 
@@ -370,17 +345,9 @@ static ssize_t ina226_set_interval(struct device *dev,
        if (val > INT_MAX || val == 0)
                return -EINVAL;
 
-       mutex_lock(&data->update_lock);
-       data->curr_config = ina226_interval_to_reg(val,
-                                                  data->regs[INA2XX_CONFIG]);
-       status = i2c_smbus_write_word_swapped(data->client,
-                                             INA2XX_CONFIG,
-                                             data->curr_config);
-
-       ina226_set_update_interval(data);
-       /* Make sure the next access re-reads all registers. */
-       data->valid = 0;
-       mutex_unlock(&data->update_lock);
+       status = regmap_update_bits(data->regmap, INA2XX_CONFIG,
+                                   INA226_AVG_RD_MASK,
+                                   ina226_interval_to_reg(val));
        if (status < 0)
                return status;
 
@@ -390,18 +357,15 @@ static ssize_t ina226_set_interval(struct device *dev,
 static ssize_t ina226_show_interval(struct device *dev,
                                    struct device_attribute *da, char *buf)
 {
-       struct ina2xx_data *data = ina2xx_update_device(dev);
+       struct ina2xx_data *data = dev_get_drvdata(dev);
+       int status;
+       unsigned int regval;
 
-       if (IS_ERR(data))
-               return PTR_ERR(data);
+       status = regmap_read(data->regmap, INA2XX_CONFIG, &regval);
+       if (status)
+               return status;
 
-       /*
-        * We don't use data->update_interval here as we want to display
-        * the actual interval used by the chip and jiffies_to_msecs()
-        * doesn't seem to be accurate enough.
-        */
-       return snprintf(buf, PAGE_SIZE, "%d\n",
-                       ina226_reg_to_interval(data->regs[INA2XX_CONFIG]));
+       return snprintf(buf, PAGE_SIZE, "%d\n", ina226_reg_to_interval(regval));
 }
 
 /* shunt voltage */
@@ -455,60 +419,51 @@ static const struct attribute_group ina226_group = {
 static int ina2xx_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
 {
-       struct i2c_adapter *adapter = client->adapter;
-       struct ina2xx_platform_data *pdata;
        struct device *dev = &client->dev;
        struct ina2xx_data *data;
        struct device *hwmon_dev;
        u32 val;
        int ret, group = 0;
 
-       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
-               return -ENODEV;
-
        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
-       if (dev_get_platdata(dev)) {
-               pdata = dev_get_platdata(dev);
-               data->rshunt = pdata->shunt_uohms;
-       } else if (!of_property_read_u32(dev->of_node,
-                                        "shunt-resistor", &val)) {
-               data->rshunt = val;
-       } else {
-               data->rshunt = INA2XX_RSHUNT_DEFAULT;
-       }
-
        /* set the device type */
-       data->kind = id->driver_data;
-       data->config = &ina2xx_config[data->kind];
-       data->curr_config = data->config->config_default;
-       data->client = client;
+       data->config = &ina2xx_config[id->driver_data];
 
-       /*
-        * Ina226 has a variable update_interval. For ina219 we
-        * use a constant value.
-        */
-       if (data->kind == ina226)
-               ina226_set_update_interval(data);
-       else
-               data->update_interval = HZ / INA2XX_CONVERSION_RATE;
+       if (of_property_read_u32(dev->of_node, "shunt-resistor", &val) < 0) {
+               struct ina2xx_platform_data *pdata = dev_get_platdata(dev);
+
+               if (pdata)
+                       val = pdata->shunt_uohms;
+               else
+                       val = INA2XX_RSHUNT_DEFAULT;
+       }
 
-       if (data->rshunt <= 0 ||
-           data->rshunt > data->config->calibration_factor)
+       if (val <= 0 || val > data->config->calibration_factor)
                return -ENODEV;
 
+       data->rshunt = val;
+
+       ina2xx_regmap_config.max_register = data->config->registers;
+
+       data->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config);
+       if (IS_ERR(data->regmap)) {
+               dev_err(dev, "failed to allocate register map\n");
+               return PTR_ERR(data->regmap);
+       }
+
        ret = ina2xx_init(data);
        if (ret < 0) {
                dev_err(dev, "error configuring the device: %d\n", ret);
                return -ENODEV;
        }
 
-       mutex_init(&data->update_lock);
+       mutex_init(&data->config_lock);
 
        data->groups[group++] = &ina2xx_group;
-       if (data->kind == ina226)
+       if (id->driver_data == ina226)
                data->groups[group++] = &ina226_group;
 
        hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
index e4e57bb..0addc84 100644 (file)
@@ -57,6 +57,7 @@ enum lm75_type {              /* keep sorted in alphabetical order */
        tmp175,
        tmp275,
        tmp75,
+       tmp75c,
 };
 
 /* Addresses scanned */
@@ -280,6 +281,11 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
                data->resolution = 12;
                data->sample_time = HZ / 2;
                break;
+       case tmp75c:
+               clr_mask |= 1 << 5;             /* not one-shot mode */
+               data->resolution = 12;
+               data->sample_time = HZ / 4;
+               break;
        }
 
        /* configure as specified */
@@ -343,6 +349,7 @@ static const struct i2c_device_id lm75_ids[] = {
        { "tmp175", tmp175, },
        { "tmp275", tmp275, },
        { "tmp75", tmp75, },
+       { "tmp75c", tmp75c, },
        { /* LIST END */ }
 };
 MODULE_DEVICE_TABLE(i2c, lm75_ids);
diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c
new file mode 100644 (file)
index 0000000..69c0ac8
--- /dev/null
@@ -0,0 +1,603 @@
+/*
+ * max31790.c - Part of lm_sensors, Linux kernel modules for hardware
+ *             monitoring.
+ *
+ * (C) 2015 by Il Han <corone.il.han@gmail.com>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+/* MAX31790 registers */
+#define MAX31790_REG_GLOBAL_CONFIG     0x00
+#define MAX31790_REG_FAN_CONFIG(ch)    (0x02 + (ch))
+#define MAX31790_REG_FAN_DYNAMICS(ch)  (0x08 + (ch))
+#define MAX31790_REG_FAN_FAULT_STATUS2 0x10
+#define MAX31790_REG_FAN_FAULT_STATUS1 0x11
+#define MAX31790_REG_TACH_COUNT(ch)    (0x18 + (ch) * 2)
+#define MAX31790_REG_PWM_DUTY_CYCLE(ch)        (0x30 + (ch) * 2)
+#define MAX31790_REG_PWMOUT(ch)                (0x40 + (ch) * 2)
+#define MAX31790_REG_TARGET_COUNT(ch)  (0x50 + (ch) * 2)
+
+/* Fan Config register bits */
+#define MAX31790_FAN_CFG_RPM_MODE      0x80
+#define MAX31790_FAN_CFG_TACH_INPUT_EN 0x08
+#define MAX31790_FAN_CFG_TACH_INPUT    0x01
+
+/* Fan Dynamics register bits */
+#define MAX31790_FAN_DYN_SR_SHIFT      5
+#define MAX31790_FAN_DYN_SR_MASK       0xE0
+#define SR_FROM_REG(reg)               (((reg) & MAX31790_FAN_DYN_SR_MASK) \
+                                        >> MAX31790_FAN_DYN_SR_SHIFT)
+
+#define FAN_RPM_MIN                    120
+#define FAN_RPM_MAX                    7864320
+
+#define RPM_FROM_REG(reg, sr)          (((reg) >> 4) ? \
+                                        ((60 * (sr) * 8192) / ((reg) >> 4)) : \
+                                        FAN_RPM_MAX)
+#define RPM_TO_REG(rpm, sr)            ((60 * (sr) * 8192) / ((rpm) * 2))
+
+#define NR_CHANNEL                     6
+
+/*
+ * Client data (each client gets its own)
+ */
+struct max31790_data {
+       struct i2c_client *client;
+       struct mutex update_lock;
+       bool valid; /* zero until following fields are valid */
+       unsigned long last_updated; /* in jiffies */
+
+       /* register values */
+       u8 fan_config[NR_CHANNEL];
+       u8 fan_dynamics[NR_CHANNEL];
+       u16 fault_status;
+       u16 tach[NR_CHANNEL * 2];
+       u16 pwm[NR_CHANNEL];
+       u16 target_count[NR_CHANNEL];
+};
+
+static struct max31790_data *max31790_update_device(struct device *dev)
+{
+       struct max31790_data *data = dev_get_drvdata(dev);
+       struct i2c_client *client = data->client;
+       struct max31790_data *ret = data;
+       int i;
+       int rv;
+
+       mutex_lock(&data->update_lock);
+
+       if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+               rv = i2c_smbus_read_byte_data(client,
+                               MAX31790_REG_FAN_FAULT_STATUS1);
+               if (rv < 0)
+                       goto abort;
+               data->fault_status = rv & 0x3F;
+
+               rv = i2c_smbus_read_byte_data(client,
+                               MAX31790_REG_FAN_FAULT_STATUS2);
+               if (rv < 0)
+                       goto abort;
+               data->fault_status |= (rv & 0x3F) << 6;
+
+               for (i = 0; i < NR_CHANNEL; i++) {
+                       rv = i2c_smbus_read_word_swapped(client,
+                                       MAX31790_REG_TACH_COUNT(i));
+                       if (rv < 0)
+                               goto abort;
+                       data->tach[i] = rv;
+
+                       if (data->fan_config[i]
+                           & MAX31790_FAN_CFG_TACH_INPUT) {
+                               rv = i2c_smbus_read_word_swapped(client,
+                                       MAX31790_REG_TACH_COUNT(NR_CHANNEL
+                                                               + i));
+                               if (rv < 0)
+                                       goto abort;
+                               data->tach[NR_CHANNEL + i] = rv;
+                       } else {
+                               rv = i2c_smbus_read_word_swapped(client,
+                                               MAX31790_REG_PWMOUT(i));
+                               if (rv < 0)
+                                       goto abort;
+                               data->pwm[i] = rv;
+
+                               rv = i2c_smbus_read_word_swapped(client,
+                                               MAX31790_REG_TARGET_COUNT(i));
+                               if (rv < 0)
+                                       goto abort;
+                               data->target_count[i] = rv;
+                       }
+               }
+
+               data->last_updated = jiffies;
+               data->valid = true;
+       }
+       goto done;
+
+abort:
+       data->valid = false;
+       ret = ERR_PTR(rv);
+
+done:
+       mutex_unlock(&data->update_lock);
+
+       return ret;
+}
+
+static const u8 tach_period[8] = { 1, 2, 4, 8, 16, 32, 32, 32 };
+
+static u8 get_tach_period(u8 fan_dynamics)
+{
+       return tach_period[SR_FROM_REG(fan_dynamics)];
+}
+
+static u8 bits_for_tach_period(int rpm)
+{
+       u8 bits;
+
+       if (rpm < 500)
+               bits = 0x0;
+       else if (rpm < 1000)
+               bits = 0x1;
+       else if (rpm < 2000)
+               bits = 0x2;
+       else if (rpm < 4000)
+               bits = 0x3;
+       else if (rpm < 8000)
+               bits = 0x4;
+       else
+               bits = 0x5;
+
+       return bits;
+}
+
+static ssize_t get_fan(struct device *dev,
+                      struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct max31790_data *data = max31790_update_device(dev);
+       int sr, rpm;
+
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       sr = get_tach_period(data->fan_dynamics[attr->index]);
+       rpm = RPM_FROM_REG(data->tach[attr->index], sr);
+
+       return sprintf(buf, "%d\n", rpm);
+}
+
+static ssize_t get_fan_target(struct device *dev,
+                             struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct max31790_data *data = max31790_update_device(dev);
+       int sr, rpm;
+
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       sr = get_tach_period(data->fan_dynamics[attr->index]);
+       rpm = RPM_FROM_REG(data->target_count[attr->index], sr);
+
+       return sprintf(buf, "%d\n", rpm);
+}
+
+static ssize_t set_fan_target(struct device *dev,
+                             struct device_attribute *devattr,
+                             const char *buf, size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct max31790_data *data = dev_get_drvdata(dev);
+       struct i2c_client *client = data->client;
+       u8 bits;
+       int sr;
+       int target_count;
+       unsigned long rpm;
+       int err;
+
+       err = kstrtoul(buf, 10, &rpm);
+       if (err)
+               return err;
+
+       mutex_lock(&data->update_lock);
+
+       rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
+       bits = bits_for_tach_period(rpm);
+       data->fan_dynamics[attr->index] =
+                       ((data->fan_dynamics[attr->index]
+                         & ~MAX31790_FAN_DYN_SR_MASK)
+                        | (bits << MAX31790_FAN_DYN_SR_SHIFT));
+       err = i2c_smbus_write_byte_data(client,
+                       MAX31790_REG_FAN_DYNAMICS(attr->index),
+                       data->fan_dynamics[attr->index]);
+
+       if (err < 0) {
+               mutex_unlock(&data->update_lock);
+               return err;
+       }
+
+       sr = get_tach_period(data->fan_dynamics[attr->index]);
+       target_count = RPM_TO_REG(rpm, sr);
+       target_count = clamp_val(target_count, 0x1, 0x7FF);
+
+       data->target_count[attr->index] = target_count << 5;
+
+       err = i2c_smbus_write_word_swapped(client,
+                       MAX31790_REG_TARGET_COUNT(attr->index),
+                       data->target_count[attr->index]);
+
+       mutex_unlock(&data->update_lock);
+
+       if (err < 0)
+               return err;
+
+       return count;
+}
+
+static ssize_t get_pwm(struct device *dev,
+                      struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct max31790_data *data = max31790_update_device(dev);
+       int pwm;
+
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       pwm = data->pwm[attr->index] >> 8;
+
+       return sprintf(buf, "%d\n", pwm);
+}
+
+static ssize_t set_pwm(struct device *dev,
+                      struct device_attribute *devattr,
+                      const char *buf, size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct max31790_data *data = dev_get_drvdata(dev);
+       struct i2c_client *client = data->client;
+       unsigned long pwm;
+       int err;
+
+       err = kstrtoul(buf, 10, &pwm);
+       if (err)
+               return err;
+
+       if (pwm > 255)
+               return -EINVAL;
+
+       mutex_lock(&data->update_lock);
+
+       data->pwm[attr->index] = pwm << 8;
+       err = i2c_smbus_write_word_swapped(client,
+                       MAX31790_REG_PWMOUT(attr->index),
+                       data->pwm[attr->index]);
+
+       mutex_unlock(&data->update_lock);
+
+       if (err < 0)
+               return err;
+
+       return count;
+}
+
+static ssize_t get_pwm_enable(struct device *dev,
+                             struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct max31790_data *data = max31790_update_device(dev);
+       int mode;
+
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       if (data->fan_config[attr->index] & MAX31790_FAN_CFG_RPM_MODE)
+               mode = 2;
+       else if (data->fan_config[attr->index] & MAX31790_FAN_CFG_TACH_INPUT_EN)
+               mode = 1;
+       else
+               mode = 0;
+
+       return sprintf(buf, "%d\n", mode);
+}
+
+static ssize_t set_pwm_enable(struct device *dev,
+                             struct device_attribute *devattr,
+                             const char *buf, size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct max31790_data *data = dev_get_drvdata(dev);
+       struct i2c_client *client = data->client;
+       unsigned long mode;
+       int err;
+
+       err = kstrtoul(buf, 10, &mode);
+       if (err)
+               return err;
+
+       switch (mode) {
+       case 0:
+               data->fan_config[attr->index] =
+                       data->fan_config[attr->index]
+                       & ~(MAX31790_FAN_CFG_TACH_INPUT_EN
+                           | MAX31790_FAN_CFG_RPM_MODE);
+               break;
+       case 1:
+               data->fan_config[attr->index] =
+                       (data->fan_config[attr->index]
+                        | MAX31790_FAN_CFG_TACH_INPUT_EN)
+                       & ~MAX31790_FAN_CFG_RPM_MODE;
+               break;
+       case 2:
+               data->fan_config[attr->index] =
+                       data->fan_config[attr->index]
+                       | MAX31790_FAN_CFG_TACH_INPUT_EN
+                       | MAX31790_FAN_CFG_RPM_MODE;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       mutex_lock(&data->update_lock);
+
+       err = i2c_smbus_write_byte_data(client,
+                       MAX31790_REG_FAN_CONFIG(attr->index),
+                       data->fan_config[attr->index]);
+
+       mutex_unlock(&data->update_lock);
+
+       if (err < 0)
+               return err;
+
+       return count;
+}
+
+static ssize_t get_fan_fault(struct device *dev,
+                            struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct max31790_data *data = max31790_update_device(dev);
+       int fault;
+
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       fault = !!(data->fault_status & (1 << attr->index));
+
+       return sprintf(buf, "%d\n", fault);
+}
+
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, get_fan, NULL, 3);
+static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, get_fan, NULL, 4);
+static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, get_fan, NULL, 5);
+
+static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, get_fan_fault, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, get_fan_fault, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan3_fault, S_IRUGO, get_fan_fault, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan4_fault, S_IRUGO, get_fan_fault, NULL, 3);
+static SENSOR_DEVICE_ATTR(fan5_fault, S_IRUGO, get_fan_fault, NULL, 4);
+static SENSOR_DEVICE_ATTR(fan6_fault, S_IRUGO, get_fan_fault, NULL, 5);
+
+static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, get_fan, NULL, 6);
+static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, get_fan, NULL, 7);
+static SENSOR_DEVICE_ATTR(fan9_input, S_IRUGO, get_fan, NULL, 8);
+static SENSOR_DEVICE_ATTR(fan10_input, S_IRUGO, get_fan, NULL, 9);
+static SENSOR_DEVICE_ATTR(fan11_input, S_IRUGO, get_fan, NULL, 10);
+static SENSOR_DEVICE_ATTR(fan12_input, S_IRUGO, get_fan, NULL, 11);
+
+static SENSOR_DEVICE_ATTR(fan7_fault, S_IRUGO, get_fan_fault, NULL, 6);
+static SENSOR_DEVICE_ATTR(fan8_fault, S_IRUGO, get_fan_fault, NULL, 7);
+static SENSOR_DEVICE_ATTR(fan9_fault, S_IRUGO, get_fan_fault, NULL, 8);
+static SENSOR_DEVICE_ATTR(fan10_fault, S_IRUGO, get_fan_fault, NULL, 9);
+static SENSOR_DEVICE_ATTR(fan11_fault, S_IRUGO, get_fan_fault, NULL, 10);
+static SENSOR_DEVICE_ATTR(fan12_fault, S_IRUGO, get_fan_fault, NULL, 11);
+
+static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO,
+               get_fan_target, set_fan_target, 0);
+static SENSOR_DEVICE_ATTR(fan2_target, S_IWUSR | S_IRUGO,
+               get_fan_target, set_fan_target, 1);
+static SENSOR_DEVICE_ATTR(fan3_target, S_IWUSR | S_IRUGO,
+               get_fan_target, set_fan_target, 2);
+static SENSOR_DEVICE_ATTR(fan4_target, S_IWUSR | S_IRUGO,
+               get_fan_target, set_fan_target, 3);
+static SENSOR_DEVICE_ATTR(fan5_target, S_IWUSR | S_IRUGO,
+               get_fan_target, set_fan_target, 4);
+static SENSOR_DEVICE_ATTR(fan6_target, S_IWUSR | S_IRUGO,
+               get_fan_target, set_fan_target, 5);
+
+static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 0);
+static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 1);
+static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 2);
+static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 3);
+static SENSOR_DEVICE_ATTR(pwm5, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 4);
+static SENSOR_DEVICE_ATTR(pwm6, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 5);
+
+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
+               get_pwm_enable, set_pwm_enable, 0);
+static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO,
+               get_pwm_enable, set_pwm_enable, 1);
+static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO,
+               get_pwm_enable, set_pwm_enable, 2);
+static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO,
+               get_pwm_enable, set_pwm_enable, 3);
+static SENSOR_DEVICE_ATTR(pwm5_enable, S_IWUSR | S_IRUGO,
+               get_pwm_enable, set_pwm_enable, 4);
+static SENSOR_DEVICE_ATTR(pwm6_enable, S_IWUSR | S_IRUGO,
+               get_pwm_enable, set_pwm_enable, 5);
+
+static struct attribute *max31790_attrs[] = {
+       &sensor_dev_attr_fan1_input.dev_attr.attr,
+       &sensor_dev_attr_fan2_input.dev_attr.attr,
+       &sensor_dev_attr_fan3_input.dev_attr.attr,
+       &sensor_dev_attr_fan4_input.dev_attr.attr,
+       &sensor_dev_attr_fan5_input.dev_attr.attr,
+       &sensor_dev_attr_fan6_input.dev_attr.attr,
+
+       &sensor_dev_attr_fan1_fault.dev_attr.attr,
+       &sensor_dev_attr_fan2_fault.dev_attr.attr,
+       &sensor_dev_attr_fan3_fault.dev_attr.attr,
+       &sensor_dev_attr_fan4_fault.dev_attr.attr,
+       &sensor_dev_attr_fan5_fault.dev_attr.attr,
+       &sensor_dev_attr_fan6_fault.dev_attr.attr,
+
+       &sensor_dev_attr_fan7_input.dev_attr.attr,
+       &sensor_dev_attr_fan8_input.dev_attr.attr,
+       &sensor_dev_attr_fan9_input.dev_attr.attr,
+       &sensor_dev_attr_fan10_input.dev_attr.attr,
+       &sensor_dev_attr_fan11_input.dev_attr.attr,
+       &sensor_dev_attr_fan12_input.dev_attr.attr,
+
+       &sensor_dev_attr_fan7_fault.dev_attr.attr,
+       &sensor_dev_attr_fan8_fault.dev_attr.attr,
+       &sensor_dev_attr_fan9_fault.dev_attr.attr,
+       &sensor_dev_attr_fan10_fault.dev_attr.attr,
+       &sensor_dev_attr_fan11_fault.dev_attr.attr,
+       &sensor_dev_attr_fan12_fault.dev_attr.attr,
+
+       &sensor_dev_attr_fan1_target.dev_attr.attr,
+       &sensor_dev_attr_fan2_target.dev_attr.attr,
+       &sensor_dev_attr_fan3_target.dev_attr.attr,
+       &sensor_dev_attr_fan4_target.dev_attr.attr,
+       &sensor_dev_attr_fan5_target.dev_attr.attr,
+       &sensor_dev_attr_fan6_target.dev_attr.attr,
+
+       &sensor_dev_attr_pwm1.dev_attr.attr,
+       &sensor_dev_attr_pwm2.dev_attr.attr,
+       &sensor_dev_attr_pwm3.dev_attr.attr,
+       &sensor_dev_attr_pwm4.dev_attr.attr,
+       &sensor_dev_attr_pwm5.dev_attr.attr,
+       &sensor_dev_attr_pwm6.dev_attr.attr,
+
+       &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+       &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+       &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+       &sensor_dev_attr_pwm4_enable.dev_attr.attr,
+       &sensor_dev_attr_pwm5_enable.dev_attr.attr,
+       &sensor_dev_attr_pwm6_enable.dev_attr.attr,
+       NULL
+};
+
+static umode_t max31790_attrs_visible(struct kobject *kobj,
+                                    struct attribute *a, int n)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct max31790_data *data = dev_get_drvdata(dev);
+       struct device_attribute *devattr =
+                       container_of(a, struct device_attribute, attr);
+       int index = to_sensor_dev_attr(devattr)->index % NR_CHANNEL;
+       u8 fan_config;
+
+       fan_config = data->fan_config[index];
+
+       if (n >= NR_CHANNEL * 2 && n < NR_CHANNEL * 4 &&
+           !(fan_config & MAX31790_FAN_CFG_TACH_INPUT))
+               return 0;
+       if (n >= NR_CHANNEL * 4 && (fan_config & MAX31790_FAN_CFG_TACH_INPUT))
+               return 0;
+
+       return a->mode;
+}
+
+static const struct attribute_group max31790_group = {
+       .attrs = max31790_attrs,
+       .is_visible = max31790_attrs_visible,
+};
+__ATTRIBUTE_GROUPS(max31790);
+
+static int max31790_init_client(struct i2c_client *client,
+                               struct max31790_data *data)
+{
+       int i, rv;
+
+       for (i = 0; i < NR_CHANNEL; i++) {
+               rv = i2c_smbus_read_byte_data(client,
+                               MAX31790_REG_FAN_CONFIG(i));
+               if (rv < 0)
+                       return rv;
+               data->fan_config[i] = rv;
+
+               rv = i2c_smbus_read_byte_data(client,
+                               MAX31790_REG_FAN_DYNAMICS(i));
+               if (rv < 0)
+                       return rv;
+               data->fan_dynamics[i] = rv;
+       }
+
+       return 0;
+}
+
+static int max31790_probe(struct i2c_client *client,
+                         const struct i2c_device_id *id)
+{
+       struct i2c_adapter *adapter = client->adapter;
+       struct device *dev = &client->dev;
+       struct max31790_data *data;
+       struct device *hwmon_dev;
+       int err;
+
+       if (!i2c_check_functionality(adapter,
+                       I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
+               return -ENODEV;
+
+       data = devm_kzalloc(dev, sizeof(struct max31790_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->client = client;
+       mutex_init(&data->update_lock);
+
+       /*
+        * Initialize the max31790 chip
+        */
+       err = max31790_init_client(client, data);
+       if (err)
+               return err;
+
+       hwmon_dev = devm_hwmon_device_register_with_groups(dev,
+                       client->name, data, max31790_groups);
+
+       return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id max31790_id[] = {
+       { "max31790", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max31790_id);
+
+static struct i2c_driver max31790_driver = {
+       .class          = I2C_CLASS_HWMON,
+       .probe          = max31790_probe,
+       .driver = {
+               .name   = "max31790",
+       },
+       .id_table       = max31790_id,
+};
+
+module_i2c_driver(max31790_driver);
+
+MODULE_AUTHOR("Il Han <corone.il.han@gmail.com>");
+MODULE_DESCRIPTION("MAX31790 sensor driver");
+MODULE_LICENSE("GPL");
index 8b4fa55..d7ebdf8 100644 (file)
@@ -515,16 +515,24 @@ static const char *const nct6779_temp_label[] = {
        "PCH_DIM1_TEMP",
        "PCH_DIM2_TEMP",
        "PCH_DIM3_TEMP",
-       "BYTE_TEMP"
+       "BYTE_TEMP",
+       "",
+       "",
+       "",
+       "",
+       "Virtual_TEMP"
 };
 
-static const u16 NCT6779_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6779_temp_label) - 1]
+#define NCT6779_NUM_LABELS     (ARRAY_SIZE(nct6779_temp_label) - 5)
+#define NCT6791_NUM_LABELS     ARRAY_SIZE(nct6779_temp_label)
+
+static const u16 NCT6779_REG_TEMP_ALTERNATE[NCT6791_NUM_LABELS - 1]
        = { 0x490, 0x491, 0x492, 0x493, 0x494, 0x495, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0,
            0, 0x400, 0x401, 0x402, 0x404, 0x405, 0x406, 0x407,
            0x408, 0 };
 
-static const u16 NCT6779_REG_TEMP_CRIT[ARRAY_SIZE(nct6779_temp_label) - 1]
+static const u16 NCT6779_REG_TEMP_CRIT[NCT6791_NUM_LABELS - 1]
        = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a };
 
 /* NCT6791 specific data */
@@ -557,6 +565,76 @@ static const u16 NCT6792_REG_TEMP_MON[] = {
 static const u16 NCT6792_REG_BEEP[NUM_REG_BEEP] = {
        0xb2, 0xb3, 0xb4, 0xb5, 0xbf };
 
+static const char *const nct6792_temp_label[] = {
+       "",
+       "SYSTIN",
+       "CPUTIN",
+       "AUXTIN0",
+       "AUXTIN1",
+       "AUXTIN2",
+       "AUXTIN3",
+       "",
+       "SMBUSMASTER 0",
+       "SMBUSMASTER 1",
+       "SMBUSMASTER 2",
+       "SMBUSMASTER 3",
+       "SMBUSMASTER 4",
+       "SMBUSMASTER 5",
+       "SMBUSMASTER 6",
+       "SMBUSMASTER 7",
+       "PECI Agent 0",
+       "PECI Agent 1",
+       "PCH_CHIP_CPU_MAX_TEMP",
+       "PCH_CHIP_TEMP",
+       "PCH_CPU_TEMP",
+       "PCH_MCH_TEMP",
+       "PCH_DIM0_TEMP",
+       "PCH_DIM1_TEMP",
+       "PCH_DIM2_TEMP",
+       "PCH_DIM3_TEMP",
+       "BYTE_TEMP",
+       "PECI Agent 0 Calibration",
+       "PECI Agent 1 Calibration",
+       "",
+       "",
+       "Virtual_TEMP"
+};
+
+static const char *const nct6793_temp_label[] = {
+       "",
+       "SYSTIN",
+       "CPUTIN",
+       "AUXTIN0",
+       "AUXTIN1",
+       "AUXTIN2",
+       "AUXTIN3",
+       "",
+       "SMBUSMASTER 0",
+       "SMBUSMASTER 1",
+       "",
+       "",
+       "",
+       "",
+       "",
+       "",
+       "PECI Agent 0",
+       "PECI Agent 1",
+       "PCH_CHIP_CPU_MAX_TEMP",
+       "PCH_CHIP_TEMP",
+       "PCH_CPU_TEMP",
+       "PCH_MCH_TEMP",
+       "Agent0 Dimm0 ",
+       "Agent0 Dimm1",
+       "Agent1 Dimm0",
+       "Agent1 Dimm1",
+       "BYTE_TEMP0",
+       "BYTE_TEMP1",
+       "PECI Agent 0 Calibration",
+       "PECI Agent 1 Calibration",
+       "",
+       "Virtual_TEMP"
+};
+
 /* NCT6102D/NCT6106D specific data */
 
 #define NCT6106_REG_VBAT       0x318
@@ -3605,7 +3683,7 @@ static int nct6775_probe(struct platform_device *pdev)
                data->speed_tolerance_limit = 63;
 
                data->temp_label = nct6779_temp_label;
-               data->temp_label_num = ARRAY_SIZE(nct6779_temp_label);
+               data->temp_label_num = NCT6779_NUM_LABELS;
 
                data->REG_CONFIG = NCT6775_REG_CONFIG;
                data->REG_VBAT = NCT6775_REG_VBAT;
@@ -3682,8 +3760,19 @@ static int nct6775_probe(struct platform_device *pdev)
                data->tolerance_mask = 0x07;
                data->speed_tolerance_limit = 63;
 
-               data->temp_label = nct6779_temp_label;
-               data->temp_label_num = ARRAY_SIZE(nct6779_temp_label);
+               switch (data->kind) {
+               default:
+               case nct6791:
+                       data->temp_label = nct6779_temp_label;
+                       break;
+               case nct6792:
+                       data->temp_label = nct6792_temp_label;
+                       break;
+               case nct6793:
+                       data->temp_label = nct6793_temp_label;
+                       break;
+               }
+               data->temp_label_num = NCT6791_NUM_LABELS;
 
                data->REG_CONFIG = NCT6775_REG_CONFIG;
                data->REG_VBAT = NCT6775_REG_VBAT;