Browse Source

Merge tag 'hwmon-for-linus-v4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging

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
Linus Torvalds 10 years ago
parent
commit
66b0199678

+ 3 - 2
Documentation/hwmon/lm75

@@ -42,8 +42,8 @@ Supported chips:
     Addresses scanned: none
     Addresses scanned: none
     Datasheet: Publicly available at the ST website
     Datasheet: Publicly available at the ST website
                http://www.st.com/internet/analog/product/121769.jsp
                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
     Addresses scanned: none
     Datasheet: Publicly available at the Texas Instruments website
     Datasheet: Publicly available at the Texas Instruments website
                http://www.ti.com/product/tmp100
                http://www.ti.com/product/tmp100
@@ -51,6 +51,7 @@ Supported chips:
                http://www.ti.com/product/tmp105
                http://www.ti.com/product/tmp105
                http://www.ti.com/product/tmp112
                http://www.ti.com/product/tmp112
                http://www.ti.com/product/tmp75
                http://www.ti.com/product/tmp75
+               http://www.ti.com/product/tmp75c
                http://www.ti.com/product/tmp175
                http://www.ti.com/product/tmp175
                http://www.ti.com/product/tmp275
                http://www.ti.com/product/tmp275
   * NXP LM75B
   * NXP LM75B

+ 37 - 0
Documentation/hwmon/max31790

@@ -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)

+ 10 - 0
drivers/hwmon/Kconfig

@@ -840,6 +840,16 @@ config SENSORS_MAX6697
 	  This driver can also be built as a module.  If so, the module
 	  This driver can also be built as a module.  If so, the module
 	  will be called max6697.
 	  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
 config SENSORS_HTU21
 	tristate "Measurement Specialties HTU21D humidity/temperature sensors"
 	tristate "Measurement Specialties HTU21D humidity/temperature sensors"
 	depends on I2C
 	depends on I2C

+ 1 - 0
drivers/hwmon/Makefile

@@ -115,6 +115,7 @@ obj-$(CONFIG_SENSORS_MAX6639)	+= max6639.o
 obj-$(CONFIG_SENSORS_MAX6642)	+= max6642.o
 obj-$(CONFIG_SENSORS_MAX6642)	+= max6642.o
 obj-$(CONFIG_SENSORS_MAX6650)	+= max6650.o
 obj-$(CONFIG_SENSORS_MAX6650)	+= max6650.o
 obj-$(CONFIG_SENSORS_MAX6697)	+= max6697.o
 obj-$(CONFIG_SENSORS_MAX6697)	+= max6697.o
+obj-$(CONFIG_SENSORS_MAX31790)	+= max31790.o
 obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
 obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
 obj-$(CONFIG_SENSORS_MCP3021)	+= mcp3021.o
 obj-$(CONFIG_SENSORS_MCP3021)	+= mcp3021.o
 obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
 obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o

+ 1 - 1
drivers/hwmon/abx500.c

@@ -377,7 +377,7 @@ static int setup_irqs(struct platform_device *pdev)
 	}
 	}
 
 
 	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
 	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)
 	if (ret < 0)
 		dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret);
 		dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret);
 
 

+ 1 - 1
drivers/hwmon/coretemp.c

@@ -52,7 +52,7 @@ module_param_named(tjmax, force_tjmax, int, 0444);
 MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
 MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
 
 
 #define BASE_SYSFS_ATTR_NO	2	/* Sysfs Base attr no for coretemp */
 #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 CORETEMP_NAME_LENGTH	19	/* String Length of attrs */
 #define MAX_CORE_ATTRS		4	/* Maximum no of basic attrs */
 #define MAX_CORE_ATTRS		4	/* Maximum no of basic attrs */
 #define TOTAL_ATTRS		(MAX_CORE_ATTRS + 1)
 #define TOTAL_ATTRS		(MAX_CORE_ATTRS + 1)

+ 62 - 25
drivers/hwmon/fam15h_power.c

@@ -26,6 +26,7 @@
 #include <linux/pci.h>
 #include <linux/pci.h>
 #include <linux/bitops.h>
 #include <linux/bitops.h>
 #include <asm/processor.h>
 #include <asm/processor.h>
+#include <asm/msr.h>
 
 
 MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
 MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
 MODULE_AUTHOR("Andreas Herrmann <herrmann.der.user@googlemail.com>");
 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_RUNNING_AVERAGE		0xe0
 #define REG_TDP_LIMIT3			0xe8
 #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 fam15h_power_data {
 	struct pci_dev *pdev;
 	struct pci_dev *pdev;
 	unsigned int tdp_to_watts;
 	unsigned int tdp_to_watts;
 	unsigned int base_tdp;
 	unsigned int base_tdp;
 	unsigned int processor_pwr_watts;
 	unsigned int processor_pwr_watts;
 	unsigned int cpu_pwr_sample_ratio;
 	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,
 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 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)
 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
 #define fam15h_power_resume NULL
 #endif
 #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;
 	u32 val, eax, ebx, ecx, edx;
 	u64 tmp;
 	u64 tmp;
+	int ret;
 
 
 	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
 	pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
 	data->base_tdp = val >> 16;
 	data->base_tdp = val >> 16;
@@ -211,11 +229,15 @@ static void fam15h_power_init_data(struct pci_dev *f4,
 	/* convert to microWatt */
 	/* convert to microWatt */
 	data->processor_pwr_watts = (tmp * 15625) >> 10;
 	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(0x80000007, &eax, &ebx, &ecx, &edx);
 
 
 	/* CPUID Fn8000_0007:EDX[12] indicates to support accumulated power */
 	/* CPUID Fn8000_0007:EDX[12] indicates to support accumulated power */
 	if (!(edx & BIT(12)))
 	if (!(edx & BIT(12)))
-		return;
+		return 0;
 
 
 	/*
 	/*
 	 * determine the ratio of the compute unit power accumulator
 	 * 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
 	 * Fn8000_0007:ECX
 	 */
 	 */
 	data->cpu_pwr_sample_ratio = 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,
 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 fam15h_power_data *data;
 	struct device *dev = &pdev->dev;
 	struct device *dev = &pdev->dev;
 	struct device *hwmon_dev;
 	struct device *hwmon_dev;
+	int ret;
 
 
 	/*
 	/*
 	 * though we ignore every other northbridge, we still have to
 	 * 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)
 	if (!data)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	fam15h_power_init_data(pdev, data);
+	ret = fam15h_power_init_data(pdev, data);
+	if (ret)
+		return ret;
+
 	data->pdev = pdev;
 	data->pdev = pdev;
 
 
+	data->groups[0] = &data->group;
+
 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, "fam15h_power",
 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, "fam15h_power",
 							   data,
 							   data,
-							   fam15h_power_groups);
+							   &data->groups[0]);
 	return PTR_ERR_OR_ZERO(hwmon_dev);
 	return PTR_ERR_OR_ZERO(hwmon_dev);
 }
 }
 
 

+ 7 - 0
drivers/hwmon/ibmpowernv.c

@@ -474,11 +474,18 @@ static const struct platform_device_id opal_sensor_driver_ids[] = {
 };
 };
 MODULE_DEVICE_TABLE(platform, 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 = {
 static struct platform_driver ibmpowernv_driver = {
 	.probe		= ibmpowernv_probe,
 	.probe		= ibmpowernv_probe,
 	.id_table	= opal_sensor_driver_ids,
 	.id_table	= opal_sensor_driver_ids,
 	.driver		= {
 	.driver		= {
 		.name	= DRVNAME,
 		.name	= DRVNAME,
+		.of_match_table	= opal_sensor_match,
 	},
 	},
 };
 };
 
 

+ 99 - 144
drivers/hwmon/ina2xx.c

@@ -37,6 +37,7 @@
 #include <linux/of.h>
 #include <linux/of.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
 #include <linux/util_macros.h>
 #include <linux/util_macros.h>
+#include <linux/regmap.h>
 
 
 #include <linux/platform_data/ina2xx.h>
 #include <linux/platform_data/ina2xx.h>
 
 
@@ -84,6 +85,11 @@
  */
  */
 #define INA226_TOTAL_CONV_TIME_DEFAULT	2200
 #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 };
 enum ina2xx_ids { ina219, ina226 };
 
 
 struct ina2xx_config {
 struct ina2xx_config {
@@ -97,20 +103,13 @@ struct ina2xx_config {
 };
 };
 
 
 struct ina2xx_data {
 struct ina2xx_data {
-	struct i2c_client *client;
 	const struct ina2xx_config *config;
 	const struct ina2xx_config *config;
 
 
 	long rshunt;
 	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];
 	const struct attribute_group *groups[INA2XX_MAX_ATTRIBUTE_GROUPS];
-	u16 regs[INA2XX_MAX_REGISTERS];
 };
 };
 
 
 static const struct ina2xx_config ina2xx_config[] = {
 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);
 	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;
 	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,
 	avg_bits = find_closest(avg, ina226_avg_tab,
 				ARRAY_SIZE(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)
 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,
 	u16 val = DIV_ROUND_CLOSEST(data->config->calibration_factor,
 				    data->rshunt);
 				    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)
 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)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
@@ -203,47 +193,52 @@ static int ina2xx_init(struct ina2xx_data *data)
 	return ina2xx_calibrate(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 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--) {
 	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
 		 * If the current value in the calibration register is 0, the
 		 * power and current registers will also remain at 0. In case
 		 * power and current registers will also remain at 0. In case
 		 * the chip has been reset let's check the calibration
 		 * the chip has been reset let's check the calibration
 		 * register and reinitialize if needed.
 		 * 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;
 		return 0;
 	}
 	}
 
 
@@ -256,51 +251,31 @@ static int ina2xx_do_update(struct device *dev)
 	return -ENODEV;
 	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;
 	int val;
 
 
 	switch (reg) {
 	switch (reg) {
 	case INA2XX_SHUNT_VOLTAGE:
 	case INA2XX_SHUNT_VOLTAGE:
 		/* signed register */
 		/* 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;
 		break;
 	case INA2XX_BUS_VOLTAGE:
 	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;
 		  * data->config->bus_voltage_lsb;
 		val = DIV_ROUND_CLOSEST(val, 1000);
 		val = DIV_ROUND_CLOSEST(val, 1000);
 		break;
 		break;
 	case INA2XX_POWER:
 	case INA2XX_POWER:
-		val = data->regs[reg] * data->config->power_lsb;
+		val = regval * data->config->power_lsb;
 		break;
 		break;
 	case INA2XX_CURRENT:
 	case INA2XX_CURRENT:
 		/* signed register, LSB=1mA (selected), in mA */
 		/* signed register, LSB=1mA (selected), in mA */
-		val = (s16)data->regs[reg];
+		val = (s16)regval;
 		break;
 		break;
 	case INA2XX_CALIBRATION:
 	case INA2XX_CALIBRATION:
 		val = DIV_ROUND_CLOSEST(data->config->calibration_factor,
 		val = DIV_ROUND_CLOSEST(data->config->calibration_factor,
-					data->regs[reg]);
+					regval);
 		break;
 		break;
 	default:
 	default:
 		/* programmer goofed */
 		/* programmer goofed */
@@ -316,25 +291,25 @@ static ssize_t ina2xx_show_value(struct device *dev,
 				 struct device_attribute *da, char *buf)
 				 struct device_attribute *da, char *buf)
 {
 {
 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
 	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",
 	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,
 static ssize_t ina2xx_set_shunt(struct device *dev,
 				struct device_attribute *da,
 				struct device_attribute *da,
 				const char *buf, size_t count)
 				const char *buf, size_t count)
 {
 {
-	struct ina2xx_data *data = ina2xx_update_device(dev);
 	unsigned long val;
 	unsigned long val;
 	int status;
 	int status;
-
-	if (IS_ERR(data))
-		return PTR_ERR(data);
+	struct ina2xx_data *data = dev_get_drvdata(dev);
 
 
 	status = kstrtoul(buf, 10, &val);
 	status = kstrtoul(buf, 10, &val);
 	if (status < 0)
 	if (status < 0)
@@ -345,10 +320,10 @@ static ssize_t ina2xx_set_shunt(struct device *dev,
 	    val > data->config->calibration_factor)
 	    val > data->config->calibration_factor)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	mutex_lock(&data->update_lock);
+	mutex_lock(&data->config_lock);
 	data->rshunt = val;
 	data->rshunt = val;
 	status = ina2xx_calibrate(data);
 	status = ina2xx_calibrate(data);
-	mutex_unlock(&data->update_lock);
+	mutex_unlock(&data->config_lock);
 	if (status < 0)
 	if (status < 0)
 		return status;
 		return status;
 
 
@@ -370,17 +345,9 @@ static ssize_t ina226_set_interval(struct device *dev,
 	if (val > INT_MAX || val == 0)
 	if (val > INT_MAX || val == 0)
 		return -EINVAL;
 		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)
 	if (status < 0)
 		return status;
 		return status;
 
 
@@ -390,18 +357,15 @@ static ssize_t ina226_set_interval(struct device *dev,
 static ssize_t ina226_show_interval(struct device *dev,
 static ssize_t ina226_show_interval(struct device *dev,
 				    struct device_attribute *da, char *buf)
 				    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 */
 /* shunt voltage */
@@ -455,60 +419,51 @@ static const struct attribute_group ina226_group = {
 static int ina2xx_probe(struct i2c_client *client,
 static int ina2xx_probe(struct i2c_client *client,
 			const struct i2c_device_id *id)
 			const struct i2c_device_id *id)
 {
 {
-	struct i2c_adapter *adapter = client->adapter;
-	struct ina2xx_platform_data *pdata;
 	struct device *dev = &client->dev;
 	struct device *dev = &client->dev;
 	struct ina2xx_data *data;
 	struct ina2xx_data *data;
 	struct device *hwmon_dev;
 	struct device *hwmon_dev;
 	u32 val;
 	u32 val;
 	int ret, group = 0;
 	int ret, group = 0;
 
 
-	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
-		return -ENODEV;
-
 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
 	if (!data)
 		return -ENOMEM;
 		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 */
 	/* 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;
 		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);
 	ret = ina2xx_init(data);
 	if (ret < 0) {
 	if (ret < 0) {
 		dev_err(dev, "error configuring the device: %d\n", ret);
 		dev_err(dev, "error configuring the device: %d\n", ret);
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}
 
 
-	mutex_init(&data->update_lock);
+	mutex_init(&data->config_lock);
 
 
 	data->groups[group++] = &ina2xx_group;
 	data->groups[group++] = &ina2xx_group;
-	if (data->kind == ina226)
+	if (id->driver_data == ina226)
 		data->groups[group++] = &ina226_group;
 		data->groups[group++] = &ina226_group;
 
 
 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,

+ 7 - 0
drivers/hwmon/lm75.c

@@ -57,6 +57,7 @@ enum lm75_type {		/* keep sorted in alphabetical order */
 	tmp175,
 	tmp175,
 	tmp275,
 	tmp275,
 	tmp75,
 	tmp75,
+	tmp75c,
 };
 };
 
 
 /* Addresses scanned */
 /* Addresses scanned */
@@ -280,6 +281,11 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
 		data->resolution = 12;
 		data->resolution = 12;
 		data->sample_time = HZ / 2;
 		data->sample_time = HZ / 2;
 		break;
 		break;
+	case tmp75c:
+		clr_mask |= 1 << 5;		/* not one-shot mode */
+		data->resolution = 12;
+		data->sample_time = HZ / 4;
+		break;
 	}
 	}
 
 
 	/* configure as specified */
 	/* configure as specified */
@@ -343,6 +349,7 @@ static const struct i2c_device_id lm75_ids[] = {
 	{ "tmp175", tmp175, },
 	{ "tmp175", tmp175, },
 	{ "tmp275", tmp275, },
 	{ "tmp275", tmp275, },
 	{ "tmp75", tmp75, },
 	{ "tmp75", tmp75, },
+	{ "tmp75c", tmp75c, },
 	{ /* LIST END */ }
 	{ /* LIST END */ }
 };
 };
 MODULE_DEVICE_TABLE(i2c, lm75_ids);
 MODULE_DEVICE_TABLE(i2c, lm75_ids);

+ 603 - 0
drivers/hwmon/max31790.c

@@ -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");

+ 95 - 6
drivers/hwmon/nct6775.c

@@ -515,16 +515,24 @@ static const char *const nct6779_temp_label[] = {
 	"PCH_DIM1_TEMP",
 	"PCH_DIM1_TEMP",
 	"PCH_DIM2_TEMP",
 	"PCH_DIM2_TEMP",
 	"PCH_DIM3_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,
 	= { 0x490, 0x491, 0x492, 0x493, 0x494, 0x495, 0, 0,
 	    0, 0, 0, 0, 0, 0, 0, 0,
 	    0, 0, 0, 0, 0, 0, 0, 0,
 	    0, 0x400, 0x401, 0x402, 0x404, 0x405, 0x406, 0x407,
 	    0, 0x400, 0x401, 0x402, 0x404, 0x405, 0x406, 0x407,
 	    0x408, 0 };
 	    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 };
 	= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a };
 
 
 /* NCT6791 specific data */
 /* NCT6791 specific data */
@@ -557,6 +565,76 @@ static const u16 NCT6792_REG_TEMP_MON[] = {
 static const u16 NCT6792_REG_BEEP[NUM_REG_BEEP] = {
 static const u16 NCT6792_REG_BEEP[NUM_REG_BEEP] = {
 	0xb2, 0xb3, 0xb4, 0xb5, 0xbf };
 	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 */
 /* NCT6102D/NCT6106D specific data */
 
 
 #define NCT6106_REG_VBAT	0x318
 #define NCT6106_REG_VBAT	0x318
@@ -3605,7 +3683,7 @@ static int nct6775_probe(struct platform_device *pdev)
 		data->speed_tolerance_limit = 63;
 		data->speed_tolerance_limit = 63;
 
 
 		data->temp_label = nct6779_temp_label;
 		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_CONFIG = NCT6775_REG_CONFIG;
 		data->REG_VBAT = NCT6775_REG_VBAT;
 		data->REG_VBAT = NCT6775_REG_VBAT;
@@ -3682,8 +3760,19 @@ static int nct6775_probe(struct platform_device *pdev)
 		data->tolerance_mask = 0x07;
 		data->tolerance_mask = 0x07;
 		data->speed_tolerance_limit = 63;
 		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_CONFIG = NCT6775_REG_CONFIG;
 		data->REG_VBAT = NCT6775_REG_VBAT;
 		data->REG_VBAT = NCT6775_REG_VBAT;