summaryrefslogtreecommitdiffstats
path: root/drivers/hwmon
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/Kconfig70
-rw-r--r--drivers/hwmon/Makefile6
-rw-r--r--drivers/hwmon/ab8500.c206
-rw-r--r--drivers/hwmon/abituguru.c11
-rw-r--r--drivers/hwmon/abituguru3.c10
-rw-r--r--drivers/hwmon/abx500.c491
-rw-r--r--drivers/hwmon/abx500.h69
-rw-r--r--drivers/hwmon/ad7314.c7
-rw-r--r--drivers/hwmon/adm1021.c11
-rw-r--r--drivers/hwmon/adm1026.c53
-rw-r--r--drivers/hwmon/adm1029.c9
-rw-r--r--drivers/hwmon/adm9240.c9
-rw-r--r--drivers/hwmon/ads7871.c42
-rw-r--r--drivers/hwmon/adt7310.c123
-rw-r--r--drivers/hwmon/adt7410.c442
-rw-r--r--drivers/hwmon/adt7411.c10
-rw-r--r--drivers/hwmon/adt7x10.c511
-rw-r--r--drivers/hwmon/adt7x10.h37
-rw-r--r--drivers/hwmon/applesmc.c2
-rw-r--r--drivers/hwmon/asb100.c23
-rw-r--r--drivers/hwmon/asc7621.c56
-rw-r--r--drivers/hwmon/coretemp.c3
-rw-r--r--drivers/hwmon/da9052-hwmon.c14
-rw-r--r--drivers/hwmon/da9055-hwmon.c4
-rw-r--r--drivers/hwmon/dme1737.c68
-rw-r--r--drivers/hwmon/f71805f.c3
-rw-r--r--drivers/hwmon/fam15h_power.c4
-rw-r--r--drivers/hwmon/fschmd.c9
-rw-r--r--drivers/hwmon/gl518sm.c5
-rw-r--r--drivers/hwmon/gpio-fan.c106
-rw-r--r--drivers/hwmon/ibmaem.c21
-rw-r--r--drivers/hwmon/ibmpex.c17
-rw-r--r--drivers/hwmon/iio_hwmon.c196
-rw-r--r--drivers/hwmon/ina2xx.c16
-rw-r--r--drivers/hwmon/it87.c24
-rw-r--r--drivers/hwmon/k8temp.c4
-rw-r--r--drivers/hwmon/lm78.c10
-rw-r--r--drivers/hwmon/lm80.c5
-rw-r--r--drivers/hwmon/lm85.c4
-rw-r--r--drivers/hwmon/lm93.c74
-rw-r--r--drivers/hwmon/lm95234.c769
-rw-r--r--drivers/hwmon/ltc4151.c12
-rw-r--r--drivers/hwmon/ltc4215.c46
-rw-r--r--drivers/hwmon/ltc4245.c116
-rw-r--r--drivers/hwmon/ltc4261.c38
-rw-r--r--drivers/hwmon/max6697.c209
-rw-r--r--drivers/hwmon/mc13783-adc.c13
-rw-r--r--drivers/hwmon/nct6775.c4191
-rw-r--r--drivers/hwmon/ntc_thermistor.c353
-rw-r--r--drivers/hwmon/pc87360.c25
-rw-r--r--drivers/hwmon/pc87427.c13
-rw-r--r--drivers/hwmon/pmbus/Kconfig6
-rw-r--r--drivers/hwmon/pmbus/lm25066.c414
-rw-r--r--drivers/hwmon/pmbus/ltc2978.c170
-rw-r--r--drivers/hwmon/s3c-hwmon.c19
-rw-r--r--drivers/hwmon/sch56xx-common.c12
-rw-r--r--drivers/hwmon/sis5595.c5
-rw-r--r--drivers/hwmon/thmc50.c7
-rw-r--r--drivers/hwmon/tmp102.c4
-rw-r--r--drivers/hwmon/tmp401.c728
-rw-r--r--drivers/hwmon/tmp421.c7
-rw-r--r--drivers/hwmon/via686a.c47
-rw-r--r--drivers/hwmon/vt1211.c23
-rw-r--r--drivers/hwmon/vt8231.c5
-rw-r--r--drivers/hwmon/w83627ehf.c31
-rw-r--r--drivers/hwmon/w83781d.c36
-rw-r--r--drivers/hwmon/w83791d.c4
-rw-r--r--drivers/hwmon/w83792d.c13
-rw-r--r--drivers/hwmon/w83793.c8
-rw-r--r--drivers/hwmon/w83795.c9
70 files changed, 8418 insertions, 1700 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 89ac1cb26f24..9c333d471433 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -39,6 +39,19 @@ config HWMON_DEBUG_CHIP
comment "Native drivers"
+config SENSORS_AB8500
+ tristate "AB8500 thermal monitoring"
+ depends on AB8500_GPADC && AB8500_BM
+ default n
+ help
+ If you say yes here you get support for the thermal sensor part
+ of the AB8500 chip. The driver includes thermal management for
+ AB8500 die and two GPADC channels. The GPADC channel are preferably
+ used to access sensors outside the AB8500 chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called abx500-temp.
+
config SENSORS_ABITUGURU
tristate "Abit uGuru (rev 1 & 2)"
depends on X86 && DMI
@@ -179,9 +192,29 @@ config SENSORS_ADM9240
This driver can also be built as a module. If so, the module
will be called adm9240.
+config SENSORS_ADT7X10
+ tristate
+ help
+ This module contains common code shared by the ADT7310/ADT7320 and
+ ADT7410/ADT7420 temperature monitoring chip drivers.
+
+ If build as a module, the module will be called adt7x10.
+
+config SENSORS_ADT7310
+ tristate "Analog Devices ADT7310/ADT7320"
+ depends on SPI_MASTER
+ select SENSORS_ADT7X10
+ help
+ If you say yes here you get support for the Analog Devices
+ ADT7310 and ADT7320 temperature monitoring chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called adt7310.
+
config SENSORS_ADT7410
tristate "Analog Devices ADT7410/ADT7420"
depends on I2C
+ select SENSORS_ADT7X10
help
If you say yes here you get support for the Analog Devices
ADT7410 and ADT7420 temperature monitoring chips.
@@ -499,6 +532,15 @@ config SENSORS_IBMPEX
This driver can also be built as a module. If so, the module
will be called ibmpex.
+config SENSORS_IIO_HWMON
+ tristate "Hwmon driver that uses channels specified via iio maps"
+ depends on IIO
+ help
+ This is a platform driver that in combination with a suitable
+ map allows IIO devices to provide basic hwmon functionality
+ for those channels specified in the map. This map can be provided
+ either via platform data or the device tree bindings.
+
config SENSORS_IT87
tristate "ITE IT87xx and compatibles"
depends on !PPC
@@ -751,6 +793,16 @@ config SENSORS_LTC4261
This driver can also be built as a module. If so, the module will
be called ltc4261.
+config SENSORS_LM95234
+ tristate "National Semiconductor LM95234"
+ depends on I2C
+ help
+ If you say yes here you get support for the LM95234 temperature
+ sensor.
+
+ This driver can also be built as a module. If so, the module
+ will be called lm95234.
+
config SENSORS_LM95241
tristate "National Semiconductor LM95241 and compatibles"
depends on I2C
@@ -877,8 +929,22 @@ config SENSORS_MCP3021
This driver can also be built as a module. If so, the module
will be called mcp3021.
+config SENSORS_NCT6775
+ tristate "Nuvoton NCT6775F and compatibles"
+ depends on !PPC
+ select HWMON_VID
+ help
+ If you say yes here you get support for the hardware monitoring
+ functionality of the Nuvoton NCT6775F, NCT6776F, NCT6779D
+ and compatible Super-I/O chips. This driver replaces the
+ w83627ehf driver for NCT6775F and NCT6776F.
+
+ This driver can also be built as a module. If so, the module
+ will be called nct6775.
+
config SENSORS_NTC_THERMISTOR
tristate "NTC thermistor support"
+ depends on (!OF && !IIO) || (OF && IIO)
help
This driver supports NTC thermistors sensor reading and its
interpretation. The driver can also monitor the temperature and
@@ -1204,8 +1270,8 @@ config SENSORS_TMP401
tristate "Texas Instruments TMP401 and compatibles"
depends on I2C
help
- If you say yes here you get support for Texas Instruments TMP401 and
- TMP411 temperature sensor chips.
+ If you say yes here you get support for Texas Instruments TMP401,
+ TMP411, TMP431, and TMP432 temperature sensor chips.
This driver can also be built as a module. If so, the module
will be called tmp401.
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 8d6d97ea7c1e..d17d3e64f9f4 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_SENSORS_W83795) += w83795.o
obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
+obj-$(CONFIG_SENSORS_AB8500) += abx500.o ab8500.o
obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o
obj-$(CONFIG_SENSORS_AD7314) += ad7314.o
@@ -34,6 +35,8 @@ obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o
obj-$(CONFIG_SENSORS_ADS1015) += ads1015.o
obj-$(CONFIG_SENSORS_ADS7828) += ads7828.o
obj-$(CONFIG_SENSORS_ADS7871) += ads7871.o
+obj-$(CONFIG_SENSORS_ADT7X10) += adt7x10.o
+obj-$(CONFIG_SENSORS_ADT7310) += adt7310.o
obj-$(CONFIG_SENSORS_ADT7410) += adt7410.o
obj-$(CONFIG_SENSORS_ADT7411) += adt7411.o
obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o
@@ -65,6 +68,7 @@ obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o
+obj-$(CONFIG_SENSORS_IIO_HWMON) += iio_hwmon.o
obj-$(CONFIG_SENSORS_INA209) += ina209.o
obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o
obj-$(CONFIG_SENSORS_IT87) += it87.o
@@ -86,6 +90,7 @@ obj-$(CONFIG_SENSORS_LM87) += lm87.o
obj-$(CONFIG_SENSORS_LM90) += lm90.o
obj-$(CONFIG_SENSORS_LM92) += lm92.o
obj-$(CONFIG_SENSORS_LM93) += lm93.o
+obj-$(CONFIG_SENSORS_LM95234) += lm95234.o
obj-$(CONFIG_SENSORS_LM95241) += lm95241.o
obj-$(CONFIG_SENSORS_LM95245) += lm95245.o
obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o
@@ -103,6 +108,7 @@ obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
obj-$(CONFIG_SENSORS_MAX6697) += max6697.o
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
+obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o
obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
diff --git a/drivers/hwmon/ab8500.c b/drivers/hwmon/ab8500.c
new file mode 100644
index 000000000000..d844dc806853
--- /dev/null
+++ b/drivers/hwmon/ab8500.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) ST-Ericsson 2010 - 2013
+ * Author: Martin Persson <martin.persson@stericsson.com>
+ * Hongbo Zhang <hongbo.zhang@linaro.org>
+ * License Terms: GNU General Public License v2
+ *
+ * When the AB8500 thermal warning temperature is reached (threshold cannot
+ * be changed by SW), an interrupt is set, and if no further action is taken
+ * within a certain time frame, pm_power off will be called.
+ *
+ * When AB8500 thermal shutdown temperature is reached a hardware shutdown of
+ * the AB8500 will occur.
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/power/ab8500.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include "abx500.h"
+
+#define DEFAULT_POWER_OFF_DELAY (HZ * 10)
+#define THERMAL_VCC 1800
+#define PULL_UP_RESISTOR 47000
+/* Number of monitored sensors should not greater than NUM_SENSORS */
+#define NUM_MONITORED_SENSORS 4
+
+struct ab8500_gpadc_cfg {
+ const struct abx500_res_to_temp *temp_tbl;
+ int tbl_sz;
+ int vcc;
+ int r_up;
+};
+
+struct ab8500_temp {
+ struct ab8500_gpadc *gpadc;
+ struct ab8500_btemp *btemp;
+ struct delayed_work power_off_work;
+ struct ab8500_gpadc_cfg cfg;
+ struct abx500_temp *abx500_data;
+};
+
+/*
+ * The hardware connection is like this:
+ * VCC----[ R_up ]-----[ NTC ]----GND
+ * where R_up is pull-up resistance, and GPADC measures voltage on NTC.
+ * and res_to_temp table is strictly sorted by falling resistance values.
+ */
+static int ab8500_voltage_to_temp(struct ab8500_gpadc_cfg *cfg,
+ int v_ntc, int *temp)
+{
+ int r_ntc, i = 0, tbl_sz = cfg->tbl_sz;
+ const struct abx500_res_to_temp *tbl = cfg->temp_tbl;
+
+ if (cfg->vcc < 0 || v_ntc >= cfg->vcc)
+ return -EINVAL;
+
+ r_ntc = v_ntc * cfg->r_up / (cfg->vcc - v_ntc);
+ if (r_ntc > tbl[0].resist || r_ntc < tbl[tbl_sz - 1].resist)
+ return -EINVAL;
+
+ while (!(r_ntc <= tbl[i].resist && r_ntc > tbl[i + 1].resist) &&
+ i < tbl_sz - 2)
+ i++;
+
+ /* return milli-Celsius */
+ *temp = tbl[i].temp * 1000 + ((tbl[i + 1].temp - tbl[i].temp) * 1000 *
+ (r_ntc - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist);
+
+ return 0;
+}
+
+static int ab8500_read_sensor(struct abx500_temp *data, u8 sensor, int *temp)
+{
+ int voltage, ret;
+ struct ab8500_temp *ab8500_data = data->plat_data;
+
+ if (sensor == BAT_CTRL) {
+ *temp = ab8500_btemp_get_batctrl_temp(ab8500_data->btemp);
+ } else if (sensor == BTEMP_BALL) {
+ *temp = ab8500_btemp_get_temp(ab8500_data->btemp);
+ } else {
+ voltage = ab8500_gpadc_convert(ab8500_data->gpadc, sensor);
+ if (voltage < 0)
+ return voltage;
+
+ ret = ab8500_voltage_to_temp(&ab8500_data->cfg, voltage, temp);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ab8500_thermal_power_off(struct work_struct *work)
+{
+ struct ab8500_temp *ab8500_data = container_of(work,
+ struct ab8500_temp, power_off_work.work);
+ struct abx500_temp *abx500_data = ab8500_data->abx500_data;
+
+ dev_warn(&abx500_data->pdev->dev, "Power off due to critical temp\n");
+
+ pm_power_off();
+}
+
+static ssize_t ab8500_show_name(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "ab8500\n");
+}
+
+static ssize_t ab8500_show_label(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ char *label;
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int index = attr->index;
+
+ switch (index) {
+ case 1:
+ label = "ext_adc1";
+ break;
+ case 2:
+ label = "ext_adc2";
+ break;
+ case 3:
+ label = "bat_temp";
+ break;
+ case 4:
+ label = "bat_ctrl";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sprintf(buf, "%s\n", label);
+}
+
+static int ab8500_temp_irq_handler(int irq, struct abx500_temp *data)
+{
+ struct ab8500_temp *ab8500_data = data->plat_data;
+
+ dev_warn(&data->pdev->dev, "Power off in %d s\n",
+ DEFAULT_POWER_OFF_DELAY / HZ);
+
+ schedule_delayed_work(&ab8500_data->power_off_work,
+ DEFAULT_POWER_OFF_DELAY);
+ return 0;
+}
+
+int abx500_hwmon_init(struct abx500_temp *data)
+{
+ struct ab8500_temp *ab8500_data;
+
+ ab8500_data = devm_kzalloc(&data->pdev->dev, sizeof(*ab8500_data),
+ GFP_KERNEL);
+ if (!ab8500_data)
+ return -ENOMEM;
+
+ ab8500_data->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ if (IS_ERR(ab8500_data->gpadc))
+ return PTR_ERR(ab8500_data->gpadc);
+
+ ab8500_data->btemp = ab8500_btemp_get();
+ if (IS_ERR(ab8500_data->btemp))
+ return PTR_ERR(ab8500_data->btemp);
+
+ INIT_DELAYED_WORK(&ab8500_data->power_off_work,
+ ab8500_thermal_power_off);
+
+ ab8500_data->cfg.vcc = THERMAL_VCC;
+ ab8500_data->cfg.r_up = PULL_UP_RESISTOR;
+ ab8500_data->cfg.temp_tbl = ab8500_temp_tbl_a_thermistor;
+ ab8500_data->cfg.tbl_sz = ab8500_temp_tbl_a_size;
+
+ data->plat_data = ab8500_data;
+
+ /*
+ * ADC_AUX1 and ADC_AUX2, connected to external NTC
+ * BTEMP_BALL and BAT_CTRL, fixed usage
+ */
+ data->gpadc_addr[0] = ADC_AUX1;
+ data->gpadc_addr[1] = ADC_AUX2;
+ data->gpadc_addr[2] = BTEMP_BALL;
+ data->gpadc_addr[3] = BAT_CTRL;
+ data->monitored_sensors = NUM_MONITORED_SENSORS;
+
+ data->ops.read_sensor = ab8500_read_sensor;
+ data->ops.irq_handler = ab8500_temp_irq_handler;
+ data->ops.show_name = ab8500_show_name;
+ data->ops.show_label = ab8500_show_label;
+ data->ops.is_visible = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL(abx500_hwmon_init);
+
+MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang@linaro.org>");
+MODULE_DESCRIPTION("AB8500 temperature driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/abituguru.c b/drivers/hwmon/abituguru.c
index 6119ff8e8c87..df0b69987914 100644
--- a/drivers/hwmon/abituguru.c
+++ b/drivers/hwmon/abituguru.c
@@ -96,9 +96,12 @@
#define ABIT_UGURU_MAX_TIMEOUTS 2
/* utility macros */
#define ABIT_UGURU_NAME "abituguru"
-#define ABIT_UGURU_DEBUG(level, format, arg...) \
- if (level <= verbose) \
- printk(KERN_DEBUG ABIT_UGURU_NAME ": " format , ## arg)
+#define ABIT_UGURU_DEBUG(level, format, arg...) \
+ do { \
+ if (level <= verbose) \
+ pr_debug(format , ## arg); \
+ } while (0)
+
/* Macros to help calculate the sysfs_names array length */
/*
* sum of strlen of: in??_input\0, in??_{min,max}\0, in??_{min,max}_alarm\0,
@@ -1533,7 +1536,7 @@ static int abituguru_resume(struct device *dev)
}
static SIMPLE_DEV_PM_OPS(abituguru_pm, abituguru_suspend, abituguru_resume);
-#define ABIT_UGURU_PM &abituguru_pm
+#define ABIT_UGURU_PM (&abituguru_pm)
#else
#define ABIT_UGURU_PM NULL
#endif /* CONFIG_PM */
diff --git a/drivers/hwmon/abituguru3.c b/drivers/hwmon/abituguru3.c
index 205327d33c4d..1d2da31c27c6 100644
--- a/drivers/hwmon/abituguru3.c
+++ b/drivers/hwmon/abituguru3.c
@@ -76,9 +76,11 @@
#define ABIT_UGURU3_SYNCHRONIZE_TIMEOUT 5
/* utility macros */
#define ABIT_UGURU3_NAME "abituguru3"
-#define ABIT_UGURU3_DEBUG(format, arg...) \
- if (verbose) \
- printk(KERN_DEBUG ABIT_UGURU3_NAME ": " format , ## arg)
+#define ABIT_UGURU3_DEBUG(format, arg...) \
+ do { \
+ if (verbose) \
+ pr_debug(format , ## arg); \
+ } while (0)
/* Macros to help calculate the sysfs_names array length */
#define ABIT_UGURU3_MAX_NO_SENSORS 26
@@ -1159,7 +1161,7 @@ static int abituguru3_resume(struct device *dev)
}
static SIMPLE_DEV_PM_OPS(abituguru3_pm, abituguru3_suspend, abituguru3_resume);
-#define ABIT_UGURU3_PM &abituguru3_pm
+#define ABIT_UGURU3_PM (&abituguru3_pm)
#else
#define ABIT_UGURU3_PM NULL
#endif /* CONFIG_PM */
diff --git a/drivers/hwmon/abx500.c b/drivers/hwmon/abx500.c
new file mode 100644
index 000000000000..b4ad87b31a37
--- /dev/null
+++ b/drivers/hwmon/abx500.c
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) ST-Ericsson 2010 - 2013
+ * Author: Martin Persson <martin.persson@stericsson.com>
+ * Hongbo Zhang <hongbo.zhang@linaro.org>
+ * License Terms: GNU General Public License v2
+ *
+ * ABX500 does not provide auto ADC, so to monitor the required temperatures,
+ * a periodic work is used. It is more important to not wake up the CPU than
+ * to perform this job, hence the use of a deferred delay.
+ *
+ * A deferred delay for thermal monitor is considered safe because:
+ * If the chip gets too hot during a sleep state it's most likely due to
+ * external factors, such as the surrounding temperature. I.e. no SW decisions
+ * will make any difference.
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+#include "abx500.h"
+
+#define DEFAULT_MONITOR_DELAY HZ
+#define DEFAULT_MAX_TEMP 130
+
+static inline void schedule_monitor(struct abx500_temp *data)
+{
+ data->work_active = true;
+ schedule_delayed_work(&data->work, DEFAULT_MONITOR_DELAY);
+}
+
+static void threshold_updated(struct abx500_temp *data)
+{
+ int i;
+ for (i = 0; i < data->monitored_sensors; i++)
+ if (data->max[i] != 0 || data->min[i] != 0) {
+ schedule_monitor(data);
+ return;
+ }
+
+ dev_dbg(&data->pdev->dev, "No active thresholds.\n");
+ cancel_delayed_work_sync(&data->work);
+ data->work_active = false;
+}
+
+static void gpadc_monitor(struct work_struct *work)
+{
+ int temp, i, ret;
+ char alarm_node[30];
+ bool updated_min_alarm, updated_max_alarm;
+ struct abx500_temp *data;
+
+ data = container_of(work, struct abx500_temp, work.work);
+ mutex_lock(&data->lock);
+
+ for (i = 0; i < data->monitored_sensors; i++) {
+ /* Thresholds are considered inactive if set to 0 */
+ if (data->max[i] == 0 && data->min[i] == 0)
+ continue;
+
+ if (data->max[i] < data->min[i])
+ continue;
+
+ ret = data->ops.read_sensor(data, data->gpadc_addr[i], &temp);
+ if (ret < 0) {
+ dev_err(&data->pdev->dev, "GPADC read failed\n");
+ continue;
+ }
+
+ updated_min_alarm = false;
+ updated_max_alarm = false;
+
+ if (data->min[i] != 0) {
+ if (temp < data->min[i]) {
+ if (data->min_alarm[i] == false) {
+ data->min_alarm[i] = true;
+ updated_min_alarm = true;
+ }
+ } else {
+ if (data->min_alarm[i] == true) {
+ data->min_alarm[i] = false;
+ updated_min_alarm = true;
+ }
+ }
+ }
+ if (data->max[i] != 0) {
+ if (temp > data->max[i]) {
+ if (data->max_alarm[i] == false) {
+ data->max_alarm[i] = true;
+ updated_max_alarm = true;
+ }
+ } else if (temp < data->max[i] - data->max_hyst[i]) {
+ if (data->max_alarm[i] == true) {
+ data->max_alarm[i] = false;
+ updated_max_alarm = true;
+ }
+ }
+ }
+
+ if (updated_min_alarm) {
+ ret = sprintf(alarm_node, "temp%d_min_alarm", i + 1);
+ sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
+ }
+ if (updated_max_alarm) {
+ ret = sprintf(alarm_node, "temp%d_max_alarm", i + 1);
+ sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
+ }
+ }
+
+ schedule_monitor(data);
+ mutex_unlock(&data->lock);
+}
+
+/* HWMON sysfs interfaces */
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ /* Show chip name */
+ return data->ops.show_name(dev, devattr, buf);
+}
+
+static ssize_t show_label(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ /* Show each sensor label */
+ return data->ops.show_label(dev, devattr, buf);
+}
+
+static ssize_t show_input(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int ret, temp;
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ u8 gpadc_addr = data->gpadc_addr[attr->index];
+
+ ret = data->ops.read_sensor(data, gpadc_addr, &temp);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", temp);
+}
+
+/* Set functions (RW nodes) */
+static ssize_t set_min(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int res = kstrtol(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ val = clamp_val(val, 0, DEFAULT_MAX_TEMP);
+
+ mutex_lock(&data->lock);
+ data->min[attr->index] = val;
+ threshold_updated(data);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t set_max(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int res = kstrtol(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ val = clamp_val(val, 0, DEFAULT_MAX_TEMP);
+
+ mutex_lock(&data->lock);
+ data->max[attr->index] = val;
+ threshold_updated(data);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t set_max_hyst(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int res = kstrtoul(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ val = clamp_val(val, 0, DEFAULT_MAX_TEMP);
+
+ mutex_lock(&data->lock);
+ data->max_hyst[attr->index] = val;
+ threshold_updated(data);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+/* Show functions (RO nodes) */
+static ssize_t show_min(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+ return sprintf(buf, "%ld\n", data->min[attr->index]);
+}
+
+static ssize_t show_max(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+ return sprintf(buf, "%ld\n", data->max[attr->index]);
+}
+
+static ssize_t show_max_hyst(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+ return sprintf(buf, "%ld\n", data->max_hyst[attr->index]);
+}
+
+static ssize_t show_min_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+ return sprintf(buf, "%d\n", data->min_alarm[attr->index]);
+}
+
+static ssize_t show_max_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+ return sprintf(buf, "%d\n", data->max_alarm[attr->index]);
+}
+
+static mode_t abx500_attrs_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct abx500_temp *data = dev_get_drvdata(dev);
+
+ if (data->ops.is_visible)
+ return data->ops.is_visible(attr, n);
+
+ return attr->mode;
+}
+
+/* Chip name, required by hwmon */
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+
+/* GPADC - SENSOR1 */
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_min, set_min, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_max, set_max, 0);
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
+ show_max_hyst, set_max_hyst, 0);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_min_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_max_alarm, NULL, 0);
+
+/* GPADC - SENSOR2 */
+static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_min, set_min, 1);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_max, set_max, 1);
+static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IWUSR | S_IRUGO,
+ show_max_hyst, set_max_hyst, 1);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_min_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_max_alarm, NULL, 1);
+
+/* GPADC - SENSOR3 */
+static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, show_label, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_input, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_min, set_min, 2);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_max, set_max, 2);
+static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IWUSR | S_IRUGO,
+ show_max_hyst, set_max_hyst, 2);
+static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_min_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_max_alarm, NULL, 2);
+
+/* GPADC - SENSOR4 */
+static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, show_label, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_input, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_min, S_IWUSR | S_IRUGO, show_min, set_min, 3);
+static SENSOR_DEVICE_ATTR(temp4_max, S_IWUSR | S_IRUGO, show_max, set_max, 3);
+static SENSOR_DEVICE_ATTR(temp4_max_hyst, S_IWUSR | S_IRUGO,
+ show_max_hyst, set_max_hyst, 3);
+static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_min_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_max_alarm, NULL, 3);
+
+struct attribute *abx500_temp_attributes[] = {
+ &sensor_dev_attr_name.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_label.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_temp2_label.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_temp3_label.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_temp4_label.dev_attr.attr,
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_min.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group abx500_temp_group = {
+ .attrs = abx500_temp_attributes,
+ .is_visible = abx500_attrs_visible,
+};
+
+static irqreturn_t abx500_temp_irq_handler(int irq, void *irq_data)
+{
+ struct platform_device *pdev = irq_data;
+ struct abx500_temp *data = platform_get_drvdata(pdev);
+
+ data->ops.irq_handler(irq, data);
+ return IRQ_HANDLED;
+}
+
+static int setup_irqs(struct platform_device *pdev)
+{
+ int ret;
+ int irq = platform_get_irq_byname(pdev, "ABX500_TEMP_WARM");
+
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Get irq by name failed\n");
+ return irq;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ abx500_temp_irq_handler, IRQF_NO_SUSPEND, "abx500-temp", pdev);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret);
+
+ return ret;
+}
+
+static int abx500_temp_probe(struct platform_device *pdev)
+{
+ struct abx500_temp *data;
+ int err;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->pdev = pdev;
+ mutex_init(&data->lock);
+
+ /* Chip specific initialization */
+ err = abx500_hwmon_init(data);
+ if (err < 0 || !data->ops.read_sensor || !data->ops.show_name ||
+ !data->ops.show_label)
+ return err;
+
+ INIT_DEFERRABLE_WORK(&data->work, gpadc_monitor);
+
+ platform_set_drvdata(pdev, data);
+
+ err = sysfs_create_group(&pdev->dev.kobj, &abx500_temp_group);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Create sysfs group failed (%d)\n", err);
+ return err;
+ }
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
+ goto exit_sysfs_group;
+ }
+
+ if (data->ops.irq_handler) {
+ err = setup_irqs(pdev);
+ if (err < 0)
+ goto exit_hwmon_reg;
+ }
+ return 0;
+
+exit_hwmon_reg:
+ hwmon_device_unregister(data->hwmon_dev);
+exit_sysfs_group:
+ sysfs_remove_group(&pdev->dev.kobj, &abx500_temp_group);
+ return err;
+}
+
+static int abx500_temp_remove(struct platform_device *pdev)
+{
+ struct abx500_temp *data = platform_get_drvdata(pdev);
+
+ cancel_delayed_work_sync(&data->work);
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&pdev->dev.kobj, &abx500_temp_group);
+
+ return 0;
+}
+
+static int abx500_temp_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct abx500_temp *data = platform_get_drvdata(pdev);
+
+ if (data->work_active)
+ cancel_delayed_work_sync(&data->work);
+
+ return 0;
+}
+
+static int abx500_temp_resume(struct platform_device *pdev)
+{
+ struct abx500_temp *data = platform_get_drvdata(pdev);
+
+ if (data->work_active)
+ schedule_monitor(data);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id abx500_temp_match[] = {
+ { .compatible = "stericsson,abx500-temp" },
+ {},
+};
+#endif
+
+static struct platform_driver abx500_temp_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "abx500-temp",
+ .of_match_table = of_match_ptr(abx500_temp_match),
+ },
+ .suspend = abx500_temp_suspend,
+ .resume = abx500_temp_resume,
+ .probe = abx500_temp_probe,
+ .remove = abx500_temp_remove,
+};
+
+module_platform_driver(abx500_temp_driver);
+
+MODULE_AUTHOR("Martin Persson <martin.persson@stericsson.com>");
+MODULE_DESCRIPTION("ABX500 temperature driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/abx500.h b/drivers/hwmon/abx500.h
new file mode 100644
index 000000000000..9b295e684d0e
--- /dev/null
+++ b/drivers/hwmon/abx500.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) ST-Ericsson 2010 - 2013
+ * License terms: GNU General Public License v2
+ * Author: Martin Persson <martin.persson@stericsson.com>
+ * Hongbo Zhang <hongbo.zhang@linaro.com>
+ */
+
+#ifndef _ABX500_H
+#define _ABX500_H
+
+#define NUM_SENSORS 5
+
+struct abx500_temp;
+
+/*
+ * struct abx500_temp_ops - abx500 chip specific ops
+ * @read_sensor: reads gpadc output
+ * @irq_handler: irq handler
+ * @show_name: hwmon device name
+ * @show_label: hwmon attribute label
+ * @is_visible: is attribute visible
+ */
+struct abx500_temp_ops {
+ int (*read_sensor)(struct abx500_temp *, u8, int *);
+ int (*irq_handler)(int, struct abx500_temp *);
+ ssize_t (*show_name)(struct device *,
+ struct device_attribute *, char *);
+ ssize_t (*show_label) (struct device *,
+ struct device_attribute *, char *);
+ int (*is_visible)(struct attribute *, int);
+};
+
+/*
+ * struct abx500_temp - representation of temp mon device
+ * @pdev: platform device
+ * @hwmon_dev: hwmon device
+ * @ops: abx500 chip specific ops
+ * @gpadc_addr: gpadc channel address
+ * @min: sensor temperature min value
+ * @max: sensor temperature max value
+ * @max_hyst: sensor temperature hysteresis value for max limit
+ * @min_alarm: sensor temperature min alarm
+ * @max_alarm: sensor temperature max alarm
+ * @work: delayed work scheduled to monitor temperature periodically
+ * @work_active: True if work is active
+ * @lock: mutex
+ * @monitored_sensors: number of monitored sensors
+ * @plat_data: private usage, usually points to platform specific data
+ */
+struct abx500_temp {
+ struct platform_device *pdev;
+ struct device *hwmon_dev;
+ struct abx500_temp_ops ops;
+ u8 gpadc_addr[NUM_SENSORS];
+ unsigned long min[NUM_SENSORS];
+ unsigned long max[NUM_SENSORS];
+ unsigned long max_hyst[NUM_SENSORS];
+ bool min_alarm[NUM_SENSORS];
+ bool max_alarm[NUM_SENSORS];
+ struct delayed_work work;
+ bool work_active;
+ struct mutex lock;
+ int monitored_sensors;
+ void *plat_data;
+};
+
+int abx500_hwmon_init(struct abx500_temp *data);
+
+#endif /* _ABX500_H */
diff --git a/drivers/hwmon/ad7314.c b/drivers/hwmon/ad7314.c
index a57584d28a40..f4f9b219bf16 100644
--- a/drivers/hwmon/ad7314.c
+++ b/drivers/hwmon/ad7314.c
@@ -116,7 +116,7 @@ static int ad7314_probe(struct spi_device *spi_dev)
if (chip == NULL)
return -ENOMEM;
- dev_set_drvdata(&spi_dev->dev, chip);
+ spi_set_drvdata(spi_dev, chip);
ret = sysfs_create_group(&spi_dev->dev.kobj, &ad7314_group);
if (ret < 0)
@@ -137,7 +137,7 @@ error_remove_group:
static int ad7314_remove(struct spi_device *spi_dev)
{
- struct ad7314_data *chip = dev_get_drvdata(&spi_dev->dev);
+ struct ad7314_data *chip = spi_get_drvdata(spi_dev);
hwmon_device_unregister(chip->hwmon_dev);
sysfs_remove_group(&spi_dev->dev.kobj, &ad7314_group);
@@ -166,6 +166,5 @@ static struct spi_driver ad7314_driver = {
module_spi_driver(ad7314_driver);
MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
-MODULE_DESCRIPTION("Analog Devices AD7314, ADT7301 and ADT7302 digital"
- " temperature sensor driver");
+MODULE_DESCRIPTION("Analog Devices AD7314, ADT7301 and ADT7302 digital temperature sensor driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c
index 71bcba8abfc0..7e76922a4ba9 100644
--- a/drivers/hwmon/adm1021.c
+++ b/drivers/hwmon/adm1021.c
@@ -312,8 +312,7 @@ static int adm1021_detect(struct i2c_client *client,
int conv_rate, status, config, man_id, dev_id;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
- pr_debug("adm1021: detect failed, "
- "smbus byte data not supported!\n");
+ pr_debug("detect failed, smbus byte data not supported!\n");
return -ENODEV;
}
@@ -324,7 +323,7 @@ static int adm1021_detect(struct i2c_client *client,
/* Check unused bits */
if ((status & 0x03) || (config & 0x3F) || (conv_rate & 0xF8)) {
- pr_debug("adm1021: detect failed, chip not detected!\n");
+ pr_debug("detect failed, chip not detected!\n");
return -ENODEV;
}
@@ -353,7 +352,7 @@ static int adm1021_detect(struct i2c_client *client,
else
type_name = "max1617";
- pr_debug("adm1021: Detected chip %s at adapter %d, address 0x%02x.\n",
+ pr_debug("Detected chip %s at adapter %d, address 0x%02x.\n",
type_name, i2c_adapter_id(adapter), client->addr);
strlcpy(info->type, type_name, I2C_NAME_SIZE);
@@ -368,10 +367,8 @@ static int adm1021_probe(struct i2c_client *client,
data = devm_kzalloc(&client->dev, sizeof(struct adm1021_data),
GFP_KERNEL);
- if (!data) {
- pr_debug("adm1021: detect failed, devm_kzalloc failed!\n");
+ if (!data)
return -ENOMEM;
- }
i2c_set_clientdata(client, data);
data->type = id->driver_data;
diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c
index ea09046e651d..3a6d9ef1c16c 100644
--- a/drivers/hwmon/adm1026.c
+++ b/drivers/hwmon/adm1026.c
@@ -49,14 +49,14 @@ static int gpio_fan[8] = { -1, -1, -1, -1, -1, -1, -1, -1 };
module_param_array(gpio_input, int, NULL, 0);
MODULE_PARM_DESC(gpio_input, "List of GPIO pins (0-16) to program as inputs");
module_param_array(gpio_output, int, NULL, 0);
-MODULE_PARM_DESC(gpio_output, "List of GPIO pins (0-16) to program as "
- "outputs");
+MODULE_PARM_DESC(gpio_output,
+ "List of GPIO pins (0-16) to program as outputs");
module_param_array(gpio_inverted, int, NULL, 0);
-MODULE_PARM_DESC(gpio_inverted, "List of GPIO pins (0-16) to program as "
- "inverted");
+MODULE_PARM_DESC(gpio_inverted,
+ "List of GPIO pins (0-16) to program as inverted");
module_param_array(gpio_normal, int, NULL, 0);
-MODULE_PARM_DESC(gpio_normal, "List of GPIO pins (0-16) to program as "
- "normal/non-inverted");
+MODULE_PARM_DESC(gpio_normal,
+ "List of GPIO pins (0-16) to program as normal/non-inverted");
module_param_array(gpio_fan, int, NULL, 0);
MODULE_PARM_DESC(gpio_fan, "List of GPIO pins (0-7) to program as fan tachs");
@@ -372,31 +372,31 @@ static void adm1026_init_client(struct i2c_client *client)
dev_dbg(&client->dev, "ADM1026_REG_CONFIG1 is: 0x%02x\n",
data->config1);
if ((data->config1 & CFG1_MONITOR) == 0) {
- dev_dbg(&client->dev, "Monitoring not currently "
- "enabled.\n");
+ dev_dbg(&client->dev,
+ "Monitoring not currently enabled.\n");
}
if (data->config1 & CFG1_INT_ENABLE) {
- dev_dbg(&client->dev, "SMBALERT interrupts are "
- "enabled.\n");
+ dev_dbg(&client->dev,
+ "SMBALERT interrupts are enabled.\n");
}
if (data->config1 & CFG1_AIN8_9) {
- dev_dbg(&client->dev, "in8 and in9 enabled. "
- "temp3 disabled.\n");
+ dev_dbg(&client->dev,
+ "in8 and in9 enabled. temp3 disabled.\n");
} else {
- dev_dbg(&client->dev, "temp3 enabled. in8 and "
- "in9 disabled.\n");
+ dev_dbg(&client->dev,
+ "temp3 enabled. in8 and in9 disabled.\n");
}
if (data->config1 & CFG1_THERM_HOT) {
- dev_dbg(&client->dev, "Automatic THERM, PWM, "
- "and temp limits enabled.\n");
+ dev_dbg(&client->dev,
+ "Automatic THERM, PWM, and temp limits enabled.\n");
}
if (data->config3 & CFG3_GPIO16_ENABLE) {
- dev_dbg(&client->dev, "GPIO16 enabled. THERM "
- "pin disabled.\n");
+ dev_dbg(&client->dev,
+ "GPIO16 enabled. THERM pin disabled.\n");
} else {
- dev_dbg(&client->dev, "THERM pin enabled. "
- "GPIO16 disabled.\n");
+ dev_dbg(&client->dev,
+ "THERM pin enabled. GPIO16 disabled.\n");
}
if (data->config3 & CFG3_VREF_250)
dev_dbg(&client->dev, "Vref is 2.50 Volts.\n");
@@ -1798,8 +1798,8 @@ static int adm1026_detect(struct i2c_client *client,
company = adm1026_read_value(client, ADM1026_REG_COMPANY);
verstep = adm1026_read_value(client, ADM1026_REG_VERSTEP);
- dev_dbg(&adapter->dev, "Detecting device at %d,0x%02x with"
- " COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
+ dev_dbg(&adapter->dev,
+ "Detecting device at %d,0x%02x with COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
i2c_adapter_id(client->adapter), client->addr,
company, verstep);
@@ -1811,11 +1811,12 @@ static int adm1026_detect(struct i2c_client *client,
/* Analog Devices ADM1026 */
} else if (company == ADM1026_COMPANY_ANALOG_DEV
&& (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
- dev_err(&adapter->dev, "Unrecognized stepping "
- "0x%02x. Defaulting to ADM1026.\n", verstep);
+ dev_err(&adapter->dev,
+ "Unrecognized stepping 0x%02x. Defaulting to ADM1026.\n",
+ verstep);
} else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
- dev_err(&adapter->dev, "Found version/stepping "
- "0x%02x. Assuming generic ADM1026.\n",
+ dev_err(&adapter->dev,
+ "Found version/stepping 0x%02x. Assuming generic ADM1026.\n",
verstep);
} else {
dev_dbg(&adapter->dev, "Autodetection failed\n");
diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c
index 97f4718382f6..9ee5e066423b 100644
--- a/drivers/hwmon/adm1029.c
+++ b/drivers/hwmon/adm1029.c
@@ -224,8 +224,9 @@ static ssize_t set_fan_div(struct device *dev,
break;
default:
mutex_unlock(&data->update_lock);
- dev_err(&client->dev, "fan_div value %ld not "
- "supported. Choose one of 1, 2 or 4!\n", val);
+ dev_err(&client->dev,
+ "fan_div value %ld not supported. Choose one of 1, 2 or 4!\n",
+ val);
return -EINVAL;
}
/* Update the value */
@@ -326,8 +327,8 @@ static int adm1029_detect(struct i2c_client *client,
* There are no "official" CHIP ID, so actually
* we use Major/Minor revision for that
*/
- pr_info("adm1029: Unknown major revision %x, "
- "please let us know\n", chip_id);
+ pr_info("Unknown major revision %x, please let us know\n",
+ chip_id);
return -ENODEV;
}
diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c
index 2416628e0ab1..086d02a9ecdc 100644
--- a/drivers/hwmon/adm9240.c
+++ b/drivers/hwmon/adm9240.c
@@ -351,8 +351,9 @@ static void adm9240_write_fan_div(struct i2c_client *client, int nr,
reg &= ~(3 << shift);
reg |= (fan_div << shift);
i2c_smbus_write_byte_data(client, ADM9240_REG_VID_FAN_DIV, reg);
- dev_dbg(&client->dev, "fan%d clock divider changed from %u "
- "to %u\n", nr + 1, 1 << old, 1 << fan_div);
+ dev_dbg(&client->dev,
+ "fan%d clock divider changed from %u to %u\n",
+ nr + 1, 1 << old, 1 << fan_div);
}
/*
@@ -699,8 +700,8 @@ static void adm9240_init_client(struct i2c_client *client)
/* start measurement cycle */
i2c_smbus_write_byte_data(client, ADM9240_REG_CONFIG, 1);
- dev_info(&client->dev, "cold start: config was 0x%02x "
- "mode %u\n", conf, mode);
+ dev_info(&client->dev,
+ "cold start: config was 0x%02x mode %u\n", conf, mode);
}
}
diff --git a/drivers/hwmon/ads7871.c b/drivers/hwmon/ads7871.c
index a79875986f91..3eff73b6220d 100644
--- a/drivers/hwmon/ads7871.c
+++ b/drivers/hwmon/ads7871.c
@@ -40,25 +40,25 @@
* the instruction byte
*/
/*Instruction Bit masks*/
-#define INST_MODE_bm (1<<7)
-#define INST_READ_bm (1<<6)
-#define INST_16BIT_bm (1<<5)
+#define INST_MODE_BM (1 << 7)
+#define INST_READ_BM (1 << 6)
+#define INST_16BIT_BM (1 << 5)
/*From figure 18 in the datasheet*/
/*bit masks for Rev/Oscillator Control Register*/
-#define MUX_CNV_bv 7
-#define MUX_CNV_bm (1<<MUX_CNV_bv)
-#define MUX_M3_bm (1<<3) /*M3 selects single ended*/
-#define MUX_G_bv 4 /*allows for reg = (gain << MUX_G_bv) | ...*/
+#define MUX_CNV_BV 7
+#define MUX_CNV_BM (1 << MUX_CNV_BV)
+#define MUX_M3_BM (1 << 3) /*M3 selects single ended*/
+#define MUX_G_BV 4 /*allows for reg = (gain << MUX_G_BV) | ...*/
/*From figure 18 in the datasheet*/
/*bit masks for Rev/Oscillator Control Register*/
-#define OSC_OSCR_bm (1<<5)
-#define OSC_OSCE_bm (1<<4)
-#define OSC_REFE_bm (1<<3)
-#define OSC_BUFE_bm (1<<2)
-#define OSC_R2V_bm (1<<1)
-#define OSC_RBG_bm (1<<0)
+#define OSC_OSCR_BM (1 << 5)
+#define OSC_OSCE_BM (1 << 4)
+#define OSC_REFE_BM (1 << 3)
+#define OSC_BUFE_BM (1 << 2)
+#define OSC_R2V_BM (1 << 1)
+#define OSC_RBG_BM (1 << 0)
#include <linux/module.h>
#include <linux/init.h>
@@ -79,7 +79,7 @@ struct ads7871_data {
static int ads7871_read_reg8(struct spi_device *spi, int reg)
{
int ret;
- reg = reg | INST_READ_bm;
+ reg = reg | INST_READ_BM;
ret = spi_w8r8(spi, reg);
return ret;
}
@@ -87,7 +87,7 @@ static int ads7871_read_reg8(struct spi_device *spi, int reg)
static int ads7871_read_reg16(struct spi_device *spi, int reg)
{
int ret;
- reg = reg | INST_READ_bm | INST_16BIT_bm;
+ reg = reg | INST_READ_BM | INST_16BIT_BM;
ret = spi_w8r16(spi, reg);
return ret;
}
@@ -111,13 +111,13 @@ static ssize_t show_voltage(struct device *dev,
* TODO: add support for conversions
* other than single ended with a gain of 1
*/
- /*MUX_M3_bm forces single ended*/
+ /*MUX_M3_BM forces single ended*/
/*This is also where the gain of the PGA would be set*/
ads7871_write_reg8(spi, REG_GAIN_MUX,
- (MUX_CNV_bm | MUX_M3_bm | channel));
+ (MUX_CNV_BM | MUX_M3_BM | channel));
ret = ads7871_read_reg8(spi, REG_GAIN_MUX);
- mux_cnv = ((ret & MUX_CNV_bm)>>MUX_CNV_bv);
+ mux_cnv = ((ret & MUX_CNV_BM) >> MUX_CNV_BV);
/*
* on 400MHz arm9 platform the conversion
* is already done when we do this test
@@ -125,14 +125,14 @@ static ssize_t show_voltage(struct device *dev,
while ((i < 2) && mux_cnv) {
i++;
ret = ads7871_read_reg8(spi, REG_GAIN_MUX);
- mux_cnv = ((ret & MUX_CNV_bm)>>MUX_CNV_bv);
+ mux_cnv = ((ret & MUX_CNV_BM) >> MUX_CNV_BV);
msleep_interruptible(1);
}
if (mux_cnv == 0) {
val = ads7871_read_reg16(spi, REG_LS_BYTE);
/*result in volts*10000 = (val/8192)*2.5*10000*/
- val = ((val>>2) * 25000) / 8192;
+ val = ((val >> 2) * 25000) / 8192;
return sprintf(buf, "%d\n", val);
} else {
return -1;
@@ -189,7 +189,7 @@ static int ads7871_probe(struct spi_device *spi)
ads7871_write_reg8(spi, REG_SER_CONTROL, 0);
ads7871_write_reg8(spi, REG_AD_CONTROL, 0);
- val = (OSC_OSCR_bm | OSC_OSCE_bm | OSC_REFE_bm | OSC_BUFE_bm);
+ val = (OSC_OSCR_BM | OSC_OSCE_BM | OSC_REFE_BM | OSC_BUFE_BM);
ads7871_write_reg8(spi, REG_OSC_CONTROL, val);
ret = ads7871_read_reg8(spi, REG_OSC_CONTROL);
diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c
new file mode 100644
index 000000000000..da5f0789fb97
--- /dev/null
+++ b/drivers/hwmon/adt7310.c
@@ -0,0 +1,123 @@
+/*
+ * ADT7310/ADT7310 digital temperature sensor driver
+ *
+ * Copyright 2012-2013 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spi/spi.h>
+#include <asm/unaligned.h>
+
+#include "adt7x10.h"
+
+#define ADT7310_STATUS 0
+#define ADT7310_CONFIG 1
+#define ADT7310_TEMPERATURE 2
+#define ADT7310_ID 3
+#define ADT7310_T_CRIT 4
+#define ADT7310_T_HYST 5
+#define ADT7310_T_ALARM_HIGH 6
+#define ADT7310_T_ALARM_LOW 7
+
+static const u8 adt7310_reg_table[] = {
+ [ADT7X10_TEMPERATURE] = ADT7310_TEMPERATURE,
+ [ADT7X10_STATUS] = ADT7310_STATUS,
+ [ADT7X10_CONFIG] = ADT7310_CONFIG,
+ [ADT7X10_T_ALARM_HIGH] = ADT7310_T_ALARM_HIGH,
+ [ADT7X10_T_ALARM_LOW] = ADT7310_T_ALARM_LOW,
+ [ADT7X10_T_CRIT] = ADT7310_T_CRIT,
+ [ADT7X10_T_HYST] = ADT7310_T_HYST,
+ [ADT7X10_ID] = ADT7310_ID,
+};
+
+#define ADT7310_CMD_REG_OFFSET 3
+#define ADT7310_CMD_READ 0x40
+
+#define AD7310_COMMAND(reg) (adt7310_reg_table[(reg)] << ADT7310_CMD_REG_OFFSET)
+
+static int adt7310_spi_read_word(struct device *dev, u8 reg)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ int ret;
+
+ ret = spi_w8r16(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ);
+ if (ret < 0)
+ return ret;
+
+ return be16_to_cpu((__force __be16)ret);
+}
+
+static int adt7310_spi_write_word(struct device *dev, u8 reg, u16 data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ u8 buf[3];
+
+ buf[0] = AD7310_COMMAND(reg);
+ put_unaligned_be16(data, &buf[1]);
+
+ return spi_write(spi, buf, sizeof(buf));
+}
+
+static int adt7310_spi_read_byte(struct device *dev, u8 reg)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ return spi_w8r8(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ);
+}
+
+static int adt7310_spi_write_byte(struct device *dev, u8 reg,
+ u8 data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ u8 buf[2];
+
+ buf[0] = AD7310_COMMAND(reg);
+ buf[1] = data;
+
+ return spi_write(spi, buf, sizeof(buf));
+}
+
+static const struct adt7x10_ops adt7310_spi_ops = {
+ .read_word = adt7310_spi_read_word,
+ .write_word = adt7310_spi_write_word,
+ .read_byte = adt7310_spi_read_byte,
+ .write_byte = adt7310_spi_write_byte,
+};
+
+static int adt7310_spi_probe(struct spi_device *spi)
+{
+ return adt7x10_probe(&spi->dev, spi_get_device_id(spi)->name, spi->irq,
+ &adt7310_spi_ops);
+}
+
+static int adt7310_spi_remove(struct spi_device *spi)
+{
+ return adt7x10_remove(&spi->dev, spi->irq);
+}
+
+static const struct spi_device_id adt7310_id[] = {
+ { "adt7310", 0 },
+ { "adt7320", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, adt7310_id);
+
+static struct spi_driver adt7310_driver = {
+ .driver = {
+ .name = "adt7310",
+ .owner = THIS_MODULE,
+ .pm = ADT7X10_DEV_PM_OPS,
+ },
+ .probe = adt7310_spi_probe,
+ .remove = adt7310_spi_remove,
+ .id_table = adt7310_id,
+};
+module_spi_driver(adt7310_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ADT7310/ADT7320 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
index 99a7290da0a3..0dc066a939b4 100644
--- a/drivers/hwmon/adt7410.c
+++ b/drivers/hwmon/adt7410.c
@@ -1,460 +1,80 @@
/*
- * adt7410.c - Part of lm_sensors, Linux kernel modules for hardware
- * monitoring
- * This driver handles the ADT7410 and compatible digital temperature sensors.
- * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22
- * based on lm75.c by Frodo Looijaard <frodol@dds.nl>
- * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.com>
+ * ADT7410/ADT7420 digital temperature sensor driver
*
- * 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.
+ * Copyright 2012-2013 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
*
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/jiffies.h>
#include <linux/i2c.h>
-#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-#include <linux/err.h>
-#include <linux/mutex.h>
-#include <linux/delay.h>
-
-/*
- * ADT7410 registers definition
- */
-
-#define ADT7410_TEMPERATURE 0
-#define ADT7410_STATUS 2
-#define ADT7410_CONFIG 3
-#define ADT7410_T_ALARM_HIGH 4
-#define ADT7410_T_ALARM_LOW 6
-#define ADT7410_T_CRIT 8
-#define ADT7410_T_HYST 0xA
-
-/*
- * ADT7410 status
- */
-#define ADT7410_STAT_T_LOW (1 << 4)
-#define ADT7410_STAT_T_HIGH (1 << 5)
-#define ADT7410_STAT_T_CRIT (1 << 6)
-#define ADT7410_STAT_NOT_RDY (1 << 7)
-
-/*
- * ADT7410 config
- */
-#define ADT7410_FAULT_QUEUE_MASK (1 << 0 | 1 << 1)
-#define ADT7410_CT_POLARITY (1 << 2)
-#define ADT7410_INT_POLARITY (1 << 3)
-#define ADT7410_EVENT_MODE (1 << 4)
-#define ADT7410_MODE_MASK (1 << 5 | 1 << 6)
-#define ADT7410_FULL (0 << 5 | 0 << 6)
-#define ADT7410_PD (1 << 5 | 1 << 6)
-#define ADT7410_RESOLUTION (1 << 7)
-
-/*
- * ADT7410 masks
- */
-#define ADT7410_T13_VALUE_MASK 0xFFF8
-#define ADT7410_T_HYST_MASK 0xF
-
-/* straight from the datasheet */
-#define ADT7410_TEMP_MIN (-55000)
-#define ADT7410_TEMP_MAX 150000
-
-enum adt7410_type { /* keep sorted in alphabetical order */
- adt7410,
-};
-
-static const u8 ADT7410_REG_TEMP[4] = {
- ADT7410_TEMPERATURE, /* input */
- ADT7410_T_ALARM_HIGH, /* high */
- ADT7410_T_ALARM_LOW, /* low */
- ADT7410_T_CRIT, /* critical */
-};
-
-/* Each client has this additional data */
-struct adt7410_data {
- struct device *hwmon_dev;
- struct mutex update_lock;
- u8 config;
- u8 oldconfig;
- bool valid; /* true if registers valid */
- unsigned long last_updated; /* In jiffies */
- s16 temp[4]; /* Register values,
- 0 = input
- 1 = high
- 2 = low
- 3 = critical */
- u8 hyst; /* hysteresis offset */
-};
-
-/*
- * adt7410 register access by I2C
- */
-static int adt7410_temp_ready(struct i2c_client *client)
-{
- int i, status;
-
- for (i = 0; i < 6; i++) {
- status = i2c_smbus_read_byte_data(client, ADT7410_STATUS);
- if (status < 0)
- return status;
- if (!(status & ADT7410_STAT_NOT_RDY))
- return 0;
- msleep(60);
- }
- return -ETIMEDOUT;
-}
-
-static struct adt7410_data *adt7410_update_device(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct adt7410_data *data = i2c_get_clientdata(client);
- struct adt7410_data *ret = data;
- mutex_lock(&data->update_lock);
-
- if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
- || !data->valid) {
- int i, status;
- dev_dbg(&client->dev, "Starting update\n");
+#include "adt7x10.h"
- status = adt7410_temp_ready(client); /* check for new value */
- if (unlikely(status)) {
- ret = ERR_PTR(status);
- goto abort;
- }
- for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
- status = i2c_smbus_read_word_swapped(client,
- ADT7410_REG_TEMP[i]);
- if (unlikely(status < 0)) {
- dev_dbg(dev,
- "Failed to read value: reg %d, error %d\n",
- ADT7410_REG_TEMP[i], status);
- ret = ERR_PTR(status);
- goto abort;
- }
- data->temp[i] = status;
- }
- status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
- if (unlikely(status < 0)) {
- dev_dbg(dev,
- "Failed to read value: reg %d, error %d\n",
- ADT7410_T_HYST, status);
- ret = ERR_PTR(status);
- goto abort;
- }
- data->hyst = status;
- data->last_updated = jiffies;
- data->valid = true;
- }
-
-abort:
- mutex_unlock(&data->update_lock);
- return ret;
-}
-
-static s16 ADT7410_TEMP_TO_REG(long temp)
+static int adt7410_i2c_read_word(struct device *dev, u8 reg)
{
- return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7410_TEMP_MIN,
- ADT7410_TEMP_MAX) * 128, 1000);
+ return i2c_smbus_read_word_swapped(to_i2c_client(dev), reg);
}
-static int ADT7410_REG_TO_TEMP(struct adt7410_data *data, s16 reg)
+static int adt7410_i2c_write_word(struct device *dev, u8 reg, u16 data)
{
- /* in 13 bit mode, bits 0-2 are status flags - mask them out */
- if (!(data->config & ADT7410_RESOLUTION))
- reg &= ADT7410_T13_VALUE_MASK;
- /*
- * temperature is stored in twos complement format, in steps of
- * 1/128°C
- */
- return DIV_ROUND_CLOSEST(reg * 1000, 128);
+ return i2c_smbus_write_word_swapped(to_i2c_client(dev), reg, data);
}
-/*-----------------------------------------------------------------------*/
-
-/* sysfs attributes for hwmon */
-
-static ssize_t adt7410_show_temp(struct device *dev,
- struct device_attribute *da, char *buf)
+static int adt7410_i2c_read_byte(struct device *dev, u8 reg)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
- struct adt7410_data *data = adt7410_update_device(dev);
-
- if (IS_ERR(data))
- return PTR_ERR(data);
-
- return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data,
- data->temp[attr->index]));
+ return i2c_smbus_read_byte_data(to_i2c_client(dev), reg);
}
-static ssize_t adt7410_set_temp(struct device *dev,
- struct device_attribute *da,
- const char *buf, size_t count)
+static int adt7410_i2c_write_byte(struct device *dev, u8 reg, u8 data)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
- struct i2c_client *client = to_i2c_client(dev);
- struct adt7410_data *data = i2c_get_clientdata(client);
- int nr = attr->index;
- long temp;
- int ret;
-
- ret = kstrtol(buf, 10, &temp);
- if (ret)
- return ret;
-
- mutex_lock(&data->update_lock);
- data->temp[nr] = ADT7410_TEMP_TO_REG(temp);
- ret = i2c_smbus_write_word_swapped(client, ADT7410_REG_TEMP[nr],
- data->temp[nr]);
- if (ret)
- count = ret;
- mutex_unlock(&data->update_lock);
- return count;
+ return i2c_smbus_write_byte_data(to_i2c_client(dev), reg, data);
}
-static ssize_t adt7410_show_t_hyst(struct device *dev,
- struct device_attribute *da,
- char *buf)
-{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
- struct adt7410_data *data;
- int nr = attr->index;
- int hyst;
-
- data = adt7410_update_device(dev);
- if (IS_ERR(data))
- return PTR_ERR(data);
- hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;
-
- /*
- * hysteresis is stored as a 4 bit offset in the device, convert it
- * to an absolute value
- */
- if (nr == 2) /* min has positive offset, others have negative */
- hyst = -hyst;
- return sprintf(buf, "%d\n",
- ADT7410_REG_TO_TEMP(data, data->temp[nr]) - hyst);
-}
-
-static ssize_t adt7410_set_t_hyst(struct device *dev,
- struct device_attribute *da,
- const char *buf, size_t count)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct adt7410_data *data = i2c_get_clientdata(client);
- int limit, ret;
- long hyst;
-
- ret = kstrtol(buf, 10, &hyst);
- if (ret)
- return ret;
- /* convert absolute hysteresis value to a 4 bit delta value */
- limit = ADT7410_REG_TO_TEMP(data, data->temp[1]);
- hyst = clamp_val(hyst, ADT7410_TEMP_MIN, ADT7410_TEMP_MAX);
- data->hyst = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000), 0,
- ADT7410_T_HYST_MASK);
- ret = i2c_smbus_write_byte_data(client, ADT7410_T_HYST, data->hyst);
- if (ret)
- return ret;
-
- return count;
-}
-
-static ssize_t adt7410_show_alarm(struct device *dev,
- struct device_attribute *da,
- char *buf)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
- int ret;
-
- ret = i2c_smbus_read_byte_data(client, ADT7410_STATUS);
- if (ret < 0)
- return ret;
-
- return sprintf(buf, "%d\n", !!(ret & attr->index));
-}
-
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7410_show_temp, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
- adt7410_show_temp, adt7410_set_temp, 1);
-static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
- adt7410_show_temp, adt7410_set_temp, 2);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
- adt7410_show_temp, adt7410_set_temp, 3);
-static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
- adt7410_show_t_hyst, adt7410_set_t_hyst, 1);
-static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO,
- adt7410_show_t_hyst, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO,
- adt7410_show_t_hyst, NULL, 3);
-static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, adt7410_show_alarm,
- NULL, ADT7410_STAT_T_LOW);
-static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adt7410_show_alarm,
- NULL, ADT7410_STAT_T_HIGH);
-static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, adt7410_show_alarm,
- NULL, ADT7410_STAT_T_CRIT);
-
-static struct attribute *adt7410_attributes[] = {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp1_max.dev_attr.attr,
- &sensor_dev_attr_temp1_min.dev_attr.attr,
- &sensor_dev_attr_temp1_crit.dev_attr.attr,
- &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
- &sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
- &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
- &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
- NULL
+static const struct adt7x10_ops adt7410_i2c_ops = {
+ .read_word = adt7410_i2c_read_word,
+ .write_word = adt7410_i2c_write_word,
+ .read_byte = adt7410_i2c_read_byte,
+ .write_byte = adt7410_i2c_write_byte,
};
-static const struct attribute_group adt7410_group = {
- .attrs = adt7410_attributes,
-};
-
-/*-----------------------------------------------------------------------*/
-
-/* device probe and removal */
-
-static int adt7410_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adt7410_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- struct adt7410_data *data;
- int ret;
-
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
- data = devm_kzalloc(&client->dev, sizeof(struct adt7410_data),
- GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- i2c_set_clientdata(client, data);
- mutex_init(&data->update_lock);
-
- /* configure as specified */
- ret = i2c_smbus_read_byte_data(client, ADT7410_CONFIG);
- if (ret < 0) {
- dev_dbg(&client->dev, "Can't read config? %d\n", ret);
- return ret;
- }
- data->oldconfig = ret;
- /*
- * Set to 16 bit resolution, continous conversion and comparator mode.
- */
- ret &= ~ADT7410_MODE_MASK;
- data->config = ret | ADT7410_FULL | ADT7410_RESOLUTION |
- ADT7410_EVENT_MODE;
- if (data->config != data->oldconfig) {
- ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
- data->config);
- if (ret)
- return ret;
- }
- dev_dbg(&client->dev, "Config %02x\n", data->config);
-
- /* Register sysfs hooks */
- ret = sysfs_create_group(&client->dev.kobj, &adt7410_group);
- if (ret)
- goto exit_restore;
-
- data->hwmon_dev = hwmon_device_register(&client->dev);
- if (IS_ERR(data->hwmon_dev)) {
- ret = PTR_ERR(data->hwmon_dev);
- goto exit_remove;
- }
-
- dev_info(&client->dev, "sensor '%s'\n", client->name);
-
- return 0;
-
-exit_remove:
- sysfs_remove_group(&client->dev.kobj, &adt7410_group);
-exit_restore:
- i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->oldconfig);
- return ret;
+ return adt7x10_probe(&client->dev, NULL, client->irq, &adt7410_i2c_ops);
}
-static int adt7410_remove(struct i2c_client *client)
+static int adt7410_i2c_remove(struct i2c_client *client)
{
- struct adt7410_data *data = i2c_get_clientdata(client);
-
- hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&client->dev.kobj, &adt7410_group);
- if (data->oldconfig != data->config)
- i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
- data->oldconfig);
- return 0;
+ return adt7x10_remove(&client->dev, client->irq);
}
static const struct i2c_device_id adt7410_ids[] = {
- { "adt7410", adt7410, },
- { "adt7420", adt7410, },
- { /* LIST END */ }
+ { "adt7410", 0 },
+ { "adt7420", 0 },
+ {}
};
MODULE_DEVICE_TABLE(i2c, adt7410_ids);
-#ifdef CONFIG_PM_SLEEP
-static int adt7410_suspend(struct device *dev)
-{
- int ret;
- struct i2c_client *client = to_i2c_client(dev);
- struct adt7410_data *data = i2c_get_clientdata(client);
-
- ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
- data->config | ADT7410_PD);
- return ret;
-}
-
-static int adt7410_resume(struct device *dev)
-{
- int ret;
- struct i2c_client *client = to_i2c_client(dev);
- struct adt7410_data *data = i2c_get_clientdata(client);
-
- ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->config);
- return ret;
-}
-
-static SIMPLE_DEV_PM_OPS(adt7410_dev_pm_ops, adt7410_suspend, adt7410_resume);
-
-#define ADT7410_DEV_PM_OPS (&adt7410_dev_pm_ops)
-#else
-#define ADT7410_DEV_PM_OPS NULL
-#endif /* CONFIG_PM */
-
static struct i2c_driver adt7410_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "adt7410",
- .pm = ADT7410_DEV_PM_OPS,
+ .pm = ADT7X10_DEV_PM_OPS,
},
- .probe = adt7410_probe,
- .remove = adt7410_remove,
+ .probe = adt7410_i2c_probe,
+ .remove = adt7410_i2c_remove,
.id_table = adt7410_ids,
.address_list = I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b),
};
-
module_i2c_driver(adt7410_driver);
-MODULE_AUTHOR("Hartmut Knaack");
-MODULE_DESCRIPTION("ADT7410/ADT7420 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ADT7410/AD7420 driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c
index 34ff03abb50b..d9299dee37d1 100644
--- a/drivers/hwmon/adt7411.c
+++ b/drivers/hwmon/adt7411.c
@@ -259,15 +259,17 @@ static int adt7411_detect(struct i2c_client *client,
val = i2c_smbus_read_byte_data(client, ADT7411_REG_MANUFACTURER_ID);
if (val < 0 || val != ADT7411_MANUFACTURER_ID) {
- dev_dbg(&client->dev, "Wrong manufacturer ID. Got %d, "
- "expected %d\n", val, ADT7411_MANUFACTURER_ID);
+ dev_dbg(&client->dev,
+ "Wrong manufacturer ID. Got %d, expected %d\n",
+ val, ADT7411_MANUFACTURER_ID);
return -ENODEV;
}
val = i2c_smbus_read_byte_data(client, ADT7411_REG_DEVICE_ID);
if (val < 0 || val != ADT7411_DEVICE_ID) {
- dev_dbg(&client->dev, "Wrong device ID. Got %d, "
- "expected %d\n", val, ADT7411_DEVICE_ID);
+ dev_dbg(&client->dev,
+ "Wrong device ID. Got %d, expected %d\n",
+ val, ADT7411_DEVICE_ID);
return -ENODEV;
}
diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c
new file mode 100644
index 000000000000..98141f483165
--- /dev/null
+++ b/drivers/hwmon/adt7x10.c
@@ -0,0 +1,511 @@
+/*
+ * adt7x10.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * This driver handles the ADT7410 and compatible digital temperature sensors.
+ * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22
+ * based on lm75.c by Frodo Looijaard <frodol@dds.nl>
+ * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include "adt7x10.h"
+
+/*
+ * ADT7X10 status
+ */
+#define ADT7X10_STAT_T_LOW (1 << 4)
+#define ADT7X10_STAT_T_HIGH (1 << 5)
+#define ADT7X10_STAT_T_CRIT (1 << 6)
+#define ADT7X10_STAT_NOT_RDY (1 << 7)
+
+/*
+ * ADT7X10 config
+ */
+#define ADT7X10_FAULT_QUEUE_MASK (1 << 0 | 1 << 1)
+#define ADT7X10_CT_POLARITY (1 << 2)
+#define ADT7X10_INT_POLARITY (1 << 3)
+#define ADT7X10_EVENT_MODE (1 << 4)
+#define ADT7X10_MODE_MASK (1 << 5 | 1 << 6)
+#define ADT7X10_FULL (0 << 5 | 0 << 6)
+#define ADT7X10_PD (1 << 5 | 1 << 6)
+#define ADT7X10_RESOLUTION (1 << 7)
+
+/*
+ * ADT7X10 masks
+ */
+#define ADT7X10_T13_VALUE_MASK 0xFFF8
+#define ADT7X10_T_HYST_MASK 0xF
+
+/* straight from the datasheet */
+#define ADT7X10_TEMP_MIN (-55000)
+#define ADT7X10_TEMP_MAX 150000
+
+/* Each client has this additional data */
+struct adt7x10_data {
+ const struct adt7x10_ops *ops;
+ const char *name;
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+ u8 config;
+ u8 oldconfig;
+ bool valid; /* true if registers valid */
+ unsigned long last_updated; /* In jiffies */
+ s16 temp[4]; /* Register values,
+ 0 = input
+ 1 = high
+ 2 = low
+ 3 = critical */
+ u8 hyst; /* hysteresis offset */
+};
+
+static int adt7x10_read_byte(struct device *dev, u8 reg)
+{
+ struct adt7x10_data *d = dev_get_drvdata(dev);
+ return d->ops->read_byte(dev, reg);
+}
+
+static int adt7x10_write_byte(struct device *dev, u8 reg, u8 data)
+{
+ struct adt7x10_data *d = dev_get_drvdata(dev);
+ return d->ops->write_byte(dev, reg, data);
+}
+
+static int adt7x10_read_word(struct device *dev, u8 reg)
+{
+ struct adt7x10_data *d = dev_get_drvdata(dev);
+ return d->ops->read_word(dev, reg);
+}
+
+static int adt7x10_write_word(struct device *dev, u8 reg, u16 data)
+{
+ struct adt7x10_data *d = dev_get_drvdata(dev);
+ return d->ops->write_word(dev, reg, data);
+}
+
+static const u8 ADT7X10_REG_TEMP[4] = {
+ ADT7X10_TEMPERATURE, /* input */
+ ADT7X10_T_ALARM_HIGH, /* high */
+ ADT7X10_T_ALARM_LOW, /* low */
+ ADT7X10_T_CRIT, /* critical */
+};
+
+static irqreturn_t adt7x10_irq_handler(int irq, void *private)
+{
+ struct device *dev = private;
+ int status;
+
+ status = adt7x10_read_byte(dev, ADT7X10_STATUS);
+ if (status < 0)
+ return IRQ_HANDLED;
+
+ if (status & ADT7X10_STAT_T_HIGH)
+ sysfs_notify(&dev->kobj, NULL, "temp1_max_alarm");
+ if (status & ADT7X10_STAT_T_LOW)
+ sysfs_notify(&dev->kobj, NULL, "temp1_min_alarm");
+ if (status & ADT7X10_STAT_T_CRIT)
+ sysfs_notify(&dev->kobj, NULL, "temp1_crit_alarm");
+
+ return IRQ_HANDLED;
+}
+
+static int adt7x10_temp_ready(struct device *dev)
+{
+ int i, status;
+
+ for (i = 0; i < 6; i++) {
+ status = adt7x10_read_byte(dev, ADT7X10_STATUS);
+ if (status < 0)
+ return status;
+ if (!(status & ADT7X10_STAT_NOT_RDY))
+ return 0;
+ msleep(60);
+ }
+ return -ETIMEDOUT;
+}
+
+static int adt7x10_update_temp(struct device *dev)
+{
+ struct adt7x10_data *data = dev_get_drvdata(dev);
+ int ret = 0;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+ || !data->valid) {
+ int temp;
+
+ dev_dbg(dev, "Starting update\n");
+
+ ret = adt7x10_temp_ready(dev); /* check for new value */
+ if (ret)
+ goto abort;
+
+ temp = adt7x10_read_word(dev, ADT7X10_REG_TEMP[0]);
+ if (temp < 0) {
+ ret = temp;
+ dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
+ ADT7X10_REG_TEMP[0], ret);
+ goto abort;
+ }
+ data->temp[0] = temp;
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+abort:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static int adt7x10_fill_cache(struct device *dev)
+{
+ struct adt7x10_data *data = dev_get_drvdata(dev);
+ int ret;
+ int i;
+
+ for (i = 1; i < ARRAY_SIZE(data->temp); i++) {
+ ret = adt7x10_read_word(dev, ADT7X10_REG_TEMP[i]);
+ if (ret < 0) {
+ dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
+ ADT7X10_REG_TEMP[i], ret);
+ return ret;
+ }
+ data->temp[i] = ret;
+ }
+
+ ret = adt7x10_read_byte(dev, ADT7X10_T_HYST);
+ if (ret < 0) {
+ dev_dbg(dev, "Failed to read value: reg %d, error %d\n",
+ ADT7X10_T_HYST, ret);
+ return ret;
+ }
+ data->hyst = ret;
+
+ return 0;
+}
+
+static s16 ADT7X10_TEMP_TO_REG(long temp)
+{
+ return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7X10_TEMP_MIN,
+ ADT7X10_TEMP_MAX) * 128, 1000);
+}
+
+static int ADT7X10_REG_TO_TEMP(struct adt7x10_data *data, s16 reg)
+{
+ /* in 13 bit mode, bits 0-2 are status flags - mask them out */
+ if (!(data->config & ADT7X10_RESOLUTION))
+ reg &= ADT7X10_T13_VALUE_MASK;
+ /*
+ * temperature is stored in twos complement format, in steps of
+ * 1/128°C
+ */
+ return DIV_ROUND_CLOSEST(reg * 1000, 128);
+}
+
+/*-----------------------------------------------------------------------*/
+
+/* sysfs attributes for hwmon */
+
+static ssize_t adt7x10_show_temp(struct device *dev,
+ struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct adt7x10_data *data = dev_get_drvdata(dev);
+
+
+ if (attr->index == 0) {
+ int ret;
+
+ ret = adt7x10_update_temp(dev);
+ if (ret)
+ return ret;
+ }
+
+ return sprintf(buf, "%d\n", ADT7X10_REG_TO_TEMP(data,
+ data->temp[attr->index]));
+}
+
+static ssize_t adt7x10_set_temp(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct adt7x10_data *data = dev_get_drvdata(dev);
+ int nr = attr->index;
+ long temp;
+ int ret;
+
+ ret = kstrtol(buf, 10, &temp);
+ if (ret)
+ return ret;
+
+ mutex_lock(&data->update_lock);
+ data->temp[nr] = ADT7X10_TEMP_TO_REG(temp);
+ ret = adt7x10_write_word(dev, ADT7X10_REG_TEMP[nr], data->temp[nr]);
+ if (ret)
+ count = ret;
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t adt7x10_show_t_hyst(struct device *dev,
+ struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct adt7x10_data *data = dev_get_drvdata(dev);
+ int nr = attr->index;
+ int hyst;
+
+ hyst = (data->hyst & ADT7X10_T_HYST_MASK) * 1000;
+
+ /*
+ * hysteresis is stored as a 4 bit offset in the device, convert it
+ * to an absolute value
+ */
+ if (nr == 2) /* min has positive offset, others have negative */
+ hyst = -hyst;
+ return sprintf(buf, "%d\n",
+ ADT7X10_REG_TO_TEMP(data, data->temp[nr]) - hyst);
+}
+
+static ssize_t adt7x10_set_t_hyst(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct adt7x10_data *data = dev_get_drvdata(dev);
+ int limit, ret;
+ long hyst;
+
+ ret = kstrtol(buf, 10, &hyst);
+ if (ret)
+ return ret;
+ /* convert absolute hysteresis value to a 4 bit delta value */
+ limit = ADT7X10_REG_TO_TEMP(data, data->temp[1]);
+ hyst = clamp_val(hyst, ADT7X10_TEMP_MIN, ADT7X10_TEMP_MAX);
+ data->hyst = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000),
+ 0, ADT7X10_T_HYST_MASK);
+ ret = adt7x10_write_byte(dev, ADT7X10_T_HYST, data->hyst);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t adt7x10_show_alarm(struct device *dev,
+ struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int ret;
+
+ ret = adt7x10_read_byte(dev, ADT7X10_STATUS);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", !!(ret & attr->index));
+}
+
+static ssize_t adt7x10_show_name(struct device *dev,
+ struct device_attribute *da,
+ char *buf)
+{
+ struct adt7x10_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", data->name);
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7x10_show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
+ adt7x10_show_temp, adt7x10_set_temp, 1);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
+ adt7x10_show_temp, adt7x10_set_temp, 2);
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
+ adt7x10_show_temp, adt7x10_set_temp, 3);
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
+ adt7x10_show_t_hyst, adt7x10_set_t_hyst, 1);
+static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO,
+ adt7x10_show_t_hyst, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO,
+ adt7x10_show_t_hyst, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, adt7x10_show_alarm,
+ NULL, ADT7X10_STAT_T_LOW);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adt7x10_show_alarm,
+ NULL, ADT7X10_STAT_T_HIGH);
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, adt7x10_show_alarm,
+ NULL, ADT7X10_STAT_T_CRIT);
+static DEVICE_ATTR(name, S_IRUGO, adt7x10_show_name, NULL);
+
+static struct attribute *adt7x10_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group adt7x10_group = {
+ .attrs = adt7x10_attributes,
+};
+
+int adt7x10_probe(struct device *dev, const char *name, int irq,
+ const struct adt7x10_ops *ops)
+{
+ struct adt7x10_data *data;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->ops = ops;
+ data->name = name;
+
+ dev_set_drvdata(dev, data);
+ mutex_init(&data->update_lock);
+
+ /* configure as specified */
+ ret = adt7x10_read_byte(dev, ADT7X10_CONFIG);
+ if (ret < 0) {
+ dev_dbg(dev, "Can't read config? %d\n", ret);
+ return ret;
+ }
+ data->oldconfig = ret;
+
+ /*
+ * Set to 16 bit resolution, continous conversion and comparator mode.
+ */
+ data->config = data->oldconfig;
+ data->config &= ~(ADT7X10_MODE_MASK | ADT7X10_CT_POLARITY |
+ ADT7X10_INT_POLARITY);
+ data->config |= ADT7X10_FULL | ADT7X10_RESOLUTION | ADT7X10_EVENT_MODE;
+
+ if (data->config != data->oldconfig) {
+ ret = adt7x10_write_byte(dev, ADT7X10_CONFIG, data->config);
+ if (ret)
+ return ret;
+ }
+ dev_dbg(dev, "Config %02x\n", data->config);
+
+ ret = adt7x10_fill_cache(dev);
+ if (ret)
+ goto exit_restore;
+
+ /* Register sysfs hooks */
+ ret = sysfs_create_group(&dev->kobj, &adt7x10_group);
+ if (ret)
+ goto exit_restore;
+
+ /*
+ * The I2C device will already have it's own 'name' attribute, but for
+ * the SPI device we need to register it. name will only be non NULL if
+ * the device doesn't register the 'name' attribute on its own.
+ */
+ if (name) {
+ ret = device_create_file(dev, &dev_attr_name);
+ if (ret)
+ goto exit_remove;
+ }
+
+ data->hwmon_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ ret = PTR_ERR(data->hwmon_dev);
+ goto exit_remove_name;
+ }
+
+ if (irq > 0) {
+ ret = request_threaded_irq(irq, NULL, adt7x10_irq_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ dev_name(dev), dev);
+ if (ret)
+ goto exit_hwmon_device_unregister;
+ }
+
+ return 0;
+
+exit_hwmon_device_unregister:
+ hwmon_device_unregister(data->hwmon_dev);
+exit_remove_name:
+ if (name)
+ device_remove_file(dev, &dev_attr_name);
+exit_remove:
+ sysfs_remove_group(&dev->kobj, &adt7x10_group);
+exit_restore:
+ adt7x10_write_byte(dev, ADT7X10_CONFIG, data->oldconfig);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(adt7x10_probe);
+
+int adt7x10_remove(struct device *dev, int irq)
+{
+ struct adt7x10_data *data = dev_get_drvdata(dev);
+
+ if (irq > 0)
+ free_irq(irq, dev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ if (data->name)
+ device_remove_file(dev, &dev_attr_name);
+ sysfs_remove_group(&dev->kobj, &adt7x10_group);
+ if (data->oldconfig != data->config)
+ adt7x10_write_byte(dev, ADT7X10_CONFIG, data->oldconfig);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(adt7x10_remove);
+
+#ifdef CONFIG_PM_SLEEP
+
+static int adt7x10_suspend(struct device *dev)
+{
+ struct adt7x10_data *data = dev_get_drvdata(dev);
+
+ return adt7x10_write_byte(dev, ADT7X10_CONFIG,
+ data->config | ADT7X10_PD);
+}
+
+static int adt7x10_resume(struct device *dev)
+{
+ struct adt7x10_data *data = dev_get_drvdata(dev);
+
+ return adt7x10_write_byte(dev, ADT7X10_CONFIG, data->config);
+}
+
+SIMPLE_DEV_PM_OPS(adt7x10_dev_pm_ops, adt7x10_suspend, adt7x10_resume);
+EXPORT_SYMBOL_GPL(adt7x10_dev_pm_ops);
+
+#endif /* CONFIG_PM_SLEEP */
+
+MODULE_AUTHOR("Hartmut Knaack");
+MODULE_DESCRIPTION("ADT7410/ADT7420, ADT7310/ADT7320 common code");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h
new file mode 100644
index 000000000000..d491c698529e
--- /dev/null
+++ b/drivers/hwmon/adt7x10.h
@@ -0,0 +1,37 @@
+#ifndef __HWMON_ADT7X10_H__
+#define __HWMON_ADT7X10_H__
+
+#include <linux/types.h>
+#include <linux/pm.h>
+
+/* ADT7410 registers definition */
+#define ADT7X10_TEMPERATURE 0
+#define ADT7X10_STATUS 2
+#define ADT7X10_CONFIG 3
+#define ADT7X10_T_ALARM_HIGH 4
+#define ADT7X10_T_ALARM_LOW 6
+#define ADT7X10_T_CRIT 8
+#define ADT7X10_T_HYST 0xA
+#define ADT7X10_ID 0xB
+
+struct device;
+
+struct adt7x10_ops {
+ int (*read_byte)(struct device *, u8 reg);
+ int (*write_byte)(struct device *, u8 reg, u8 data);
+ int (*read_word)(struct device *, u8 reg);
+ int (*write_word)(struct device *, u8 reg, u16 data);
+};
+
+int adt7x10_probe(struct device *dev, const char *name, int irq,
+ const struct adt7x10_ops *ops);
+int adt7x10_remove(struct device *dev, int irq);
+
+#ifdef CONFIG_PM_SLEEP
+extern const struct dev_pm_ops adt7x10_dev_pm_ops;
+#define ADT7X10_DEV_PM_OPS (&adt7x10_dev_pm_ops)
+#else
+#define ADT7X10_DEV_PM_OPS NULL
+#endif
+
+#endif
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index b41baffa20f0..62c2e32e25ef 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -922,7 +922,7 @@ static void applesmc_brightness_set(struct led_classdev *led_cdev,
ret = queue_work(applesmc_led_wq, &backlight_work);
if (debug && (!ret))
- printk(KERN_DEBUG "applesmc: work was already on the queue.\n");
+ dev_dbg(led_cdev->dev, "work was already on the queue.\n");
}
static ssize_t applesmc_key_count_show(struct device *dev,
diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c
index 6ac612cabda1..f96063680e58 100644
--- a/drivers/hwmon/asb100.c
+++ b/drivers/hwmon/asb100.c
@@ -55,8 +55,8 @@ static const unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END };
static unsigned short force_subclients[4];
module_param_array(force_subclients, short, NULL, 0);
-MODULE_PARM_DESC(force_subclients, "List of subclient addresses: "
- "{bus, clientaddr, subclientaddr1, subclientaddr2}");
+MODULE_PARM_DESC(force_subclients,
+ "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}");
/* Voltage IN registers 0-6 */
#define ASB100_REG_IN(nr) (0x20 + (nr))
@@ -689,8 +689,8 @@ static int asb100_detect_subclients(struct i2c_client *client)
for (i = 2; i <= 3; i++) {
if (force_subclients[i] < 0x48 ||
force_subclients[i] > 0x4f) {
- dev_err(&client->dev, "invalid subclient "
- "address %d; must be 0x48-0x4f\n",
+ dev_err(&client->dev,
+ "invalid subclient address %d; must be 0x48-0x4f\n",
force_subclients[i]);
err = -ENODEV;
goto ERROR_SC_2;
@@ -708,24 +708,27 @@ static int asb100_detect_subclients(struct i2c_client *client)
}
if (sc_addr[0] == sc_addr[1]) {
- dev_err(&client->dev, "duplicate addresses 0x%x "
- "for subclients\n", sc_addr[0]);
+ dev_err(&client->dev,
+ "duplicate addresses 0x%x for subclients\n",
+ sc_addr[0]);
err = -ENODEV;
goto ERROR_SC_2;
}
data->lm75[0] = i2c_new_dummy(adapter, sc_addr[0]);
if (!data->lm75[0]) {
- dev_err(&client->dev, "subclient %d registration "
- "at address 0x%x failed.\n", 1, sc_addr[0]);
+ dev_err(&client->dev,
+ "subclient %d registration at address 0x%x failed.\n",
+ 1, sc_addr[0]);
err = -ENOMEM;
goto ERROR_SC_2;
}
data->lm75[1] = i2c_new_dummy(adapter, sc_addr[1]);
if (!data->lm75[1]) {
- dev_err(&client->dev, "subclient %d registration "
- "at address 0x%x failed.\n", 2, sc_addr[1]);
+ dev_err(&client->dev,
+ "subclient %d registration at address 0x%x failed.\n",
+ 2, sc_addr[1]);
err = -ENOMEM;
goto ERROR_SC_3;
}
diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c
index da7f5b5d5db5..3ad9d849add2 100644
--- a/drivers/hwmon/asc7621.c
+++ b/drivers/hwmon/asc7621.c
@@ -159,12 +159,12 @@ static inline int write_byte(struct i2c_client *client, u8 reg, u8 data)
* and retrieval of like parameters.
*/
-#define SETUP_SHOW_data_param(d, a) \
+#define SETUP_SHOW_DATA_PARAM(d, a) \
struct sensor_device_attribute *sda = to_sensor_dev_attr(a); \
struct asc7621_data *data = asc7621_update_device(d); \
struct asc7621_param *param = to_asc7621_param(sda)
-#define SETUP_STORE_data_param(d, a) \
+#define SETUP_STORE_DATA_PARAM(d, a) \
struct sensor_device_attribute *sda = to_sensor_dev_attr(a); \
struct i2c_client *client = to_i2c_client(d); \
struct asc7621_data *data = i2c_get_clientdata(client); \
@@ -177,7 +177,7 @@ static inline int write_byte(struct i2c_client *client, u8 reg, u8 data)
static ssize_t show_u8(struct device *dev, struct device_attribute *attr,
char *buf)
{
- SETUP_SHOW_data_param(dev, attr);
+ SETUP_SHOW_DATA_PARAM(dev, attr);
return sprintf(buf, "%u\n", data->reg[param->msb[0]]);
}
@@ -185,7 +185,7 @@ static ssize_t show_u8(struct device *dev, struct device_attribute *attr,
static ssize_t store_u8(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- SETUP_STORE_data_param(dev, attr);
+ SETUP_STORE_DATA_PARAM(dev, attr);
long reqval;
if (kstrtol(buf, 10, &reqval))
@@ -206,7 +206,7 @@ static ssize_t store_u8(struct device *dev, struct device_attribute *attr,
static ssize_t show_bitmask(struct device *dev,
struct device_attribute *attr, char *buf)
{
- SETUP_SHOW_data_param(dev, attr);
+ SETUP_SHOW_DATA_PARAM(dev, attr);
return sprintf(buf, "%u\n",
(data->reg[param->msb[0]] >> param->
@@ -217,7 +217,7 @@ static ssize_t store_bitmask(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- SETUP_STORE_data_param(dev, attr);
+ SETUP_STORE_DATA_PARAM(dev, attr);
long reqval;
u8 currval;
@@ -246,7 +246,7 @@ static ssize_t store_bitmask(struct device *dev,
static ssize_t show_fan16(struct device *dev,
struct device_attribute *attr, char *buf)
{
- SETUP_SHOW_data_param(dev, attr);
+ SETUP_SHOW_DATA_PARAM(dev, attr);
u16 regval;
mutex_lock(&data->update_lock);
@@ -262,7 +262,7 @@ static ssize_t store_fan16(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
- SETUP_STORE_data_param(dev, attr);
+ SETUP_STORE_DATA_PARAM(dev, attr);
long reqval;
if (kstrtol(buf, 10, &reqval))
@@ -307,7 +307,7 @@ static int asc7621_in_scaling[] = {
static ssize_t show_in10(struct device *dev, struct device_attribute *attr,
char *buf)
{
- SETUP_SHOW_data_param(dev, attr);
+ SETUP_SHOW_DATA_PARAM(dev, attr);
u16 regval;
u8 nr = sda->index;
@@ -325,7 +325,7 @@ static ssize_t show_in10(struct device *dev, struct device_attribute *attr,
static ssize_t show_in8(struct device *dev, struct device_attribute *attr,
char *buf)
{
- SETUP_SHOW_data_param(dev, attr);
+ SETUP_SHOW_DATA_PARAM(dev, attr);
u8 nr = sda->index;
return sprintf(buf, "%u\n",
@@ -336,7 +336,7 @@ static ssize_t show_in8(struct device *dev, struct device_attribute *attr,
static ssize_t store_in8(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- SETUP_STORE_data_param(dev, attr);
+ SETUP_STORE_DATA_PARAM(dev, attr);
long reqval;
u8 nr = sda->index;
@@ -360,7 +360,7 @@ static ssize_t store_in8(struct device *dev, struct device_attribute *attr,
static ssize_t show_temp8(struct device *dev,
struct device_attribute *attr, char *buf)
{
- SETUP_SHOW_data_param(dev, attr);
+ SETUP_SHOW_DATA_PARAM(dev, attr);
return sprintf(buf, "%d\n", ((s8) data->reg[param->msb[0]]) * 1000);
}
@@ -369,7 +369,7 @@ static ssize_t store_temp8(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
- SETUP_STORE_data_param(dev, attr);
+ SETUP_STORE_DATA_PARAM(dev, attr);
long reqval;
s8 temp;
@@ -397,7 +397,7 @@ static ssize_t store_temp8(struct device *dev,
static ssize_t show_temp10(struct device *dev,
struct device_attribute *attr, char *buf)
{
- SETUP_SHOW_data_param(dev, attr);
+ SETUP_SHOW_DATA_PARAM(dev, attr);
u8 msb, lsb;
int temp;
@@ -414,7 +414,7 @@ static ssize_t show_temp10(struct device *dev,
static ssize_t show_temp62(struct device *dev,
struct device_attribute *attr, char *buf)
{
- SETUP_SHOW_data_param(dev, attr);
+ SETUP_SHOW_DATA_PARAM(dev, attr);
u8 regval = data->reg[param->msb[0]];
int temp = ((s8) (regval & 0xfc) * 1000) + ((regval & 0x03) * 250);
@@ -425,7 +425,7 @@ static ssize_t store_temp62(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
- SETUP_STORE_data_param(dev, attr);
+ SETUP_STORE_DATA_PARAM(dev, attr);
long reqval, i, f;
s8 temp;
@@ -459,7 +459,7 @@ static u32 asc7621_range_map[] = {
static ssize_t show_ap2_temp(struct device *dev,
struct device_attribute *attr, char *buf)
{
- SETUP_SHOW_data_param(dev, attr);
+ SETUP_SHOW_DATA_PARAM(dev, attr);
long auto_point1;
u8 regval;
int temp;
@@ -479,7 +479,7 @@ static ssize_t store_ap2_temp(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- SETUP_STORE_data_param(dev, attr);
+ SETUP_STORE_DATA_PARAM(dev, attr);
long reqval, auto_point1;
int i;
u8 currval, newval = 0;
@@ -510,7 +510,7 @@ static ssize_t store_ap2_temp(struct device *dev,
static ssize_t show_pwm_ac(struct device *dev,
struct device_attribute *attr, char *buf)
{
- SETUP_SHOW_data_param(dev, attr);
+ SETUP_SHOW_DATA_PARAM(dev, attr);
u8 config, altbit, regval;
u8 map[] = {
0x01, 0x02, 0x04, 0x1f, 0x00, 0x06, 0x07, 0x10,
@@ -530,7 +530,7 @@ static ssize_t store_pwm_ac(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- SETUP_STORE_data_param(dev, attr);
+ SETUP_STORE_DATA_PARAM(dev, attr);
unsigned long reqval;
u8 currval, config, altbit, newval;
u16 map[] = {
@@ -569,7 +569,7 @@ static ssize_t store_pwm_ac(struct device *dev,
static ssize_t show_pwm_enable(struct device *dev,
struct device_attribute *attr, char *buf)
{
- SETUP_SHOW_data_param(dev, attr);
+ SETUP_SHOW_DATA_PARAM(dev, attr);
u8 config, altbit, minoff, val, newval;
mutex_lock(&data->update_lock);
@@ -599,7 +599,7 @@ static ssize_t store_pwm_enable(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- SETUP_STORE_data_param(dev, attr);
+ SETUP_STORE_DATA_PARAM(dev, attr);
long reqval;
u8 currval, config, altbit, newval, minoff = 255;
@@ -659,7 +659,7 @@ static u32 asc7621_pwm_freq_map[] = {
static ssize_t show_pwm_freq(struct device *dev,
struct device_attribute *attr, char *buf)
{
- SETUP_SHOW_data_param(dev, attr);
+ SETUP_SHOW_DATA_PARAM(dev, attr);
u8 regval =
(data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
@@ -672,7 +672,7 @@ static ssize_t store_pwm_freq(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- SETUP_STORE_data_param(dev, attr);
+ SETUP_STORE_DATA_PARAM(dev, attr);
unsigned long reqval;
u8 currval, newval = 255;
int i;
@@ -707,7 +707,7 @@ static u32 asc7621_pwm_auto_spinup_map[] = {
static ssize_t show_pwm_ast(struct device *dev,
struct device_attribute *attr, char *buf)
{
- SETUP_SHOW_data_param(dev, attr);
+ SETUP_SHOW_DATA_PARAM(dev, attr);
u8 regval =
(data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
@@ -721,7 +721,7 @@ static ssize_t store_pwm_ast(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- SETUP_STORE_data_param(dev, attr);
+ SETUP_STORE_DATA_PARAM(dev, attr);
long reqval;
u8 currval, newval = 255;
u32 i;
@@ -756,7 +756,7 @@ static u32 asc7621_temp_smoothing_time_map[] = {
static ssize_t show_temp_st(struct device *dev,
struct device_attribute *attr, char *buf)
{
- SETUP_SHOW_data_param(dev, attr);
+ SETUP_SHOW_DATA_PARAM(dev, attr);
u8 regval =
(data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
regval = clamp_val(regval, 0, 7);
@@ -768,7 +768,7 @@ static ssize_t store_temp_st(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- SETUP_STORE_data_param(dev, attr);
+ SETUP_STORE_DATA_PARAM(dev, attr);
long reqval;
u8 currval, newval = 255;
u32 i;
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index 3f1e297663ad..658ce3a8717f 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -411,8 +411,7 @@ static int __cpuinit chk_ucode_version(unsigned int cpu)
* fixed for stepping D0 (6EC).
*/
if (c->x86_model == 0xe && c->x86_mask < 0xc && c->microcode < 0x39) {
- pr_err("Errata AE18 not fixed, update BIOS or "
- "microcode of the CPU!\n");
+ pr_err("Errata AE18 not fixed, update BIOS or microcode of the CPU!\n");
return -ENODEV;
}
return 0;
diff --git a/drivers/hwmon/da9052-hwmon.c b/drivers/hwmon/da9052-hwmon.c
index ab4452c5a98c..960fac3fb166 100644
--- a/drivers/hwmon/da9052-hwmon.c
+++ b/drivers/hwmon/da9052-hwmon.c
@@ -43,19 +43,19 @@ static const char * const input_names[] = {
};
/* Conversion function for VDDOUT and VBAT */
-static inline int volt_reg_to_mV(int value)
+static inline int volt_reg_to_mv(int value)
{
return DIV_ROUND_CLOSEST(value * 1000, 512) + 2500;
}
/* Conversion function for ADC channels 4, 5 and 6 */
-static inline int input_reg_to_mV(int value)
+static inline int input_reg_to_mv(int value)
{
return DIV_ROUND_CLOSEST(value * 2500, 1023);
}
/* Conversion function for VBBAT */
-static inline int vbbat_reg_to_mV(int value)
+static inline int vbbat_reg_to_mv(int value)
{
return DIV_ROUND_CLOSEST(value * 2500, 512);
}
@@ -96,7 +96,7 @@ static ssize_t da9052_read_vddout(struct device *dev,
goto hwmon_err;
mutex_unlock(&hwmon->hwmon_lock);
- return sprintf(buf, "%d\n", volt_reg_to_mV(vdd));
+ return sprintf(buf, "%d\n", volt_reg_to_mv(vdd));
hwmon_err_release:
da9052_disable_vddout_channel(hwmon->da9052);
@@ -137,7 +137,7 @@ static ssize_t da9052_read_vbat(struct device *dev,
if (ret < 0)
return ret;
- return sprintf(buf, "%d\n", volt_reg_to_mV(ret));
+ return sprintf(buf, "%d\n", volt_reg_to_mv(ret));
}
static ssize_t da9052_read_misc_channel(struct device *dev,
@@ -152,7 +152,7 @@ static ssize_t da9052_read_misc_channel(struct device *dev,
if (ret < 0)
return ret;
- return sprintf(buf, "%d\n", input_reg_to_mV(ret));
+ return sprintf(buf, "%d\n", input_reg_to_mv(ret));
}
static ssize_t da9052_read_tjunc(struct device *dev,
@@ -187,7 +187,7 @@ static ssize_t da9052_read_vbbat(struct device *dev,
if (ret < 0)
return ret;
- return sprintf(buf, "%d\n", vbbat_reg_to_mV(ret));
+ return sprintf(buf, "%d\n", vbbat_reg_to_mv(ret));
}
static ssize_t da9052_hwmon_show_name(struct device *dev,
diff --git a/drivers/hwmon/da9055-hwmon.c b/drivers/hwmon/da9055-hwmon.c
index 9465c050c326..029ecabc4380 100644
--- a/drivers/hwmon/da9055-hwmon.c
+++ b/drivers/hwmon/da9055-hwmon.c
@@ -119,7 +119,7 @@ static irqreturn_t da9055_auxadc_irq(int irq, void *irq_data)
}
/* Conversion function for VSYS and ADCINx */
-static inline int volt_reg_to_mV(int value, int channel)
+static inline int volt_reg_to_mv(int value, int channel)
{
if (channel == DA9055_ADC_VSYS)
return DIV_ROUND_CLOSEST(value * 1000, DA9055_VSYS_DIV) + 2500;
@@ -168,7 +168,7 @@ static ssize_t da9055_read_auto_ch(struct device *dev,
mutex_unlock(&hwmon->hwmon_lock);
- return sprintf(buf, "%d\n", volt_reg_to_mV(adc, channel));
+ return sprintf(buf, "%d\n", volt_reg_to_mv(adc, channel));
hwmon_err_release:
da9055_disable_auto_mode(hwmon->da9055, channel);
diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c
index c347c94f2f73..4ae3fff13f44 100644
--- a/drivers/hwmon/dme1737.c
+++ b/drivers/hwmon/dme1737.c
@@ -55,14 +55,16 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID");
static bool probe_all_addr;
module_param(probe_all_addr, bool, 0);
-MODULE_PARM_DESC(probe_all_addr, "Include probing of non-standard LPC "
- "addresses");
+MODULE_PARM_DESC(probe_all_addr,
+ "Include probing of non-standard LPC addresses");
/* Addresses to scan */
static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END};
enum chips { dme1737, sch5027, sch311x, sch5127 };
+#define DO_REPORT "Please report to the driver maintainer."
+
/* ---------------------------------------------------------------------
* Registers
*
@@ -566,9 +568,9 @@ static u8 dme1737_read(const struct dme1737_data *data, u8 reg)
val = i2c_smbus_read_byte_data(client, reg);
if (val < 0) {
- dev_warn(&client->dev, "Read from register "
- "0x%02x failed! Please report to the driver "
- "maintainer.\n", reg);
+ dev_warn(&client->dev,
+ "Read from register 0x%02x failed! %s\n",
+ reg, DO_REPORT);
}
} else { /* ISA device */
outb(reg, data->addr);
@@ -587,9 +589,9 @@ static s32 dme1737_write(const struct dme1737_data *data, u8 reg, u8 val)
res = i2c_smbus_write_byte_data(client, reg, val);
if (res < 0) {
- dev_warn(&client->dev, "Write to register "
- "0x%02x failed! Please report to the driver "
- "maintainer.\n", reg);
+ dev_warn(&client->dev,
+ "Write to register 0x%02x failed! %s\n",
+ reg, DO_REPORT);
}
} else { /* ISA device */
outb(reg, data->addr);
@@ -1167,8 +1169,8 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
/* Only valid for fan[1-4] */
if (!(val == 1 || val == 2 || val == 4)) {
count = -EINVAL;
- dev_warn(dev, "Fan type value %ld not "
- "supported. Choose one of 1, 2, or 4.\n",
+ dev_warn(dev,
+ "Fan type value %ld not supported. Choose one of 1, 2, or 4.\n",
val);
goto exit;
}
@@ -1294,8 +1296,8 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
/* Only valid for pwm[1-3] */
if (val < 0 || val > 2) {
count = -EINVAL;
- dev_warn(dev, "PWM enable %ld not "
- "supported. Choose one of 0, 1, or 2.\n",
+ dev_warn(dev,
+ "PWM enable %ld not supported. Choose one of 0, 1, or 2.\n",
val);
goto exit;
}
@@ -1399,8 +1401,8 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
if (!(val == 1 || val == 2 || val == 4 ||
val == 6 || val == 7)) {
count = -EINVAL;
- dev_warn(dev, "PWM auto channels zone %ld "
- "not supported. Choose one of 1, 2, 4, 6, "
+ dev_warn(dev,
+ "PWM auto channels zone %ld not supported. Choose one of 1, 2, 4, 6, "
"or 7.\n", val);
goto exit;
}
@@ -2178,8 +2180,8 @@ static int dme1737_create_files(struct device *dev)
* selected attributes from read-only to read-writeable.
*/
if (data->config & 0x02) {
- dev_info(dev, "Device is locked. Some attributes "
- "will be read-only.\n");
+ dev_info(dev,
+ "Device is locked. Some attributes will be read-only.\n");
} else {
/* Change permissions of zone sysfs attributes */
dme1737_chmod_group(dev, &dme1737_zone_chmod_group,
@@ -2247,9 +2249,8 @@ static int dme1737_init_device(struct device *dev)
/* Inform if part is not monitoring/started */
if (!(data->config & 0x01)) {
if (!force_start) {
- dev_err(dev, "Device is not monitoring. "
- "Use the force_start load parameter to "
- "override.\n");
+ dev_err(dev,
+ "Device is not monitoring. Use the force_start load parameter to override.\n");
return -EFAULT;
}
@@ -2289,8 +2290,8 @@ static int dme1737_init_device(struct device *dev)
*/
if (dme1737_i2c_get_features(0x2e, data) &&
dme1737_i2c_get_features(0x4e, data)) {
- dev_warn(dev, "Failed to query Super-IO for optional "
- "features.\n");
+ dev_warn(dev,
+ "Failed to query Super-IO for optional features.\n");
}
}
@@ -2317,8 +2318,8 @@ static int dme1737_init_device(struct device *dev)
break;
}
- dev_info(dev, "Optional features: pwm3=%s, pwm5=%s, pwm6=%s, "
- "fan3=%s, fan4=%s, fan5=%s, fan6=%s.\n",
+ dev_info(dev,
+ "Optional features: pwm3=%s, pwm5=%s, pwm6=%s, fan3=%s, fan4=%s, fan5=%s, fan6=%s.\n",
(data->has_features & HAS_PWM(2)) ? "yes" : "no",
(data->has_features & HAS_PWM(4)) ? "yes" : "no",
(data->has_features & HAS_PWM(5)) ? "yes" : "no",
@@ -2330,18 +2331,16 @@ static int dme1737_init_device(struct device *dev)
reg = dme1737_read(data, DME1737_REG_TACH_PWM);
/* Inform if fan-to-pwm mapping differs from the default */
if (client && reg != 0xa4) { /* I2C chip */
- dev_warn(dev, "Non-standard fan to pwm mapping: "
- "fan1->pwm%d, fan2->pwm%d, fan3->pwm%d, "
- "fan4->pwm%d. Please report to the driver "
- "maintainer.\n",
+ dev_warn(dev,
+ "Non-standard fan to pwm mapping: fan1->pwm%d, fan2->pwm%d, fan3->pwm%d, fan4->pwm%d. %s\n",
(reg & 0x03) + 1, ((reg >> 2) & 0x03) + 1,
- ((reg >> 4) & 0x03) + 1, ((reg >> 6) & 0x03) + 1);
+ ((reg >> 4) & 0x03) + 1, ((reg >> 6) & 0x03) + 1,
+ DO_REPORT);
} else if (!client && reg != 0x24) { /* ISA chip */
- dev_warn(dev, "Non-standard fan to pwm mapping: "
- "fan1->pwm%d, fan2->pwm%d, fan3->pwm%d. "
- "Please report to the driver maintainer.\n",
+ dev_warn(dev,
+ "Non-standard fan to pwm mapping: fan1->pwm%d, fan2->pwm%d, fan3->pwm%d. %s\n",
(reg & 0x03) + 1, ((reg >> 2) & 0x03) + 1,
- ((reg >> 4) & 0x03) + 1);
+ ((reg >> 4) & 0x03) + 1, DO_REPORT);
}
/*
@@ -2355,8 +2354,9 @@ static int dme1737_init_device(struct device *dev)
DME1737_REG_PWM_CONFIG(ix));
if ((data->has_features & HAS_PWM(ix)) &&
(PWM_EN_FROM_REG(data->pwm_config[ix]) == -1)) {
- dev_info(dev, "Switching pwm%d to "
- "manual mode.\n", ix + 1);
+ dev_info(dev,
+ "Switching pwm%d to manual mode.\n",
+ ix + 1);
data->pwm_config[ix] = PWM_EN_TO_REG(1,
data->pwm_config[ix]);
dme1737_write(data, DME1737_REG_PWM(ix), 0);
diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c
index a9816979c5de..0c9f3da242bf 100644
--- a/drivers/hwmon/f71805f.c
+++ b/drivers/hwmon/f71805f.c
@@ -1350,8 +1350,7 @@ static void f71805f_init_device(struct f71805f_data *data)
reg = f71805f_read8(data, F71805F_REG_START);
if ((reg & 0x41) != 0x01) {
- printk(KERN_DEBUG DRVNAME ": Starting monitoring "
- "operations\n");
+ pr_debug("Starting monitoring operations\n");
f71805f_write8(data, F71805F_REG_START, (reg | 0x01) & ~0x40);
}
diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
index b757088aeddb..dff841085baf 100644
--- a/drivers/hwmon/fam15h_power.c
+++ b/drivers/hwmon/fam15h_power.c
@@ -189,8 +189,8 @@ static void fam15h_power_init_data(struct pci_dev *f4,
/* result not allowed to be >= 256W */
if ((tmp >> 16) >= 256)
- dev_warn(&f4->dev, "Bogus value for ProcessorPwrWatts "
- "(processor_pwr_watts>=%u)\n",
+ dev_warn(&f4->dev,
+ "Bogus value for ProcessorPwrWatts (processor_pwr_watts>=%u)\n",
(unsigned int) (tmp >> 16));
/* convert to microWatt */
diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c
index 8af2755cdb87..d58abdc5a4cf 100644
--- a/drivers/hwmon/fschmd.c
+++ b/drivers/hwmon/fschmd.c
@@ -463,8 +463,9 @@ static ssize_t store_fan_div(struct device *dev, struct device_attribute
v = 3;
break;
default:
- dev_err(dev, "fan_div value %lu not supported. "
- "Choose one of 2, 4 or 8!\n", v);
+ dev_err(dev,
+ "fan_div value %lu not supported. Choose one of 2, 4 or 8!\n",
+ v);
return -EINVAL;
}
@@ -1249,8 +1250,8 @@ static int fschmd_probe(struct i2c_client *client,
}
if (i == ARRAY_SIZE(watchdog_minors)) {
data->watchdog_miscdev.minor = 0;
- dev_warn(&client->dev, "Couldn't register watchdog chardev "
- "(due to no free minor)\n");
+ dev_warn(&client->dev,
+ "Couldn't register watchdog chardev (due to no free minor)\n");
}
mutex_unlock(&watchdog_data_mutex);
diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c
index e2e5909a34df..95257a5621d8 100644
--- a/drivers/hwmon/gl518sm.c
+++ b/drivers/hwmon/gl518sm.c
@@ -344,8 +344,9 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
val = 3;
break;
default:
- dev_err(dev, "Invalid fan clock divider %lu, choose one "
- "of 1, 2, 4 or 8\n", val);
+ dev_err(dev,
+ "Invalid fan clock divider %lu, choose one of 1, 2, 4 or 8\n",
+ val);
return -EINVAL;
}
diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c
index 39781945a5d2..3104149795c5 100644
--- a/drivers/hwmon/gpio-fan.c
+++ b/drivers/hwmon/gpio-fan.c
@@ -105,10 +105,6 @@ static int fan_alarm_init(struct gpio_fan_data *fan_data,
if (err)
return err;
- err = device_create_file(&pdev->dev, &dev_attr_fan1_alarm);
- if (err)
- return err;
-
/*
* If the alarm GPIO don't support interrupts, just leave
* without initializing the fail notification support.
@@ -121,23 +117,9 @@ static int fan_alarm_init(struct gpio_fan_data *fan_data,
irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH);
err = devm_request_irq(&pdev->dev, alarm_irq, fan_alarm_irq_handler,
IRQF_SHARED, "GPIO fan alarm", fan_data);
- if (err)
- goto err_free_sysfs;
-
- return 0;
-
-err_free_sysfs:
- device_remove_file(&pdev->dev, &dev_attr_fan1_alarm);
return err;
}
-static void fan_alarm_free(struct gpio_fan_data *fan_data)
-{
- struct platform_device *pdev = fan_data->pdev;
-
- device_remove_file(&pdev->dev, &dev_attr_fan1_alarm);
-}
-
/*
* Control GPIOs.
*/
@@ -327,6 +309,12 @@ exit_unlock:
return ret;
}
+static ssize_t show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "gpio-fan\n");
+}
+
static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm);
static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
show_pwm_enable, set_pwm_enable);
@@ -336,8 +324,26 @@ static DEVICE_ATTR(fan1_max, S_IRUGO, show_rpm_max, NULL);
static DEVICE_ATTR(fan1_input, S_IRUGO, show_rpm, NULL);
static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, show_rpm, set_rpm);
-static struct attribute *gpio_fan_ctrl_attributes[] = {
- &dev_attr_pwm1.attr,
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static umode_t gpio_fan_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct gpio_fan_data *data = dev_get_drvdata(dev);
+
+ if (index == 1 && !data->alarm)
+ return 0;
+ if (index > 1 && !data->ctrl)
+ return 0;
+
+ return attr->mode;
+}
+
+static struct attribute *gpio_fan_attributes[] = {
+ &dev_attr_name.attr,
+ &dev_attr_fan1_alarm.attr, /* 1 */
+ &dev_attr_pwm1.attr, /* 2 */
&dev_attr_pwm1_enable.attr,
&dev_attr_pwm1_mode.attr,
&dev_attr_fan1_input.attr,
@@ -347,8 +353,9 @@ static struct attribute *gpio_fan_ctrl_attributes[] = {
NULL
};
-static const struct attribute_group gpio_fan_ctrl_group = {
- .attrs = gpio_fan_ctrl_attributes,
+static const struct attribute_group gpio_fan_group = {
+ .attrs = gpio_fan_attributes,
+ .is_visible = gpio_fan_is_visible,
};
static int fan_ctrl_init(struct gpio_fan_data *fan_data,
@@ -379,30 +386,9 @@ static int fan_ctrl_init(struct gpio_fan_data *fan_data,
if (fan_data->speed_index < 0)
return -ENODEV;
- err = sysfs_create_group(&pdev->dev.kobj, &gpio_fan_ctrl_group);
- return err;
-}
-
-static void fan_ctrl_free(struct gpio_fan_data *fan_data)
-{
- struct platform_device *pdev = fan_data->pdev;
-
- sysfs_remove_group(&pdev->dev.kobj, &gpio_fan_ctrl_group);
-}
-
-/*
- * Platform driver.
- */
-
-static ssize_t show_name(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return sprintf(buf, "gpio-fan\n");
+ return 0;
}
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
-
-
#ifdef CONFIG_OF_GPIO
/*
* Translate OpenFirmware node properties into platform_data
@@ -546,38 +532,30 @@ static int gpio_fan_probe(struct platform_device *pdev)
/* Configure control GPIOs if available. */
if (pdata->ctrl && pdata->num_ctrl > 0) {
- if (!pdata->speed || pdata->num_speed <= 1) {
- err = -EINVAL;
- goto err_free_alarm;
- }
+ if (!pdata->speed || pdata->num_speed <= 1)
+ return -EINVAL;
err = fan_ctrl_init(fan_data, pdata);
if (err)
- goto err_free_alarm;
+ return err;
}
- err = device_create_file(&pdev->dev, &dev_attr_name);
+ err = sysfs_create_group(&pdev->dev.kobj, &gpio_fan_group);
if (err)
- goto err_free_ctrl;
+ return err;
/* Make this driver part of hwmon class. */
fan_data->hwmon_dev = hwmon_device_register(&pdev->dev);
if (IS_ERR(fan_data->hwmon_dev)) {
err = PTR_ERR(fan_data->hwmon_dev);
- goto err_remove_name;
+ goto err_remove;
}
dev_info(&pdev->dev, "GPIO fan initialized\n");
return 0;
-err_remove_name:
- device_remove_file(&pdev->dev, &dev_attr_name);
-err_free_ctrl:
- if (fan_data->ctrl)
- fan_ctrl_free(fan_data);
-err_free_alarm:
- if (fan_data->alarm)
- fan_alarm_free(fan_data);
+err_remove:
+ sysfs_remove_group(&pdev->dev.kobj, &gpio_fan_group);
return err;
}
@@ -586,11 +564,7 @@ static int gpio_fan_remove(struct platform_device *pdev)
struct gpio_fan_data *fan_data = platform_get_drvdata(pdev);
hwmon_device_unregister(fan_data->hwmon_dev);
- device_remove_file(&pdev->dev, &dev_attr_name);
- if (fan_data->alarm)
- fan_alarm_free(fan_data);
- if (fan_data->ctrl)
- fan_ctrl_free(fan_data);
+ sysfs_remove_group(&pdev->dev.kobj, &gpio_fan_group);
return 0;
}
@@ -619,7 +593,7 @@ static int gpio_fan_resume(struct device *dev)
}
static SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume);
-#define GPIO_FAN_PM &gpio_fan_pm
+#define GPIO_FAN_PM (&gpio_fan_pm)
#else
#define GPIO_FAN_PM NULL
#endif
diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c
index a14f634248e7..1429f6e177f4 100644
--- a/drivers/hwmon/ibmaem.c
+++ b/drivers/hwmon/ibmaem.c
@@ -289,8 +289,9 @@ static int aem_init_ipmi_data(struct aem_ipmi_data *data, int iface,
err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
data, &data->user);
if (err < 0) {
- dev_err(bmc, "Unable to register user with IPMI "
- "interface %d\n", data->interface);
+ dev_err(bmc,
+ "Unable to register user with IPMI interface %d\n",
+ data->interface);
return -EACCES;
}
@@ -328,8 +329,8 @@ static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
struct aem_ipmi_data *data = user_msg_data;
if (msg->msgid != data->tx_msgid) {
- dev_err(data->bmc_device, "Mismatch between received msgid "
- "(%02x) and transmitted msgid (%02x)!\n",
+ dev_err(data->bmc_device,
+ "Mismatch between received msgid (%02x) and transmitted msgid (%02x)!\n",
(int)msg->msgid,
(int)data->tx_msgid);
ipmi_free_recv_msg(msg);
@@ -575,8 +576,8 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle)
/* Register with hwmon */
data->hwmon_dev = hwmon_device_register(&data->pdev->dev);
if (IS_ERR(data->hwmon_dev)) {
- dev_err(&data->pdev->dev, "Unable to register hwmon "
- "device for IPMI interface %d\n",
+ dev_err(&data->pdev->dev,
+ "Unable to register hwmon device for IPMI interface %d\n",
probe->interface);
res = PTR_ERR(data->hwmon_dev);
goto hwmon_reg_err;
@@ -715,8 +716,8 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe,
/* Register with hwmon */
data->hwmon_dev = hwmon_device_register(&data->pdev->dev);
if (IS_ERR(data->hwmon_dev)) {
- dev_err(&data->pdev->dev, "Unable to register hwmon "
- "device for IPMI interface %d\n",
+ dev_err(&data->pdev->dev,
+ "Unable to register hwmon device for IPMI interface %d\n",
probe->interface);
res = PTR_ERR(data->hwmon_dev);
goto hwmon_reg_err;
@@ -768,8 +769,8 @@ static void aem_init_aem2(struct aem_ipmi_data *probe)
while (!aem_find_aem2(probe, &fi_resp, i)) {
if (fi_resp.major != 2) {
- dev_err(probe->bmc_device, "Unknown AEM v%d; please "
- "report this to the maintainer.\n",
+ dev_err(probe->bmc_device,
+ "Unknown AEM v%d; please report this to the maintainer.\n",
fi_resp.major);
i++;
continue;
diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
index b622a93ec32c..74b365ea01c7 100644
--- a/drivers/hwmon/ibmpex.c
+++ b/drivers/hwmon/ibmpex.c
@@ -163,8 +163,8 @@ static int ibmpex_ver_check(struct ibmpex_bmc_data *data)
data->sensor_major = data->rx_msg_data[0];
data->sensor_minor = data->rx_msg_data[1];
- dev_info(data->bmc_device, "Found BMC with sensor interface "
- "v%d.%d %d-%02d-%02d on interface %d\n",
+ dev_info(data->bmc_device,
+ "Found BMC with sensor interface v%d.%d %d-%02d-%02d on interface %d\n",
data->sensor_major,
data->sensor_minor,
extract_value(data->rx_msg_data, 2),
@@ -478,8 +478,9 @@ static void ibmpex_register_bmc(int iface, struct device *dev)
err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
data, &data->user);
if (err < 0) {
- dev_err(dev, "Unable to register user with IPMI "
- "interface %d\n", data->interface);
+ dev_err(dev,
+ "Unable to register user with IPMI interface %d\n",
+ data->interface);
goto out;
}
@@ -501,8 +502,8 @@ static void ibmpex_register_bmc(int iface, struct device *dev)
data->hwmon_dev = hwmon_device_register(data->bmc_device);
if (IS_ERR(data->hwmon_dev)) {
- dev_err(data->bmc_device, "Unable to register hwmon "
- "device for IPMI interface %d\n",
+ dev_err(data->bmc_device,
+ "Unable to register hwmon device for IPMI interface %d\n",
data->interface);
goto out_user;
}
@@ -567,8 +568,8 @@ static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data;
if (msg->msgid != data->tx_msgid) {
- dev_err(data->bmc_device, "Mismatch between received msgid "
- "(%02x) and transmitted msgid (%02x)!\n",
+ dev_err(data->bmc_device,
+ "Mismatch between received msgid (%02x) and transmitted msgid (%02x)!\n",
(int)msg->msgid,
(int)data->tx_msgid);
ipmi_free_recv_msg(msg);
diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c
new file mode 100644
index 000000000000..aafa4531b961
--- /dev/null
+++ b/drivers/hwmon/iio_hwmon.c
@@ -0,0 +1,196 @@
+/* Hwmon client for industrial I/O devices
+ *
+ * Copyright (c) 2011 Jonathan Cameron
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/of.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/types.h>
+
+/**
+ * struct iio_hwmon_state - device instance state
+ * @channels: filled with array of channels from iio
+ * @num_channels: number of channels in channels (saves counting twice)
+ * @hwmon_dev: associated hwmon device
+ * @attr_group: the group of attributes
+ * @attrs: null terminated array of attribute pointers.
+ */
+struct iio_hwmon_state {
+ struct iio_channel *channels;
+ int num_channels;
+ struct device *hwmon_dev;
+ struct attribute_group attr_group;
+ struct attribute **attrs;
+};
+
+/*
+ * Assumes that IIO and hwmon operate in the same base units.
+ * This is supposed to be true, but needs verification for
+ * new channel types.
+ */
+static ssize_t iio_hwmon_read_val(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int result;
+ int ret;
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ struct iio_hwmon_state *state = dev_get_drvdata(dev);
+
+ ret = iio_read_channel_processed(&state->channels[sattr->index],
+ &result);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", result);
+}
+
+static ssize_t show_name(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ const char *name = "iio_hwmon";
+
+ if (dev->of_node && dev->of_node->name)
+ name = dev->of_node->name;
+
+ return sprintf(buf, "%s\n", name);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static int iio_hwmon_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iio_hwmon_state *st;
+ struct sensor_device_attribute *a;
+ int ret, i;
+ int in_i = 1, temp_i = 1, curr_i = 1;
+ enum iio_chan_type type;
+ struct iio_channel *channels;
+
+ channels = iio_channel_get_all(dev);
+ if (IS_ERR(channels))
+ return PTR_ERR(channels);
+
+ st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
+ if (st == NULL)
+ return -ENOMEM;
+
+ st->channels = channels;
+
+ /* count how many attributes we have */
+ while (st->channels[st->num_channels].indio_dev)
+ st->num_channels++;
+
+ st->attrs = devm_kzalloc(dev,
+ sizeof(*st->attrs) * (st->num_channels + 2),
+ GFP_KERNEL);
+ if (st->attrs == NULL) {
+ ret = -ENOMEM;
+ goto error_release_channels;
+ }
+
+ for (i = 0; i < st->num_channels; i++) {
+ a = devm_kzalloc(dev, sizeof(*a), GFP_KERNEL);
+ if (a == NULL) {
+ ret = -ENOMEM;
+ goto error_release_channels;
+ }
+
+ sysfs_attr_init(&a->dev_attr.attr);
+ ret = iio_get_channel_type(&st->channels[i], &type);
+ if (ret < 0)
+ goto error_release_channels;
+
+ switch (type) {
+ case IIO_VOLTAGE:
+ a->dev_attr.attr.name = kasprintf(GFP_KERNEL,
+ "in%d_input",
+ in_i++);
+ break;
+ case IIO_TEMP:
+ a->dev_attr.attr.name = kasprintf(GFP_KERNEL,
+ "temp%d_input",
+ temp_i++);
+ break;
+ case IIO_CURRENT:
+ a->dev_attr.attr.name = kasprintf(GFP_KERNEL,
+ "curr%d_input",
+ curr_i++);
+ break;
+ default:
+ ret = -EINVAL;
+ goto error_release_channels;
+ }
+ if (a->dev_attr.attr.name == NULL) {
+ ret = -ENOMEM;
+ goto error_release_channels;
+ }
+ a->dev_attr.show = iio_hwmon_read_val;
+ a->dev_attr.attr.mode = S_IRUGO;
+ a->index = i;
+ st->attrs[i] = &a->dev_attr.attr;
+ }
+ st->attrs[st->num_channels] = &dev_attr_name.attr;
+ st->attr_group.attrs = st->attrs;
+ platform_set_drvdata(pdev, st);
+ ret = sysfs_create_group(&dev->kobj, &st->attr_group);
+ if (ret < 0)
+ goto error_release_channels;
+
+ st->hwmon_dev = hwmon_device_register(dev);
+ if (IS_ERR(st->hwmon_dev)) {
+ ret = PTR_ERR(st->hwmon_dev);
+ goto error_remove_group;
+ }
+ return 0;
+
+error_remove_group:
+ sysfs_remove_group(&dev->kobj, &st->attr_group);
+error_release_channels:
+ iio_channel_release_all(st->channels);
+ return ret;
+}
+
+static int iio_hwmon_remove(struct platform_device *pdev)
+{
+ struct iio_hwmon_state *st = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(st->hwmon_dev);
+ sysfs_remove_group(&pdev->dev.kobj, &st->attr_group);
+ iio_channel_release_all(st->channels);
+
+ return 0;
+}
+
+static struct of_device_id iio_hwmon_of_match[] = {
+ { .compatible = "iio-hwmon", },
+ { }
+};
+
+static struct platform_driver __refdata iio_hwmon_driver = {
+ .driver = {
+ .name = "iio_hwmon",
+ .owner = THIS_MODULE,
+ .of_match_table = iio_hwmon_of_match,
+ },
+ .probe = iio_hwmon_probe,
+ .remove = iio_hwmon_remove,
+};
+
+module_platform_driver(iio_hwmon_driver);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("IIO to hwmon driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
index 8e7158c3ad2f..4958b2f89dce 100644
--- a/drivers/hwmon/ina2xx.c
+++ b/drivers/hwmon/ina2xx.c
@@ -186,20 +186,20 @@ static ssize_t ina2xx_show_value(struct device *dev,
}
/* shunt voltage */
-static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, \
- ina2xx_show_value, NULL, INA2XX_SHUNT_VOLTAGE);
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ina2xx_show_value, NULL,
+ INA2XX_SHUNT_VOLTAGE);
/* bus voltage */
-static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, \
- ina2xx_show_value, NULL, INA2XX_BUS_VOLTAGE);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ina2xx_show_value, NULL,
+ INA2XX_BUS_VOLTAGE);
/* calculated current */
-static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, \
- ina2xx_show_value, NULL, INA2XX_CURRENT);
+static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ina2xx_show_value, NULL,
+ INA2XX_CURRENT);
/* calculated power */
-static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, \
- ina2xx_show_value, NULL, INA2XX_POWER);
+static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ina2xx_show_value, NULL,
+ INA2XX_POWER);
/* pointers to created device attributes */
static struct attribute *ina2xx_attributes[] = {
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index 37fc980fde24..72b21d5b1c62 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -1778,7 +1778,7 @@ static int __init it87_find(unsigned short *address,
superio_select(5);
sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
} else if (sio_data->type == it8783) {
- int reg25, reg27, reg2A, reg2C, regEF;
+ int reg25, reg27, reg2a, reg2c, regef;
sio_data->skip_vid = 1; /* No VID */
@@ -1786,15 +1786,15 @@ static int __init it87_find(unsigned short *address,
reg25 = superio_inb(IT87_SIO_GPIO1_REG);
reg27 = superio_inb(IT87_SIO_GPIO3_REG);
- reg2A = superio_inb(IT87_SIO_PINX1_REG);
- reg2C = superio_inb(IT87_SIO_PINX2_REG);
- regEF = superio_inb(IT87_SIO_SPI_REG);
+ reg2a = superio_inb(IT87_SIO_PINX1_REG);
+ reg2c = superio_inb(IT87_SIO_PINX2_REG);
+ regef = superio_inb(IT87_SIO_SPI_REG);
/* Check if fan3 is there or not */
- if ((reg27 & (1 << 0)) || !(reg2C & (1 << 2)))
+ if ((reg27 & (1 << 0)) || !(reg2c & (1 << 2)))
sio_data->skip_fan |= (1 << 2);
if ((reg25 & (1 << 4))
- || (!(reg2A & (1 << 1)) && (regEF & (1 << 0))))
+ || (!(reg2a & (1 << 1)) && (regef & (1 << 0))))
sio_data->skip_pwm |= (1 << 2);
/* Check if fan2 is there or not */
@@ -1804,7 +1804,7 @@ static int __init it87_find(unsigned short *address,
sio_data->skip_pwm |= (1 << 1);
/* VIN5 */
- if ((reg27 & (1 << 0)) || (reg2C & (1 << 2)))
+ if ((reg27 & (1 << 0)) || (reg2c & (1 << 2)))
sio_data->skip_in |= (1 << 5); /* No VIN5 */
/* VIN6 */
@@ -1829,18 +1829,18 @@ static int __init it87_find(unsigned short *address,
* not the case, and ask the user to report if the
* resulting voltage is sane.
*/
- if (!(reg2C & (1 << 1))) {
- reg2C |= (1 << 1);
- superio_outb(IT87_SIO_PINX2_REG, reg2C);
+ if (!(reg2c & (1 << 1))) {
+ reg2c |= (1 << 1);
+ superio_outb(IT87_SIO_PINX2_REG, reg2c);
pr_notice("Routing internal VCCH5V to in7.\n");
}
pr_notice("in7 routed to internal voltage divider, with external pin disabled.\n");
pr_notice("Please report if it displays a reasonable voltage.\n");
}
- if (reg2C & (1 << 0))
+ if (reg2c & (1 << 0))
sio_data->internal |= (1 << 0);
- if (reg2C & (1 << 1))
+ if (reg2c & (1 << 1))
sio_data->internal |= (1 << 1);
sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c
index 9f3c0aeacdb9..5b50e9e4f96b 100644
--- a/drivers/hwmon/k8temp.c
+++ b/drivers/hwmon/k8temp.c
@@ -200,8 +200,8 @@ static int k8temp_probe(struct pci_dev *pdev,
*/
if (model >= 0x40) {
data->swap_core_select = 1;
- dev_warn(&pdev->dev, "Temperature readouts might be wrong - "
- "check erratum #141\n");
+ dev_warn(&pdev->dev,
+ "Temperature readouts might be wrong - check erratum #141\n");
}
/*
diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c
index 483538fa1bd5..6cf6bff79003 100644
--- a/drivers/hwmon/lm78.c
+++ b/drivers/hwmon/lm78.c
@@ -386,8 +386,9 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *da,
data->fan_div[nr] = 3;
break;
default:
- dev_err(dev, "fan_div value %ld not "
- "supported. Choose one of 1, 2, 4 or 8!\n", val);
+ dev_err(dev,
+ "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n",
+ val);
mutex_unlock(&data->update_lock);
return -EINVAL;
}
@@ -636,8 +637,9 @@ static int lm78_i2c_detect(struct i2c_client *client,
goto err_nodev;
if (lm78_alias_detect(client, i)) {
- dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
- "be the same as ISA device\n", address);
+ dev_dbg(&adapter->dev,
+ "Device at 0x%02x appears to be the same as ISA device\n",
+ address);
goto err_nodev;
}
diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c
index 357fbb998728..eba89aac3ece 100644
--- a/drivers/hwmon/lm80.c
+++ b/drivers/hwmon/lm80.c
@@ -286,8 +286,9 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
data->fan_div[nr] = 3;
break;
default:
- dev_err(&client->dev, "fan_div value %ld not "
- "supported. Choose one of 1, 2, 4 or 8!\n", val);
+ dev_err(&client->dev,
+ "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n",
+ val);
mutex_unlock(&data->update_lock);
return -EINVAL;
}
diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c
index 47ade8ba152d..3894c408fda3 100644
--- a/drivers/hwmon/lm85.c
+++ b/drivers/hwmon/lm85.c
@@ -1293,8 +1293,8 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info)
company = lm85_read_value(client, LM85_REG_COMPANY);
verstep = lm85_read_value(client, LM85_REG_VERSTEP);
- dev_dbg(&adapter->dev, "Detecting device at 0x%02x with "
- "COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
+ dev_dbg(&adapter->dev,
+ "Detecting device at 0x%02x with COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
address, company, verstep);
/* All supported chips have the version in common */
diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c
index b40f34cdb3ca..a6f46058b1be 100644
--- a/drivers/hwmon/lm93.c
+++ b/drivers/hwmon/lm93.c
@@ -354,12 +354,12 @@ static const unsigned long lm93_vin_val_max[16] = {
static unsigned LM93_IN_FROM_REG(int nr, u8 reg)
{
- const long uV_max = lm93_vin_val_max[nr] * 1000;
- const long uV_min = lm93_vin_val_min[nr] * 1000;
+ const long uv_max = lm93_vin_val_max[nr] * 1000;
+ const long uv_min = lm93_vin_val_min[nr] * 1000;
- const long slope = (uV_max - uV_min) /
+ const long slope = (uv_max - uv_min) /
(lm93_vin_reg_max[nr] - lm93_vin_reg_min[nr]);
- const long intercept = uV_min - slope * lm93_vin_reg_min[nr];
+ const long intercept = uv_min - slope * lm93_vin_reg_min[nr];
return (slope * reg + intercept + 500) / 1000;
}
@@ -371,20 +371,20 @@ static unsigned LM93_IN_FROM_REG(int nr, u8 reg)
static u8 LM93_IN_TO_REG(int nr, unsigned val)
{
/* range limit */
- const long mV = clamp_val(val,
+ const long mv = clamp_val(val,
lm93_vin_val_min[nr], lm93_vin_val_max[nr]);
/* try not to lose too much precision here */
- const long uV = mV * 1000;
- const long uV_max = lm93_vin_val_max[nr] * 1000;
- const long uV_min = lm93_vin_val_min[nr] * 1000;
+ const long uv = mv * 1000;
+ const long uv_max = lm93_vin_val_max[nr] * 1000;
+ const long uv_min = lm93_vin_val_min[nr] * 1000;
/* convert */
- const long slope = (uV_max - uV_min) /
+ const long slope = (uv_max - uv_min) /
(lm93_vin_reg_max[nr] - lm93_vin_reg_min[nr]);
- const long intercept = uV_min - slope * lm93_vin_reg_min[nr];
+ const long intercept = uv_min - slope * lm93_vin_reg_min[nr];
- u8 result = ((uV - intercept + (slope/2)) / slope);
+ u8 result = ((uv - intercept + (slope/2)) / slope);
result = clamp_val(result,
lm93_vin_reg_min[nr], lm93_vin_reg_max[nr]);
return result;
@@ -393,10 +393,10 @@ static u8 LM93_IN_TO_REG(int nr, unsigned val)
/* vid in mV, upper == 0 indicates low limit, otherwise upper limit */
static unsigned LM93_IN_REL_FROM_REG(u8 reg, int upper, int vid)
{
- const long uV_offset = upper ? (((reg >> 4 & 0x0f) + 1) * 12500) :
+ const long uv_offset = upper ? (((reg >> 4 & 0x0f) + 1) * 12500) :
(((reg >> 0 & 0x0f) + 1) * -25000);
- const long uV_vid = vid * 1000;
- return (uV_vid + uV_offset + 5000) / 10000;
+ const long uv_vid = vid * 1000;
+ return (uv_vid + uv_offset + 5000) / 10000;
}
#define LM93_IN_MIN_FROM_REG(reg, vid) LM93_IN_REL_FROM_REG((reg), 0, (vid))
@@ -409,13 +409,13 @@ static unsigned LM93_IN_REL_FROM_REG(u8 reg, int upper, int vid)
*/
static u8 LM93_IN_REL_TO_REG(unsigned val, int upper, int vid)
{
- long uV_offset = vid * 1000 - val * 10000;
+ long uv_offset = vid * 1000 - val * 10000;
if (upper) {
- uV_offset = clamp_val(uV_offset, 12500, 200000);
- return (u8)((uV_offset / 12500 - 1) << 4);
+ uv_offset = clamp_val(uv_offset, 12500, 200000);
+ return (u8)((uv_offset / 12500 - 1) << 4);
} else {
- uV_offset = clamp_val(uV_offset, -400000, -25000);
- return (u8)((uV_offset / -25000 - 1) << 0);
+ uv_offset = clamp_val(uv_offset, -400000, -25000);
+ return (u8)((uv_offset / -25000 - 1) << 0);
}
}
@@ -818,8 +818,9 @@ static u8 lm93_read_byte(struct i2c_client *client, u8 reg)
if (value >= 0) {
return value;
} else {
- dev_warn(&client->dev, "lm93: read byte data failed, "
- "address 0x%02x.\n", reg);
+ dev_warn(&client->dev,
+ "lm93: read byte data failed, address 0x%02x.\n",
+ reg);
mdelay(i + 3);
}
@@ -838,8 +839,9 @@ static int lm93_write_byte(struct i2c_client *client, u8 reg, u8 value)
result = i2c_smbus_write_byte_data(client, reg, value);
if (result < 0)
- dev_warn(&client->dev, "lm93: write byte data failed, "
- "0x%02x at address 0x%02x.\n", value, reg);
+ dev_warn(&client->dev,
+ "lm93: write byte data failed, 0x%02x at address 0x%02x.\n",
+ value, reg);
return result;
}
@@ -854,8 +856,9 @@ static u16 lm93_read_word(struct i2c_client *client, u8 reg)
if (value >= 0) {
return value;
} else {
- dev_warn(&client->dev, "lm93: read word data failed, "
- "address 0x%02x.\n", reg);
+ dev_warn(&client->dev,
+ "lm93: read word data failed, address 0x%02x.\n",
+ reg);
mdelay(i + 3);
}
@@ -874,8 +877,9 @@ static int lm93_write_word(struct i2c_client *client, u8 reg, u16 value)
result = i2c_smbus_write_word_data(client, reg, value);
if (result < 0)
- dev_warn(&client->dev, "lm93: write word data failed, "
- "0x%04x at address 0x%02x.\n", value, reg);
+ dev_warn(&client->dev,
+ "lm93: write word data failed, 0x%04x at address 0x%02x.\n",
+ value, reg);
return result;
}
@@ -898,8 +902,8 @@ static void lm93_read_block(struct i2c_client *client, u8 fbn, u8 *values)
if (result == lm93_block_read_cmds[fbn].len) {
break;
} else {
- dev_warn(&client->dev, "lm93: block read data failed, "
- "command 0x%02x.\n",
+ dev_warn(&client->dev,
+ "lm93: block read data failed, command 0x%02x.\n",
lm93_block_read_cmds[fbn].cmd);
mdelay(i + 3);
}
@@ -2672,8 +2676,8 @@ static void lm93_init_client(struct i2c_client *client)
return;
}
- dev_warn(&client->dev, "timed out waiting for sensor "
- "chip to signal ready!\n");
+ dev_warn(&client->dev,
+ "timed out waiting for sensor chip to signal ready!\n");
}
/* Return 0 if detection is successful, -ENODEV otherwise */
@@ -2733,12 +2737,12 @@ static int lm93_probe(struct i2c_client *client,
dev_dbg(&client->dev, "using SMBus block data transactions\n");
update = lm93_update_client_full;
} else if ((LM93_SMBUS_FUNC_MIN & func) == LM93_SMBUS_FUNC_MIN) {
- dev_dbg(&client->dev, "disabled SMBus block data "
- "transactions\n");
+ dev_dbg(&client->dev,
+ "disabled SMBus block data transactions\n");
update = lm93_update_client_min;
} else {
- dev_dbg(&client->dev, "detect failed, "
- "smbus byte and/or word data not supported!\n");
+ dev_dbg(&client->dev,
+ "detect failed, smbus byte and/or word data not supported!\n");
return -ENODEV;
}
diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c
new file mode 100644
index 000000000000..307c9eaeeb9f
--- /dev/null
+++ b/drivers/hwmon/lm95234.c
@@ -0,0 +1,769 @@
+/*
+ * Driver for Texas Instruments / National Semiconductor LM95234
+ *
+ * Copyright (c) 2013 Guenter Roeck <linux@roeck-us.net>
+ *
+ * Derived from lm95241.c
+ * Copyright (C) 2008, 2010 Davide Rizzo <elpa.rizzo@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/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+
+#define DRVNAME "lm95234"
+
+static const unsigned short normal_i2c[] = { 0x18, 0x4d, 0x4e, I2C_CLIENT_END };
+
+/* LM95234 registers */
+#define LM95234_REG_MAN_ID 0xFE
+#define LM95234_REG_CHIP_ID 0xFF
+#define LM95234_REG_STATUS 0x02
+#define LM95234_REG_CONFIG 0x03
+#define LM95234_REG_CONVRATE 0x04
+#define LM95234_REG_STS_FAULT 0x07
+#define LM95234_REG_STS_TCRIT1 0x08
+#define LM95234_REG_STS_TCRIT2 0x09
+#define LM95234_REG_TEMPH(x) ((x) + 0x10)
+#define LM95234_REG_TEMPL(x) ((x) + 0x20)
+#define LM95234_REG_UTEMPH(x) ((x) + 0x19) /* Remote only */
+#define LM95234_REG_UTEMPL(x) ((x) + 0x29)
+#define LM95234_REG_REM_MODEL 0x30
+#define LM95234_REG_REM_MODEL_STS 0x38
+#define LM95234_REG_OFFSET(x) ((x) + 0x31) /* Remote only */
+#define LM95234_REG_TCRIT1(x) ((x) + 0x40)
+#define LM95234_REG_TCRIT2(x) ((x) + 0x49) /* Remote channel 1,2 */
+#define LM95234_REG_TCRIT_HYST 0x5a
+
+#define NATSEMI_MAN_ID 0x01
+#define LM95234_CHIP_ID 0x79
+
+/* Client data (each client gets its own) */
+struct lm95234_data {
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+ unsigned long last_updated, interval; /* in jiffies */
+ bool valid; /* false until following fields are valid */
+ /* registers values */
+ int temp[5]; /* temperature (signed) */
+ u32 status; /* fault/alarm status */
+ u8 tcrit1[5]; /* critical temperature limit */
+ u8 tcrit2[2]; /* high temperature limit */
+ s8 toffset[4]; /* remote temperature offset */
+ u8 thyst; /* common hysteresis */
+
+ u8 sensor_type; /* temperature sensor type */
+};
+
+static int lm95234_read_temp(struct i2c_client *client, int index, int *t)
+{
+ int val;
+ u16 temp = 0;
+
+ if (index) {
+ val = i2c_smbus_read_byte_data(client,
+ LM95234_REG_UTEMPH(index - 1));
+ if (val < 0)
+ return val;
+ temp = val << 8;
+ val = i2c_smbus_read_byte_data(client,
+ LM95234_REG_UTEMPL(index - 1));
+ if (val < 0)
+ return val;
+ temp |= val;
+ *t = temp;
+ }
+ /*
+ * Read signed temperature if unsigned temperature is 0,
+ * or if this is the local sensor.
+ */
+ if (!temp) {
+ val = i2c_smbus_read_byte_data(client,
+ LM95234_REG_TEMPH(index));
+ if (val < 0)
+ return val;
+ temp = val << 8;
+ val = i2c_smbus_read_byte_data(client,
+ LM95234_REG_TEMPL(index));
+ if (val < 0)
+ return val;
+ temp |= val;
+ *t = (s16)temp;
+ }
+ return 0;
+}
+
+static u16 update_intervals[] = { 143, 364, 1000, 2500 };
+
+/* Fill value cache. Must be called with update lock held. */
+
+static int lm95234_fill_cache(struct i2c_client *client)
+{
+ struct lm95234_data *data = i2c_get_clientdata(client);
+ int i, ret;
+
+ ret = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE);
+ if (ret < 0)
+ return ret;
+
+ data->interval = msecs_to_jiffies(update_intervals[ret & 0x03]);
+
+ for (i = 0; i < ARRAY_SIZE(data->tcrit1); i++) {
+ ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT1(i));
+ if (ret < 0)
+ return ret;
+ data->tcrit1[i] = ret;
+ }
+ for (i = 0; i < ARRAY_SIZE(data->tcrit2); i++) {
+ ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT2(i));
+ if (ret < 0)
+ return ret;
+ data->tcrit2[i] = ret;
+ }
+ for (i = 0; i < ARRAY_SIZE(data->toffset); i++) {
+ ret = i2c_smbus_read_byte_data(client, LM95234_REG_OFFSET(i));
+ if (ret < 0)
+ return ret;
+ data->toffset[i] = ret;
+ }
+
+ ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT_HYST);
+ if (ret < 0)
+ return ret;
+ data->thyst = ret;
+
+ ret = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL);
+ if (ret < 0)
+ return ret;
+ data->sensor_type = ret;
+
+ return 0;
+}
+
+static int lm95234_update_device(struct i2c_client *client,
+ struct lm95234_data *data)
+{
+ int ret;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + data->interval) ||
+ !data->valid) {
+ int i;
+
+ if (!data->valid) {
+ ret = lm95234_fill_cache(client);
+ if (ret < 0)
+ goto abort;
+ }
+
+ data->valid = false;
+ for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
+ ret = lm95234_read_temp(client, i, &data->temp[i]);
+ if (ret < 0)
+ goto abort;
+ }
+
+ ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_FAULT);
+ if (ret < 0)
+ goto abort;
+ data->status = ret;
+
+ ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_TCRIT1);
+ if (ret < 0)
+ goto abort;
+ data->status |= ret << 8;
+
+ ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_TCRIT2);
+ if (ret < 0)
+ goto abort;
+ data->status |= ret << 16;
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+ ret = 0;
+abort:
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95234_data *data = i2c_get_clientdata(client);
+ int index = to_sensor_dev_attr(attr)->index;
+ int ret = lm95234_update_device(client, data);
+
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%d\n",
+ DIV_ROUND_CLOSEST(data->temp[index] * 125, 32));
+}
+
+static ssize_t show_alarm(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95234_data *data = i2c_get_clientdata(client);
+ u32 mask = to_sensor_dev_attr(attr)->index;
+ int ret = lm95234_update_device(client, data);
+
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u", !!(data->status & mask));
+}
+
+static ssize_t show_type(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95234_data *data = i2c_get_clientdata(client);
+ u8 mask = to_sensor_dev_attr(attr)->index;
+ int ret = lm95234_update_device(client, data);
+
+ if (ret)
+ return ret;
+
+ return sprintf(buf, data->sensor_type & mask ? "1\n" : "2\n");
+}
+
+static ssize_t set_type(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95234_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ u8 mask = to_sensor_dev_attr(attr)->index;
+ int ret = lm95234_update_device(client, data);
+
+ if (ret)
+ return ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val != 1 && val != 2)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ if (val == 1)
+ data->sensor_type |= mask;
+ else
+ data->sensor_type &= ~mask;
+ data->valid = false;
+ i2c_smbus_write_byte_data(client, LM95234_REG_REM_MODEL,
+ data->sensor_type);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t show_tcrit2(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95234_data *data = i2c_get_clientdata(client);
+ int index = to_sensor_dev_attr(attr)->index;
+ int ret = lm95234_update_device(client, data);
+
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u", data->tcrit2[index] * 1000);
+}
+
+static ssize_t set_tcrit2(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95234_data *data = i2c_get_clientdata(client);
+ int index = to_sensor_dev_attr(attr)->index;
+ long val;
+ int ret = lm95234_update_device(client, data);
+
+ if (ret)
+ return ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, index ? 255 : 127);
+
+ mutex_lock(&data->update_lock);
+ data->tcrit2[index] = val;
+ i2c_smbus_write_byte_data(client, LM95234_REG_TCRIT2(index), val);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t show_tcrit2_hyst(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95234_data *data = i2c_get_clientdata(client);
+ int index = to_sensor_dev_attr(attr)->index;
+ int ret = lm95234_update_device(client, data);
+
+ if (ret)
+ return ret;
+
+ /* Result can be negative, so be careful with unsigned operands */
+ return sprintf(buf, "%d",
+ ((int)data->tcrit2[index] - (int)data->thyst) * 1000);
+}
+
+static ssize_t show_tcrit1(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95234_data *data = i2c_get_clientdata(client);
+ int index = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%u", data->tcrit1[index] * 1000);
+}
+
+static ssize_t set_tcrit1(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95234_data *data = i2c_get_clientdata(client);
+ int index = to_sensor_dev_attr(attr)->index;
+ long val;
+ int ret = lm95234_update_device(client, data);
+
+ if (ret)
+ return ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 255);
+
+ mutex_lock(&data->update_lock);
+ data->tcrit1[index] = val;
+ i2c_smbus_write_byte_data(client, LM95234_REG_TCRIT1(index), val);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t show_tcrit1_hyst(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95234_data *data = i2c_get_clientdata(client);
+ int index = to_sensor_dev_attr(attr)->index;
+ int ret = lm95234_update_device(client, data);
+
+ if (ret)
+ return ret;
+
+ /* Result can be negative, so be careful with unsigned operands */
+ return sprintf(buf, "%d",
+ ((int)data->tcrit1[index] - (int)data->thyst) * 1000);
+}
+
+static ssize_t set_tcrit1_hyst(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95234_data *data = i2c_get_clientdata(client);
+ int index = to_sensor_dev_attr(attr)->index;
+ long val;
+ int ret = lm95234_update_device(client, data);
+
+ if (ret)
+ return ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ val = DIV_ROUND_CLOSEST(val, 1000);
+ val = clamp_val((int)data->tcrit1[index] - val, 0, 31);
+
+ mutex_lock(&data->update_lock);
+ data->thyst = val;
+ i2c_smbus_write_byte_data(client, LM95234_REG_TCRIT_HYST, val);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t show_offset(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95234_data *data = i2c_get_clientdata(client);
+ int index = to_sensor_dev_attr(attr)->index;
+ int ret = lm95234_update_device(client, data);
+
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%d", data->toffset[index] * 500);
+}
+
+static ssize_t set_offset(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95234_data *data = i2c_get_clientdata(client);
+ int index = to_sensor_dev_attr(attr)->index;
+ long val;
+ int ret = lm95234_update_device(client, data);
+
+ if (ret)
+ return ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ /* Accuracy is 1/2 degrees C */
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 500), -128, 127);
+
+ mutex_lock(&data->update_lock);
+ data->toffset[index] = val;
+ i2c_smbus_write_byte_data(client, LM95234_REG_OFFSET(index), val);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t show_interval(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95234_data *data = i2c_get_clientdata(client);
+ int ret = lm95234_update_device(client, data);
+
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%lu\n",
+ DIV_ROUND_CLOSEST(data->interval * 1000, HZ));
+}
+
+static ssize_t set_interval(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm95234_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ u8 regval;
+ int ret = lm95234_update_device(client, data);
+
+ if (ret)
+ return ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ for (regval = 0; regval < 3; regval++) {
+ if (val <= update_intervals[regval])
+ break;
+ }
+
+ mutex_lock(&data->update_lock);
+ data->interval = msecs_to_jiffies(update_intervals[regval]);
+ i2c_smbus_write_byte_data(client, LM95234_REG_CONVRATE, regval);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4);
+
+static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL,
+ BIT(0) | BIT(1));
+static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL,
+ BIT(2) | BIT(3));
+static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_alarm, NULL,
+ BIT(4) | BIT(5));
+static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_alarm, NULL,
+ BIT(6) | BIT(7));
+
+static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, set_type,
+ BIT(1));
+static SENSOR_DEVICE_ATTR(temp3_type, S_IWUSR | S_IRUGO, show_type, set_type,
+ BIT(2));
+static SENSOR_DEVICE_ATTR(temp4_type, S_IWUSR | S_IRUGO, show_type, set_type,
+ BIT(3));
+static SENSOR_DEVICE_ATTR(temp5_type, S_IWUSR | S_IRUGO, show_type, set_type,
+ BIT(4));
+
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_tcrit1,
+ set_tcrit1, 0);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_tcrit2,
+ set_tcrit2, 0);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_tcrit2,
+ set_tcrit2, 1);
+static SENSOR_DEVICE_ATTR(temp4_max, S_IWUSR | S_IRUGO, show_tcrit1,
+ set_tcrit1, 3);
+static SENSOR_DEVICE_ATTR(temp5_max, S_IWUSR | S_IRUGO, show_tcrit1,
+ set_tcrit1, 4);
+
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_tcrit1_hyst,
+ set_tcrit1_hyst, 0);
+static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IRUGO, show_tcrit2_hyst, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IRUGO, show_tcrit2_hyst, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp4_max_hyst, S_IRUGO, show_tcrit1_hyst, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_max_hyst, S_IRUGO, show_tcrit1_hyst, NULL, 4);
+
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL,
+ BIT(0 + 8));
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL,
+ BIT(1 + 16));
+static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL,
+ BIT(2 + 16));
+static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL,
+ BIT(3 + 8));
+static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_alarm, NULL,
+ BIT(4 + 8));
+
+static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_tcrit1,
+ set_tcrit1, 1);
+static SENSOR_DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_tcrit1,
+ set_tcrit1, 2);
+
+static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_tcrit1_hyst, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_tcrit1_hyst, NULL, 2);
+
+static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL,
+ BIT(1 + 8));
+static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL,
+ BIT(2 + 8));
+
+static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_offset,
+ set_offset, 0);
+static SENSOR_DEVICE_ATTR(temp3_offset, S_IWUSR | S_IRUGO, show_offset,
+ set_offset, 1);
+static SENSOR_DEVICE_ATTR(temp4_offset, S_IWUSR | S_IRUGO, show_offset,
+ set_offset, 2);
+static SENSOR_DEVICE_ATTR(temp5_offset, S_IWUSR | S_IRUGO, show_offset,
+ set_offset, 3);
+
+static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval,
+ set_interval);
+
+static struct attribute *lm95234_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp5_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+ &sensor_dev_attr_temp4_fault.dev_attr.attr,
+ &sensor_dev_attr_temp5_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_type.dev_attr.attr,
+ &sensor_dev_attr_temp3_type.dev_attr.attr,
+ &sensor_dev_attr_temp4_type.dev_attr.attr,
+ &sensor_dev_attr_temp5_type.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp5_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_offset.dev_attr.attr,
+ &sensor_dev_attr_temp3_offset.dev_attr.attr,
+ &sensor_dev_attr_temp4_offset.dev_attr.attr,
+ &sensor_dev_attr_temp5_offset.dev_attr.attr,
+ &dev_attr_update_interval.attr,
+ NULL
+};
+
+static const struct attribute_group lm95234_group = {
+ .attrs = lm95234_attributes,
+};
+
+static int lm95234_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int mfg_id, chip_id, val;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ mfg_id = i2c_smbus_read_byte_data(client, LM95234_REG_MAN_ID);
+ if (mfg_id != NATSEMI_MAN_ID)
+ return -ENODEV;
+
+ chip_id = i2c_smbus_read_byte_data(client, LM95234_REG_CHIP_ID);
+ if (chip_id != LM95234_CHIP_ID)
+ return -ENODEV;
+
+ val = i2c_smbus_read_byte_data(client, LM95234_REG_STATUS);
+ if (val & 0x30)
+ return -ENODEV;
+
+ val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG);
+ if (val & 0xbc)
+ return -ENODEV;
+
+ val = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE);
+ if (val & 0xfc)
+ return -ENODEV;
+
+ val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL);
+ if (val & 0xe1)
+ return -ENODEV;
+
+ val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS);
+ if (val & 0xe1)
+ return -ENODEV;
+
+ strlcpy(info->type, "lm95234", I2C_NAME_SIZE);
+ return 0;
+}
+
+static int lm95234_init_client(struct i2c_client *client)
+{
+ int val, model;
+
+ /* start conversion if necessary */
+ val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG);
+ if (val < 0)
+ return val;
+ if (val & 0x40)
+ i2c_smbus_write_byte_data(client, LM95234_REG_CONFIG,
+ val & ~0x40);
+
+ /* If diode type status reports an error, try to fix it */
+ val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS);
+ if (val < 0)
+ return val;
+ model = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL);
+ if (model < 0)
+ return model;
+ if (model & val) {
+ dev_notice(&client->dev,
+ "Fixing remote diode type misconfiguration (0x%x)\n",
+ val);
+ i2c_smbus_write_byte_data(client, LM95234_REG_REM_MODEL,
+ model & ~val);
+ }
+ return 0;
+}
+
+static int lm95234_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct lm95234_data *data;
+ int err;
+
+ data = devm_kzalloc(dev, sizeof(struct lm95234_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ /* Initialize the LM95234 chip */
+ err = lm95234_init_client(client);
+ if (err < 0)
+ return err;
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&dev->kobj, &lm95234_group);
+ if (err)
+ return err;
+
+ data->hwmon_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove_files;
+ }
+
+ return 0;
+
+exit_remove_files:
+ sysfs_remove_group(&dev->kobj, &lm95234_group);
+ return err;
+}
+
+static int lm95234_remove(struct i2c_client *client)
+{
+ struct lm95234_data *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm95234_group);
+
+ return 0;
+}
+
+/* Driver data (common to all clients) */
+static const struct i2c_device_id lm95234_id[] = {
+ { "lm95234", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm95234_id);
+
+static struct i2c_driver lm95234_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = DRVNAME,
+ },
+ .probe = lm95234_probe,
+ .remove = lm95234_remove,
+ .id_table = lm95234_id,
+ .detect = lm95234_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(lm95234_driver);
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("LM95234 sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ltc4151.c b/drivers/hwmon/ltc4151.c
index 4319a94f549d..af81be1237c9 100644
--- a/drivers/hwmon/ltc4151.c
+++ b/drivers/hwmon/ltc4151.c
@@ -146,14 +146,14 @@ static ssize_t ltc4151_show_value(struct device *dev,
/*
* Input voltages.
*/
-static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, \
- ltc4151_show_value, NULL, LTC4151_VIN_H);
-static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, \
- ltc4151_show_value, NULL, LTC4151_ADIN_H);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4151_show_value, NULL,
+ LTC4151_VIN_H);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4151_show_value, NULL,
+ LTC4151_ADIN_H);
/* Currents (via sense resistor) */
-static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, \
- ltc4151_show_value, NULL, LTC4151_SENSE_H);
+static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4151_show_value, NULL,
+ LTC4151_SENSE_H);
/*
* Finally, construct an array of pointers to members of the above objects,
diff --git a/drivers/hwmon/ltc4215.c b/drivers/hwmon/ltc4215.c
index e8876108a6b3..8a142960d69e 100644
--- a/drivers/hwmon/ltc4215.c
+++ b/drivers/hwmon/ltc4215.c
@@ -172,12 +172,12 @@ static ssize_t ltc4215_show_alarm(struct device *dev,
struct device_attribute *da,
char *buf)
{
- struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct ltc4215_data *data = ltc4215_update_device(dev);
- const u8 reg = data->regs[attr->index];
- const u32 mask = attr->nr;
+ const u8 reg = data->regs[LTC4215_STATUS];
+ const u32 mask = attr->index;
- return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0);
+ return snprintf(buf, PAGE_SIZE, "%u\n", !!(reg & mask));
}
/*
@@ -186,39 +186,29 @@ static ssize_t ltc4215_show_alarm(struct device *dev,
* for each register.
*/
-#define LTC4215_VOLTAGE(name, ltc4215_cmd_idx) \
- static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
- ltc4215_show_voltage, NULL, ltc4215_cmd_idx)
-
-#define LTC4215_CURRENT(name) \
- static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
- ltc4215_show_current, NULL, 0);
-
-#define LTC4215_POWER(name) \
- static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
- ltc4215_show_power, NULL, 0);
-
-#define LTC4215_ALARM(name, mask, reg) \
- static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \
- ltc4215_show_alarm, NULL, (mask), reg)
-
/* Construct a sensor_device_attribute structure for each register */
/* Current */
-LTC4215_CURRENT(curr1_input);
-LTC4215_ALARM(curr1_max_alarm, (1 << 2), LTC4215_STATUS);
+static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4215_show_current, NULL, 0);
+static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc4215_show_alarm, NULL,
+ 1 << 2);
/* Power (virtual) */
-LTC4215_POWER(power1_input);
+static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ltc4215_show_power, NULL, 0);
/* Input Voltage */
-LTC4215_VOLTAGE(in1_input, LTC4215_ADIN);
-LTC4215_ALARM(in1_max_alarm, (1 << 0), LTC4215_STATUS);
-LTC4215_ALARM(in1_min_alarm, (1 << 1), LTC4215_STATUS);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4215_show_voltage, NULL,
+ LTC4215_ADIN);
+static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ltc4215_show_alarm, NULL,
+ 1 << 0);
+static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ltc4215_show_alarm, NULL,
+ 1 << 1);
/* Output Voltage */
-LTC4215_VOLTAGE(in2_input, LTC4215_SOURCE);
-LTC4215_ALARM(in2_min_alarm, (1 << 3), LTC4215_STATUS);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4215_show_voltage, NULL,
+ LTC4215_SOURCE);
+static SENSOR_DEVICE_ATTR(in2_min_alarm, S_IRUGO, ltc4215_show_alarm, NULL,
+ 1 << 3);
/*
* Finally, construct an array of pointers to members of the above objects,
diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c
index 3653f79dc2de..cdc1ecc6734d 100644
--- a/drivers/hwmon/ltc4245.c
+++ b/drivers/hwmon/ltc4245.c
@@ -319,80 +319,82 @@ static ssize_t ltc4245_show_gpio(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%u\n", val * 10);
}
-/*
- * These macros are used below in constructing device attribute objects
- * for use with sysfs_create_group() to make a sysfs device file
- * for each register.
- */
-
-#define LTC4245_VOLTAGE(name, ltc4245_cmd_idx) \
- static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
- ltc4245_show_voltage, NULL, ltc4245_cmd_idx)
-
-#define LTC4245_CURRENT(name, ltc4245_cmd_idx) \
- static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
- ltc4245_show_current, NULL, ltc4245_cmd_idx)
-
-#define LTC4245_POWER(name, ltc4245_cmd_idx) \
- static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
- ltc4245_show_power, NULL, ltc4245_cmd_idx)
-
-#define LTC4245_ALARM(name, mask, reg) \
- static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \
- ltc4245_show_alarm, NULL, (mask), reg)
-
-#define LTC4245_GPIO_VOLTAGE(name, gpio_num) \
- static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
- ltc4245_show_gpio, NULL, gpio_num)
-
/* Construct a sensor_device_attribute structure for each register */
/* Input voltages */
-LTC4245_VOLTAGE(in1_input, LTC4245_12VIN);
-LTC4245_VOLTAGE(in2_input, LTC4245_5VIN);
-LTC4245_VOLTAGE(in3_input, LTC4245_3VIN);
-LTC4245_VOLTAGE(in4_input, LTC4245_VEEIN);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4245_show_voltage, NULL,
+ LTC4245_12VIN);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4245_show_voltage, NULL,
+ LTC4245_5VIN);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc4245_show_voltage, NULL,
+ LTC4245_3VIN);
+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc4245_show_voltage, NULL,
+ LTC4245_VEEIN);
/* Input undervoltage alarms */
-LTC4245_ALARM(in1_min_alarm, (1 << 0), LTC4245_FAULT1);
-LTC4245_ALARM(in2_min_alarm, (1 << 1), LTC4245_FAULT1);
-LTC4245_ALARM(in3_min_alarm, (1 << 2), LTC4245_FAULT1);
-LTC4245_ALARM(in4_min_alarm, (1 << 3), LTC4245_FAULT1);
+static SENSOR_DEVICE_ATTR_2(in1_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+ 1 << 0, LTC4245_FAULT1);
+static SENSOR_DEVICE_ATTR_2(in2_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+ 1 << 1, LTC4245_FAULT1);
+static SENSOR_DEVICE_ATTR_2(in3_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+ 1 << 2, LTC4245_FAULT1);
+static SENSOR_DEVICE_ATTR_2(in4_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+ 1 << 3, LTC4245_FAULT1);
/* Currents (via sense resistor) */
-LTC4245_CURRENT(curr1_input, LTC4245_12VSENSE);
-LTC4245_CURRENT(curr2_input, LTC4245_5VSENSE);
-LTC4245_CURRENT(curr3_input, LTC4245_3VSENSE);
-LTC4245_CURRENT(curr4_input, LTC4245_VEESENSE);
+static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4245_show_current, NULL,
+ LTC4245_12VSENSE);
+static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc4245_show_current, NULL,
+ LTC4245_5VSENSE);
+static SENSOR_DEVICE_ATTR(curr3_input, S_IRUGO, ltc4245_show_current, NULL,
+ LTC4245_3VSENSE);
+static SENSOR_DEVICE_ATTR(curr4_input, S_IRUGO, ltc4245_show_current, NULL,
+ LTC4245_VEESENSE);
/* Overcurrent alarms */
-LTC4245_ALARM(curr1_max_alarm, (1 << 4), LTC4245_FAULT1);
-LTC4245_ALARM(curr2_max_alarm, (1 << 5), LTC4245_FAULT1);
-LTC4245_ALARM(curr3_max_alarm, (1 << 6), LTC4245_FAULT1);
-LTC4245_ALARM(curr4_max_alarm, (1 << 7), LTC4245_FAULT1);
+static SENSOR_DEVICE_ATTR_2(curr1_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+ 1 << 4, LTC4245_FAULT1);
+static SENSOR_DEVICE_ATTR_2(curr2_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+ 1 << 5, LTC4245_FAULT1);
+static SENSOR_DEVICE_ATTR_2(curr3_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+ 1 << 6, LTC4245_FAULT1);
+static SENSOR_DEVICE_ATTR_2(curr4_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+ 1 << 7, LTC4245_FAULT1);
/* Output voltages */
-LTC4245_VOLTAGE(in5_input, LTC4245_12VOUT);
-LTC4245_VOLTAGE(in6_input, LTC4245_5VOUT);
-LTC4245_VOLTAGE(in7_input, LTC4245_3VOUT);
-LTC4245_VOLTAGE(in8_input, LTC4245_VEEOUT);
+static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, ltc4245_show_voltage, NULL,
+ LTC4245_12VOUT);
+static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, ltc4245_show_voltage, NULL,
+ LTC4245_5VOUT);
+static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, ltc4245_show_voltage, NULL,
+ LTC4245_3VOUT);
+static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, ltc4245_show_voltage, NULL,
+ LTC4245_VEEOUT);
/* Power Bad alarms */
-LTC4245_ALARM(in5_min_alarm, (1 << 0), LTC4245_FAULT2);
-LTC4245_ALARM(in6_min_alarm, (1 << 1), LTC4245_FAULT2);
-LTC4245_ALARM(in7_min_alarm, (1 << 2), LTC4245_FAULT2);
-LTC4245_ALARM(in8_min_alarm, (1 << 3), LTC4245_FAULT2);
+static SENSOR_DEVICE_ATTR_2(in5_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+ 1 << 0, LTC4245_FAULT2);
+static SENSOR_DEVICE_ATTR_2(in6_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+ 1 << 1, LTC4245_FAULT2);
+static SENSOR_DEVICE_ATTR_2(in7_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+ 1 << 2, LTC4245_FAULT2);
+static SENSOR_DEVICE_ATTR_2(in8_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+ 1 << 3, LTC4245_FAULT2);
/* GPIO voltages */
-LTC4245_GPIO_VOLTAGE(in9_input, 0);
-LTC4245_GPIO_VOLTAGE(in10_input, 1);
-LTC4245_GPIO_VOLTAGE(in11_input, 2);
+static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, ltc4245_show_gpio, NULL, 0);
+static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, ltc4245_show_gpio, NULL, 1);
+static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, ltc4245_show_gpio, NULL, 2);
/* Power Consumption (virtual) */
-LTC4245_POWER(power1_input, LTC4245_12VSENSE);
-LTC4245_POWER(power2_input, LTC4245_5VSENSE);
-LTC4245_POWER(power3_input, LTC4245_3VSENSE);
-LTC4245_POWER(power4_input, LTC4245_VEESENSE);
+static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ltc4245_show_power, NULL,
+ LTC4245_12VSENSE);
+static SENSOR_DEVICE_ATTR(power2_input, S_IRUGO, ltc4245_show_power, NULL,
+ LTC4245_5VSENSE);
+static SENSOR_DEVICE_ATTR(power3_input, S_IRUGO, ltc4245_show_power, NULL,
+ LTC4245_3VSENSE);
+static SENSOR_DEVICE_ATTR(power4_input, S_IRUGO, ltc4245_show_power, NULL,
+ LTC4245_VEESENSE);
/*
* Finally, construct an array of pointers to members of the above objects,
diff --git a/drivers/hwmon/ltc4261.c b/drivers/hwmon/ltc4261.c
index 84a2d2872b20..487da58ec86c 100644
--- a/drivers/hwmon/ltc4261.c
+++ b/drivers/hwmon/ltc4261.c
@@ -165,24 +165,12 @@ static ssize_t ltc4261_show_bool(struct device *dev,
}
/*
- * These macros are used below in constructing device attribute objects
- * for use with sysfs_create_group() to make a sysfs device file
- * for each register.
- */
-
-#define LTC4261_VALUE(name, ltc4261_cmd_idx) \
- static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
- ltc4261_show_value, NULL, ltc4261_cmd_idx)
-
-#define LTC4261_BOOL(name, mask) \
- static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
- ltc4261_show_bool, NULL, (mask))
-
-/*
* Input voltages.
*/
-LTC4261_VALUE(in1_input, LTC4261_ADIN_H);
-LTC4261_VALUE(in2_input, LTC4261_ADIN2_H);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4261_show_value, NULL,
+ LTC4261_ADIN_H);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4261_show_value, NULL,
+ LTC4261_ADIN2_H);
/*
* Voltage alarms. The chip has only one set of voltage alarm status bits,
@@ -192,16 +180,22 @@ LTC4261_VALUE(in2_input, LTC4261_ADIN2_H);
* To ensure that the alarm condition is reported to the user, report it
* with both voltage sensors.
*/
-LTC4261_BOOL(in1_min_alarm, FAULT_UV);
-LTC4261_BOOL(in1_max_alarm, FAULT_OV);
-LTC4261_BOOL(in2_min_alarm, FAULT_UV);
-LTC4261_BOOL(in2_max_alarm, FAULT_OV);
+static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ltc4261_show_bool, NULL,
+ FAULT_UV);
+static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ltc4261_show_bool, NULL,
+ FAULT_OV);
+static SENSOR_DEVICE_ATTR(in2_min_alarm, S_IRUGO, ltc4261_show_bool, NULL,
+ FAULT_UV);
+static SENSOR_DEVICE_ATTR(in2_max_alarm, S_IRUGO, ltc4261_show_bool, NULL,
+ FAULT_OV);
/* Currents (via sense resistor) */
-LTC4261_VALUE(curr1_input, LTC4261_SENSE_H);
+static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4261_show_value, NULL,
+ LTC4261_SENSE_H);
/* Overcurrent alarm */
-LTC4261_BOOL(curr1_max_alarm, FAULT_OC);
+static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc4261_show_bool, NULL,
+ FAULT_OC);
static struct attribute *ltc4261_attributes[] = {
&sensor_dev_attr_in1_input.dev_attr.attr,
diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c
index bf4aa3777fc1..328fb0353c17 100644
--- a/drivers/hwmon/max6697.c
+++ b/drivers/hwmon/max6697.c
@@ -399,82 +399,95 @@ static SENSOR_DEVICE_ATTR(temp6_fault, S_IRUGO, show_alarm, NULL, 5);
static SENSOR_DEVICE_ATTR(temp7_fault, S_IRUGO, show_alarm, NULL, 6);
static SENSOR_DEVICE_ATTR(temp8_fault, S_IRUGO, show_alarm, NULL, 7);
-static struct attribute *max6697_attributes[8][7] = {
- {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp1_max.dev_attr.attr,
- &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_crit.dev_attr.attr,
- &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
- NULL
- }, {
- &sensor_dev_attr_temp2_input.dev_attr.attr,
- &sensor_dev_attr_temp2_max.dev_attr.attr,
- &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_crit.dev_attr.attr,
- &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_fault.dev_attr.attr,
- NULL
- }, {
- &sensor_dev_attr_temp3_input.dev_attr.attr,
- &sensor_dev_attr_temp3_max.dev_attr.attr,
- &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp3_crit.dev_attr.attr,
- &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp3_fault.dev_attr.attr,
- NULL
- }, {
- &sensor_dev_attr_temp4_input.dev_attr.attr,
- &sensor_dev_attr_temp4_max.dev_attr.attr,
- &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp4_crit.dev_attr.attr,
- &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp4_fault.dev_attr.attr,
- NULL
- }, {
- &sensor_dev_attr_temp5_input.dev_attr.attr,
- &sensor_dev_attr_temp5_max.dev_attr.attr,
- &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp5_crit.dev_attr.attr,
- &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp5_fault.dev_attr.attr,
- NULL
- }, {
- &sensor_dev_attr_temp6_input.dev_attr.attr,
- &sensor_dev_attr_temp6_max.dev_attr.attr,
- &sensor_dev_attr_temp6_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp6_crit.dev_attr.attr,
- &sensor_dev_attr_temp6_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp6_fault.dev_attr.attr,
- NULL
- }, {
- &sensor_dev_attr_temp7_input.dev_attr.attr,
- &sensor_dev_attr_temp7_max.dev_attr.attr,
- &sensor_dev_attr_temp7_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp7_crit.dev_attr.attr,
- &sensor_dev_attr_temp7_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp7_fault.dev_attr.attr,
- NULL
- }, {
- &sensor_dev_attr_temp8_input.dev_attr.attr,
- &sensor_dev_attr_temp8_max.dev_attr.attr,
- &sensor_dev_attr_temp8_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp8_crit.dev_attr.attr,
- &sensor_dev_attr_temp8_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp8_fault.dev_attr.attr,
- NULL
- }
+static DEVICE_ATTR(dummy, 0, NULL, NULL);
+
+static umode_t max6697_is_visible(struct kobject *kobj, struct attribute *attr,
+ int index)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct max6697_data *data = i2c_get_clientdata(client);
+ const struct max6697_chip_data *chip = data->chip;
+ int channel = index / 6; /* channel number */
+ int nr = index % 6; /* attribute index within channel */
+
+ if (channel >= chip->channels)
+ return 0;
+
+ if ((nr == 3 || nr == 4) && !(chip->have_crit & (1 << channel)))
+ return 0;
+ if (nr == 5 && !(chip->have_fault & (1 << channel)))
+ return 0;
+
+ return attr->mode;
+}
+
+/*
+ * max6697_is_visible uses the index into the following array to determine
+ * if attributes should be created or not. Any change in order or content
+ * must be matched in max6697_is_visible.
+ */
+static struct attribute *max6697_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &dev_attr_dummy.attr,
+
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_fault.dev_attr.attr,
+
+ &sensor_dev_attr_temp5_input.dev_attr.attr,
+ &sensor_dev_attr_temp5_max.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_crit.dev_attr.attr,
+ &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_fault.dev_attr.attr,
+
+ &sensor_dev_attr_temp6_input.dev_attr.attr,
+ &sensor_dev_attr_temp6_max.dev_attr.attr,
+ &sensor_dev_attr_temp6_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp6_crit.dev_attr.attr,
+ &sensor_dev_attr_temp6_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp6_fault.dev_attr.attr,
+
+ &sensor_dev_attr_temp7_input.dev_attr.attr,
+ &sensor_dev_attr_temp7_max.dev_attr.attr,
+ &sensor_dev_attr_temp7_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp7_crit.dev_attr.attr,
+ &sensor_dev_attr_temp7_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp7_fault.dev_attr.attr,
+
+ &sensor_dev_attr_temp8_input.dev_attr.attr,
+ &sensor_dev_attr_temp8_max.dev_attr.attr,
+ &sensor_dev_attr_temp8_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp8_crit.dev_attr.attr,
+ &sensor_dev_attr_temp8_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp8_fault.dev_attr.attr,
+ NULL
};
-static const struct attribute_group max6697_group[8] = {
- { .attrs = max6697_attributes[0] },
- { .attrs = max6697_attributes[1] },
- { .attrs = max6697_attributes[2] },
- { .attrs = max6697_attributes[3] },
- { .attrs = max6697_attributes[4] },
- { .attrs = max6697_attributes[5] },
- { .attrs = max6697_attributes[6] },
- { .attrs = max6697_attributes[7] },
+static const struct attribute_group max6697_group = {
+ .attrs = max6697_attributes, .is_visible = max6697_is_visible,
};
static void max6697_get_config_of(struct device_node *node,
@@ -606,21 +619,13 @@ done:
return 0;
}
-static void max6697_remove_files(struct i2c_client *client)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(max6697_group); i++)
- sysfs_remove_group(&client->dev.kobj, &max6697_group[i]);
-}
-
static int max6697_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = client->adapter;
struct device *dev = &client->dev;
struct max6697_data *data;
- int i, err;
+ int err;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
@@ -639,37 +644,9 @@ static int max6697_probe(struct i2c_client *client,
if (err)
return err;
- for (i = 0; i < data->chip->channels; i++) {
- err = sysfs_create_file(&dev->kobj,
- max6697_attributes[i][0]);
- if (err)
- goto error;
- err = sysfs_create_file(&dev->kobj,
- max6697_attributes[i][1]);
- if (err)
- goto error;
- err = sysfs_create_file(&dev->kobj,
- max6697_attributes[i][2]);
- if (err)
- goto error;
-
- if (data->chip->have_crit & (1 << i)) {
- err = sysfs_create_file(&dev->kobj,
- max6697_attributes[i][3]);
- if (err)
- goto error;
- err = sysfs_create_file(&dev->kobj,
- max6697_attributes[i][4]);
- if (err)
- goto error;
- }
- if (data->chip->have_fault & (1 << i)) {
- err = sysfs_create_file(&dev->kobj,
- max6697_attributes[i][5]);
- if (err)
- goto error;
- }
- }
+ err = sysfs_create_group(&client->dev.kobj, &max6697_group);
+ if (err)
+ return err;
data->hwmon_dev = hwmon_device_register(dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -680,7 +657,7 @@ static int max6697_probe(struct i2c_client *client,
return 0;
error:
- max6697_remove_files(client);
+ sysfs_remove_group(&client->dev.kobj, &max6697_group);
return err;
}
@@ -689,7 +666,7 @@ static int max6697_remove(struct i2c_client *client)
struct max6697_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
- max6697_remove_files(client);
+ sysfs_remove_group(&client->dev.kobj, &max6697_group);
return 0;
}
diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c
index 2a7f331cd3c0..982d8622c09b 100644
--- a/drivers/hwmon/mc13783-adc.c
+++ b/drivers/hwmon/mc13783-adc.c
@@ -273,18 +273,7 @@ static struct platform_driver mc13783_adc_driver = {
.id_table = mc13783_adc_idtable,
};
-static int __init mc13783_adc_init(void)
-{
- return platform_driver_probe(&mc13783_adc_driver, mc13783_adc_probe);
-}
-
-static void __exit mc13783_adc_exit(void)
-{
- platform_driver_unregister(&mc13783_adc_driver);
-}
-
-module_init(mc13783_adc_init);
-module_exit(mc13783_adc_exit);
+module_platform_driver_probe(mc13783_adc_driver, mc13783_adc_probe);
MODULE_DESCRIPTION("MC13783 ADC driver");
MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c
new file mode 100644
index 000000000000..f43f5e571db9
--- /dev/null
+++ b/drivers/hwmon/nct6775.c
@@ -0,0 +1,4191 @@
+/*
+ * nct6775 - Driver for the hardware monitoring functionality of
+ * Nuvoton NCT677x Super-I/O chips
+ *
+ * Copyright (C) 2012 Guenter Roeck <linux@roeck-us.net>
+ *
+ * Derived from w83627ehf driver
+ * Copyright (C) 2005-2012 Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2006 Yuan Mu (Winbond),
+ * Rudolf Marek <r.marek@assembler.cz>
+ * David Hubbard <david.c.hubbard@gmail.com>
+ * Daniel J Blueman <daniel.blueman@gmail.com>
+ * Copyright (C) 2010 Sheng-Yuan Huang (Nuvoton) (PS00)
+ *
+ * Shamelessly ripped from the w83627hf driver
+ * Copyright (C) 2003 Mark Studebaker
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * Supports the following chips:
+ *
+ * Chip #vin #fan #pwm #temp chip IDs man ID
+ * nct6775f 9 4 3 6+3 0xb470 0xc1 0x5ca3
+ * nct6776f 9 5 3 6+3 0xc330 0xc1 0x5ca3
+ * nct6779d 15 5 5 2+6 0xc560 0xc1 0x5ca3
+ *
+ * #temp lists the number of monitored temperature sources (first value) plus
+ * the number of directly connectable temperature sensors (second value).
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon-vid.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include "lm75.h"
+
+#define USE_ALTERNATE
+
+enum kinds { nct6775, nct6776, nct6779 };
+
+/* used to set data->name = nct6775_device_names[data->sio_kind] */
+static const char * const nct6775_device_names[] = {
+ "nct6775",
+ "nct6776",
+ "nct6779",
+};
+
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+static unsigned short fan_debounce;
+module_param(fan_debounce, ushort, 0);
+MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
+
+#define DRVNAME "nct6775"
+
+/*
+ * Super-I/O constants and functions
+ */
+
+#define NCT6775_LD_ACPI 0x0a
+#define NCT6775_LD_HWM 0x0b
+#define NCT6775_LD_VID 0x0d
+
+#define SIO_REG_LDSEL 0x07 /* Logical device select */
+#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
+#define SIO_REG_ENABLE 0x30 /* Logical device enable */
+#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
+
+#define SIO_NCT6775_ID 0xb470
+#define SIO_NCT6776_ID 0xc330
+#define SIO_NCT6779_ID 0xc560
+#define SIO_ID_MASK 0xFFF0
+
+enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };
+
+static inline void
+superio_outb(int ioreg, int reg, int val)
+{
+ outb(reg, ioreg);
+ outb(val, ioreg + 1);
+}
+
+static inline int
+superio_inb(int ioreg, int reg)
+{
+ outb(reg, ioreg);
+ return inb(ioreg + 1);
+}
+
+static inline void
+superio_select(int ioreg, int ld)
+{
+ outb(SIO_REG_LDSEL, ioreg);
+ outb(ld, ioreg + 1);
+}
+
+static inline int
+superio_enter(int ioreg)
+{
+ /*
+ * Try to reserve <ioreg> and <ioreg + 1> for exclusive access.
+ */
+ if (!request_muxed_region(ioreg, 2, DRVNAME))
+ return -EBUSY;
+
+ outb(0x87, ioreg);
+ outb(0x87, ioreg);
+
+ return 0;
+}
+
+static inline void
+superio_exit(int ioreg)
+{
+ outb(0xaa, ioreg);
+ outb(0x02, ioreg);
+ outb(0x02, ioreg + 1);
+ release_region(ioreg, 2);
+}
+
+/*
+ * ISA constants
+ */
+
+#define IOREGION_ALIGNMENT (~7)
+#define IOREGION_OFFSET 5
+#define IOREGION_LENGTH 2
+#define ADDR_REG_OFFSET 0
+#define DATA_REG_OFFSET 1
+
+#define NCT6775_REG_BANK 0x4E
+#define NCT6775_REG_CONFIG 0x40
+
+/*
+ * Not currently used:
+ * REG_MAN_ID has the value 0x5ca3 for all supported chips.
+ * REG_CHIP_ID == 0x88/0xa1/0xc1 depending on chip model.
+ * REG_MAN_ID is at port 0x4f
+ * REG_CHIP_ID is at port 0x58
+ */
+
+#define NUM_TEMP 10 /* Max number of temp attribute sets w/ limits*/
+#define NUM_TEMP_FIXED 6 /* Max number of fixed temp attribute sets */
+
+#define NUM_REG_ALARM 4 /* Max number of alarm registers */
+
+/* Common and NCT6775 specific data */
+
+/* Voltage min/max registers for nr=7..14 are in bank 5 */
+
+static const u16 NCT6775_REG_IN_MAX[] = {
+ 0x2b, 0x2d, 0x2f, 0x31, 0x33, 0x35, 0x37, 0x554, 0x556, 0x558, 0x55a,
+ 0x55c, 0x55e, 0x560, 0x562 };
+static const u16 NCT6775_REG_IN_MIN[] = {
+ 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x555, 0x557, 0x559, 0x55b,
+ 0x55d, 0x55f, 0x561, 0x563 };
+static const u16 NCT6775_REG_IN[] = {
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x550, 0x551, 0x552
+};
+
+#define NCT6775_REG_VBAT 0x5D
+#define NCT6775_REG_DIODE 0x5E
+
+#define NCT6775_REG_FANDIV1 0x506
+#define NCT6775_REG_FANDIV2 0x507
+
+#define NCT6775_REG_CR_FAN_DEBOUNCE 0xf0
+
+static const u16 NCT6775_REG_ALARM[NUM_REG_ALARM] = { 0x459, 0x45A, 0x45B };
+
+/* 0..15 voltages, 16..23 fans, 24..31 temperatures */
+
+static const s8 NCT6775_ALARM_BITS[] = {
+ 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */
+ 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */
+ -1, /* unused */
+ 6, 7, 11, 10, 23, /* fan1..fan5 */
+ -1, -1, -1, /* unused */
+ 4, 5, 13, -1, -1, -1, /* temp1..temp6 */
+ 12, -1 }; /* intrusion0, intrusion1 */
+
+#define FAN_ALARM_BASE 16
+#define TEMP_ALARM_BASE 24
+#define INTRUSION_ALARM_BASE 30
+
+static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee };
+static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 };
+
+/* DC or PWM output fan configuration */
+static const u8 NCT6775_REG_PWM_MODE[] = { 0x04, 0x04, 0x12 };
+static const u8 NCT6775_PWM_MODE_MASK[] = { 0x01, 0x02, 0x01 };
+
+/* Advanced Fan control, some values are common for all fans */
+
+static const u16 NCT6775_REG_TARGET[] = { 0x101, 0x201, 0x301, 0x801, 0x901 };
+static const u16 NCT6775_REG_FAN_MODE[] = { 0x102, 0x202, 0x302, 0x802, 0x902 };
+static const u16 NCT6775_REG_FAN_STEP_DOWN_TIME[] = {
+ 0x103, 0x203, 0x303, 0x803, 0x903 };
+static const u16 NCT6775_REG_FAN_STEP_UP_TIME[] = {
+ 0x104, 0x204, 0x304, 0x804, 0x904 };
+static const u16 NCT6775_REG_FAN_STOP_OUTPUT[] = {
+ 0x105, 0x205, 0x305, 0x805, 0x905 };
+static const u16 NCT6775_REG_FAN_START_OUTPUT[]
+ = { 0x106, 0x206, 0x306, 0x806, 0x906 };
+static const u16 NCT6775_REG_FAN_MAX_OUTPUT[] = { 0x10a, 0x20a, 0x30a };
+static const u16 NCT6775_REG_FAN_STEP_OUTPUT[] = { 0x10b, 0x20b, 0x30b };
+
+static const u16 NCT6775_REG_FAN_STOP_TIME[] = {
+ 0x107, 0x207, 0x307, 0x807, 0x907 };
+static const u16 NCT6775_REG_PWM[] = { 0x109, 0x209, 0x309, 0x809, 0x909 };
+static const u16 NCT6775_REG_PWM_READ[] = { 0x01, 0x03, 0x11, 0x13, 0x15 };
+
+static const u16 NCT6775_REG_FAN[] = { 0x630, 0x632, 0x634, 0x636, 0x638 };
+static const u16 NCT6775_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d };
+static const u16 NCT6775_REG_FAN_PULSES[] = { 0x641, 0x642, 0x643, 0x644, 0 };
+
+static const u16 NCT6775_REG_TEMP[] = {
+ 0x27, 0x150, 0x250, 0x62b, 0x62c, 0x62d };
+
+static const u16 NCT6775_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
+ 0, 0x152, 0x252, 0x628, 0x629, 0x62A };
+static const u16 NCT6775_REG_TEMP_HYST[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
+ 0x3a, 0x153, 0x253, 0x673, 0x678, 0x67D };
+static const u16 NCT6775_REG_TEMP_OVER[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
+ 0x39, 0x155, 0x255, 0x672, 0x677, 0x67C };
+
+static const u16 NCT6775_REG_TEMP_SOURCE[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
+ 0x621, 0x622, 0x623, 0x624, 0x625, 0x626 };
+
+static const u16 NCT6775_REG_TEMP_SEL[] = {
+ 0x100, 0x200, 0x300, 0x800, 0x900 };
+
+static const u16 NCT6775_REG_WEIGHT_TEMP_SEL[] = {
+ 0x139, 0x239, 0x339, 0x839, 0x939 };
+static const u16 NCT6775_REG_WEIGHT_TEMP_STEP[] = {
+ 0x13a, 0x23a, 0x33a, 0x83a, 0x93a };
+static const u16 NCT6775_REG_WEIGHT_TEMP_STEP_TOL[] = {
+ 0x13b, 0x23b, 0x33b, 0x83b, 0x93b };
+static const u16 NCT6775_REG_WEIGHT_DUTY_STEP[] = {
+ 0x13c, 0x23c, 0x33c, 0x83c, 0x93c };
+static const u16 NCT6775_REG_WEIGHT_TEMP_BASE[] = {
+ 0x13d, 0x23d, 0x33d, 0x83d, 0x93d };
+
+static const u16 NCT6775_REG_TEMP_OFFSET[] = { 0x454, 0x455, 0x456 };
+
+static const u16 NCT6775_REG_AUTO_TEMP[] = {
+ 0x121, 0x221, 0x321, 0x821, 0x921 };
+static const u16 NCT6775_REG_AUTO_PWM[] = {
+ 0x127, 0x227, 0x327, 0x827, 0x927 };
+
+#define NCT6775_AUTO_TEMP(data, nr, p) ((data)->REG_AUTO_TEMP[nr] + (p))
+#define NCT6775_AUTO_PWM(data, nr, p) ((data)->REG_AUTO_PWM[nr] + (p))
+
+static const u16 NCT6775_REG_CRITICAL_ENAB[] = { 0x134, 0x234, 0x334 };
+
+static const u16 NCT6775_REG_CRITICAL_TEMP[] = {
+ 0x135, 0x235, 0x335, 0x835, 0x935 };
+static const u16 NCT6775_REG_CRITICAL_TEMP_TOLERANCE[] = {
+ 0x138, 0x238, 0x338, 0x838, 0x938 };
+
+static const char *const nct6775_temp_label[] = {
+ "",
+ "SYSTIN",
+ "CPUTIN",
+ "AUXTIN",
+ "AMD SB-TSI",
+ "PECI Agent 0",
+ "PECI Agent 1",
+ "PECI Agent 2",
+ "PECI Agent 3",
+ "PECI Agent 4",
+ "PECI Agent 5",
+ "PECI Agent 6",
+ "PECI Agent 7",
+ "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"
+};
+
+static const u16 NCT6775_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6775_temp_label) - 1]
+ = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x661, 0x662, 0x664 };
+
+static const u16 NCT6775_REG_TEMP_CRIT[ARRAY_SIZE(nct6775_temp_label) - 1]
+ = { 0, 0, 0, 0, 0xa00, 0xa01, 0xa02, 0xa03, 0xa04, 0xa05, 0xa06,
+ 0xa07 };
+
+/* NCT6776 specific data */
+
+static const s8 NCT6776_ALARM_BITS[] = {
+ 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */
+ 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */
+ -1, /* unused */
+ 6, 7, 11, 10, 23, /* fan1..fan5 */
+ -1, -1, -1, /* unused */
+ 4, 5, 13, -1, -1, -1, /* temp1..temp6 */
+ 12, 9 }; /* intrusion0, intrusion1 */
+
+static const u16 NCT6776_REG_TOLERANCE_H[] = {
+ 0x10c, 0x20c, 0x30c, 0x80c, 0x90c };
+
+static const u8 NCT6776_REG_PWM_MODE[] = { 0x04, 0, 0 };
+static const u8 NCT6776_PWM_MODE_MASK[] = { 0x01, 0, 0 };
+
+static const u16 NCT6776_REG_FAN_MIN[] = { 0x63a, 0x63c, 0x63e, 0x640, 0x642 };
+static const u16 NCT6776_REG_FAN_PULSES[] = { 0x644, 0x645, 0x646, 0, 0 };
+
+static const u16 NCT6776_REG_WEIGHT_DUTY_BASE[] = {
+ 0x13e, 0x23e, 0x33e, 0x83e, 0x93e };
+
+static const u16 NCT6776_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
+ 0x18, 0x152, 0x252, 0x628, 0x629, 0x62A };
+
+static const char *const nct6776_temp_label[] = {
+ "",
+ "SYSTIN",
+ "CPUTIN",
+ "AUXTIN",
+ "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"
+};
+
+static const u16 NCT6776_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6776_temp_label) - 1]
+ = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x401, 0x402, 0x404 };
+
+static const u16 NCT6776_REG_TEMP_CRIT[ARRAY_SIZE(nct6776_temp_label) - 1]
+ = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a };
+
+/* NCT6779 specific data */
+
+static const u16 NCT6779_REG_IN[] = {
+ 0x480, 0x481, 0x482, 0x483, 0x484, 0x485, 0x486, 0x487,
+ 0x488, 0x489, 0x48a, 0x48b, 0x48c, 0x48d, 0x48e };
+
+static const u16 NCT6779_REG_ALARM[NUM_REG_ALARM] = {
+ 0x459, 0x45A, 0x45B, 0x568 };
+
+static const s8 NCT6779_ALARM_BITS[] = {
+ 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */
+ 17, 24, 25, 26, 27, 28, 29, /* in8..in14 */
+ -1, /* unused */
+ 6, 7, 11, 10, 23, /* fan1..fan5 */
+ -1, -1, -1, /* unused */
+ 4, 5, 13, -1, -1, -1, /* temp1..temp6 */
+ 12, 9 }; /* intrusion0, intrusion1 */
+
+static const u16 NCT6779_REG_FAN[] = { 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8 };
+static const u16 NCT6779_REG_FAN_PULSES[] = {
+ 0x644, 0x645, 0x646, 0x647, 0x648 };
+
+static const u16 NCT6779_REG_CRITICAL_PWM_ENABLE[] = {
+ 0x136, 0x236, 0x336, 0x836, 0x936 };
+static const u16 NCT6779_REG_CRITICAL_PWM[] = {
+ 0x137, 0x237, 0x337, 0x837, 0x937 };
+
+static const u16 NCT6779_REG_TEMP[] = { 0x27, 0x150 };
+static const u16 NCT6779_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6779_REG_TEMP)] = {
+ 0x18, 0x152 };
+static const u16 NCT6779_REG_TEMP_HYST[ARRAY_SIZE(NCT6779_REG_TEMP)] = {
+ 0x3a, 0x153 };
+static const u16 NCT6779_REG_TEMP_OVER[ARRAY_SIZE(NCT6779_REG_TEMP)] = {
+ 0x39, 0x155 };
+
+static const u16 NCT6779_REG_TEMP_OFFSET[] = {
+ 0x454, 0x455, 0x456, 0x44a, 0x44b, 0x44c };
+
+static const char *const nct6779_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"
+};
+
+static const u16 NCT6779_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6779_temp_label) - 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]
+ = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a };
+
+static enum pwm_enable reg_to_pwm_enable(int pwm, int mode)
+{
+ if (mode == 0 && pwm == 255)
+ return off;
+ return mode + 1;
+}
+
+static int pwm_enable_to_reg(enum pwm_enable mode)
+{
+ if (mode == off)
+ return 0;
+ return mode - 1;
+}
+
+/*
+ * Conversions
+ */
+
+/* 1 is DC mode, output in ms */
+static unsigned int step_time_from_reg(u8 reg, u8 mode)
+{
+ return mode ? 400 * reg : 100 * reg;
+}
+
+static u8 step_time_to_reg(unsigned int msec, u8 mode)
+{
+ return clamp_val((mode ? (msec + 200) / 400 :
+ (msec + 50) / 100), 1, 255);
+}
+
+static unsigned int fan_from_reg8(u16 reg, unsigned int divreg)
+{
+ if (reg == 0 || reg == 255)
+ return 0;
+ return 1350000U / (reg << divreg);
+}
+
+static unsigned int fan_from_reg13(u16 reg, unsigned int divreg)
+{
+ if ((reg & 0xff1f) == 0xff1f)
+ return 0;
+
+ reg = (reg & 0x1f) | ((reg & 0xff00) >> 3);
+
+ if (reg == 0)
+ return 0;
+
+ return 1350000U / reg;
+}
+
+static unsigned int fan_from_reg16(u16 reg, unsigned int divreg)
+{
+ if (reg == 0 || reg == 0xffff)
+ return 0;
+
+ /*
+ * Even though the registers are 16 bit wide, the fan divisor
+ * still applies.
+ */
+ return 1350000U / (reg << divreg);
+}
+
+static u16 fan_to_reg(u32 fan, unsigned int divreg)
+{
+ if (!fan)
+ return 0;
+
+ return (1350000U / fan) >> divreg;
+}
+
+static inline unsigned int
+div_from_reg(u8 reg)
+{
+ return 1 << reg;
+}
+
+/*
+ * Some of the voltage inputs have internal scaling, the tables below
+ * contain 8 (the ADC LSB in mV) * scaling factor * 100
+ */
+static const u16 scale_in[15] = {
+ 800, 800, 1600, 1600, 800, 800, 800, 1600, 1600, 800, 800, 800, 800,
+ 800, 800
+};
+
+static inline long in_from_reg(u8 reg, u8 nr)
+{
+ return DIV_ROUND_CLOSEST(reg * scale_in[nr], 100);
+}
+
+static inline u8 in_to_reg(u32 val, u8 nr)
+{
+ return clamp_val(DIV_ROUND_CLOSEST(val * 100, scale_in[nr]), 0, 255);
+}
+
+/*
+ * Data structures and manipulation thereof
+ */
+
+struct nct6775_data {
+ int addr; /* IO base of hw monitor block */
+ enum kinds kind;
+ const char *name;
+
+ struct device *hwmon_dev;
+
+ u16 reg_temp[4][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst,
+ * 3=temp_crit
+ */
+ u8 temp_src[NUM_TEMP];
+ u16 reg_temp_config[NUM_TEMP];
+ const char * const *temp_label;
+ int temp_label_num;
+
+ u16 REG_CONFIG;
+ u16 REG_VBAT;
+ u16 REG_DIODE;
+
+ const s8 *ALARM_BITS;
+
+ const u16 *REG_VIN;
+ const u16 *REG_IN_MINMAX[2];
+
+ const u16 *REG_TARGET;
+ const u16 *REG_FAN;
+ const u16 *REG_FAN_MODE;
+ const u16 *REG_FAN_MIN;
+ const u16 *REG_FAN_PULSES;
+ const u16 *REG_FAN_TIME[3];
+
+ const u16 *REG_TOLERANCE_H;
+
+ const u8 *REG_PWM_MODE;
+ const u8 *PWM_MODE_MASK;
+
+ const u16 *REG_PWM[7]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor,
+ * [3]=pwm_max, [4]=pwm_step,
+ * [5]=weight_duty_step, [6]=weight_duty_base
+ */
+ const u16 *REG_PWM_READ;
+
+ const u16 *REG_AUTO_TEMP;
+ const u16 *REG_AUTO_PWM;
+
+ const u16 *REG_CRITICAL_TEMP;
+ const u16 *REG_CRITICAL_TEMP_TOLERANCE;
+
+ const u16 *REG_TEMP_SOURCE; /* temp register sources */
+ const u16 *REG_TEMP_SEL;
+ const u16 *REG_WEIGHT_TEMP_SEL;
+ const u16 *REG_WEIGHT_TEMP[3]; /* 0=base, 1=tolerance, 2=step */
+
+ const u16 *REG_TEMP_OFFSET;
+
+ const u16 *REG_ALARM;
+
+ unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg);
+ unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg);
+
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ /* Register values */
+ u8 bank; /* current register bank */
+ u8 in_num; /* number of in inputs we have */
+ u8 in[15][3]; /* [0]=in, [1]=in_max, [2]=in_min */
+ unsigned int rpm[5];
+ u16 fan_min[5];
+ u8 fan_pulses[5];
+ u8 fan_div[5];
+ u8 has_pwm;
+ u8 has_fan; /* some fan inputs can be disabled */
+ u8 has_fan_min; /* some fans don't have min register */
+ bool has_fan_div;
+
+ u8 temp_fixed_num; /* 3 or 6 */
+ u8 temp_type[NUM_TEMP_FIXED];
+ s8 temp_offset[NUM_TEMP_FIXED];
+ s16 temp[4][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst,
+ * 3=temp_crit */
+ u64 alarms;
+
+ u8 pwm_num; /* number of pwm */
+ u8 pwm_mode[5]; /* 1->DC variable voltage, 0->PWM variable duty cycle */
+ enum pwm_enable pwm_enable[5];
+ /* 0->off
+ * 1->manual
+ * 2->thermal cruise mode (also called SmartFan I)
+ * 3->fan speed cruise mode
+ * 4->SmartFan III
+ * 5->enhanced variable thermal cruise (SmartFan IV)
+ */
+ u8 pwm[7][5]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor,
+ * [3]=pwm_max, [4]=pwm_step,
+ * [5]=weight_duty_step, [6]=weight_duty_base
+ */
+
+ u8 target_temp[5];
+ u8 target_temp_mask;
+ u32 target_speed[5];
+ u32 target_speed_tolerance[5];
+ u8 speed_tolerance_limit;
+
+ u8 temp_tolerance[2][5];
+ u8 tolerance_mask;
+
+ u8 fan_time[3][5]; /* 0 = stop_time, 1 = step_up, 2 = step_down */
+
+ /* Automatic fan speed control registers */
+ int auto_pwm_num;
+ u8 auto_pwm[5][7];
+ u8 auto_temp[5][7];
+ u8 pwm_temp_sel[5];
+ u8 pwm_weight_temp_sel[5];
+ u8 weight_temp[3][5]; /* 0->temp_step, 1->temp_step_tol,
+ * 2->temp_base
+ */
+
+ u8 vid;
+ u8 vrm;
+
+ u16 have_temp;
+ u16 have_temp_fixed;
+ u16 have_in;
+#ifdef CONFIG_PM
+ /* Remember extra register values over suspend/resume */
+ u8 vbat;
+ u8 fandiv1;
+ u8 fandiv2;
+#endif
+};
+
+struct nct6775_sio_data {
+ int sioreg;
+ enum kinds kind;
+};
+
+static bool is_word_sized(struct nct6775_data *data, u16 reg)
+{
+ switch (data->kind) {
+ case nct6775:
+ return (((reg & 0xff00) == 0x100 ||
+ (reg & 0xff00) == 0x200) &&
+ ((reg & 0x00ff) == 0x50 ||
+ (reg & 0x00ff) == 0x53 ||
+ (reg & 0x00ff) == 0x55)) ||
+ (reg & 0xfff0) == 0x630 ||
+ reg == 0x640 || reg == 0x642 ||
+ reg == 0x662 ||
+ ((reg & 0xfff0) == 0x650 && (reg & 0x000f) >= 0x06) ||
+ reg == 0x73 || reg == 0x75 || reg == 0x77;
+ case nct6776:
+ return (((reg & 0xff00) == 0x100 ||
+ (reg & 0xff00) == 0x200) &&
+ ((reg & 0x00ff) == 0x50 ||
+ (reg & 0x00ff) == 0x53 ||
+ (reg & 0x00ff) == 0x55)) ||
+ (reg & 0xfff0) == 0x630 ||
+ reg == 0x402 ||
+ reg == 0x640 || reg == 0x642 ||
+ ((reg & 0xfff0) == 0x650 && (reg & 0x000f) >= 0x06) ||
+ reg == 0x73 || reg == 0x75 || reg == 0x77;
+ case nct6779:
+ return reg == 0x150 || reg == 0x153 || reg == 0x155 ||
+ ((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x09) ||
+ reg == 0x402 ||
+ reg == 0x63a || reg == 0x63c || reg == 0x63e ||
+ reg == 0x640 || reg == 0x642 ||
+ reg == 0x73 || reg == 0x75 || reg == 0x77 || reg == 0x79 ||
+ reg == 0x7b;
+ }
+ return false;
+}
+
+/*
+ * On older chips, only registers 0x50-0x5f are banked.
+ * On more recent chips, all registers are banked.
+ * Assume that is the case and set the bank number for each access.
+ * Cache the bank number so it only needs to be set if it changes.
+ */
+static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg)
+{
+ u8 bank = reg >> 8;
+ if (data->bank != bank) {
+ outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET);
+ outb_p(bank, data->addr + DATA_REG_OFFSET);
+ data->bank = bank;
+ }
+}
+
+static u16 nct6775_read_value(struct nct6775_data *data, u16 reg)
+{
+ int res, word_sized = is_word_sized(data, reg);
+
+ nct6775_set_bank(data, reg);
+ outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET);
+ res = inb_p(data->addr + DATA_REG_OFFSET);
+ if (word_sized) {
+ outb_p((reg & 0xff) + 1,
+ data->addr + ADDR_REG_OFFSET);
+ res = (res << 8) + inb_p(data->addr + DATA_REG_OFFSET);
+ }
+ return res;
+}
+
+static int nct6775_write_value(struct nct6775_data *data, u16 reg, u16 value)
+{
+ int word_sized = is_word_sized(data, reg);
+
+ nct6775_set_bank(data, reg);
+ outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET);
+ if (word_sized) {
+ outb_p(value >> 8, data->addr + DATA_REG_OFFSET);
+ outb_p((reg & 0xff) + 1,
+ data->addr + ADDR_REG_OFFSET);
+ }
+ outb_p(value & 0xff, data->addr + DATA_REG_OFFSET);
+ return 0;
+}
+
+/* We left-align 8-bit temperature values to make the code simpler */
+static u16 nct6775_read_temp(struct nct6775_data *data, u16 reg)
+{
+ u16 res;
+
+ res = nct6775_read_value(data, reg);
+ if (!is_word_sized(data, reg))
+ res <<= 8;
+
+ return res;
+}
+
+static int nct6775_write_temp(struct nct6775_data *data, u16 reg, u16 value)
+{
+ if (!is_word_sized(data, reg))
+ value >>= 8;
+ return nct6775_write_value(data, reg, value);
+}
+
+/* This function assumes that the caller holds data->update_lock */
+static void nct6775_write_fan_div(struct nct6775_data *data, int nr)
+{
+ u8 reg;
+
+ switch (nr) {
+ case 0:
+ reg = (nct6775_read_value(data, NCT6775_REG_FANDIV1) & 0x70)
+ | (data->fan_div[0] & 0x7);
+ nct6775_write_value(data, NCT6775_REG_FANDIV1, reg);
+ break;
+ case 1:
+ reg = (nct6775_read_value(data, NCT6775_REG_FANDIV1) & 0x7)
+ | ((data->fan_div[1] << 4) & 0x70);
+ nct6775_write_value(data, NCT6775_REG_FANDIV1, reg);
+ break;
+ case 2:
+ reg = (nct6775_read_value(data, NCT6775_REG_FANDIV2) & 0x70)
+ | (data->fan_div[2] & 0x7);
+ nct6775_write_value(data, NCT6775_REG_FANDIV2, reg);
+ break;
+ case 3:
+ reg = (nct6775_read_value(data, NCT6775_REG_FANDIV2) & 0x7)
+ | ((data->fan_div[3] << 4) & 0x70);
+ nct6775_write_value(data, NCT6775_REG_FANDIV2, reg);
+ break;
+ }
+}
+
+static void nct6775_write_fan_div_common(struct nct6775_data *data, int nr)
+{
+ if (data->kind == nct6775)
+ nct6775_write_fan_div(data, nr);
+}
+
+static void nct6775_update_fan_div(struct nct6775_data *data)
+{
+ u8 i;
+
+ i = nct6775_read_value(data, NCT6775_REG_FANDIV1);
+ data->fan_div[0] = i & 0x7;
+ data->fan_div[1] = (i & 0x70) >> 4;
+ i = nct6775_read_value(data, NCT6775_REG_FANDIV2);
+ data->fan_div[2] = i & 0x7;
+ if (data->has_fan & (1 << 3))
+ data->fan_div[3] = (i & 0x70) >> 4;
+}
+
+static void nct6775_update_fan_div_common(struct nct6775_data *data)
+{
+ if (data->kind == nct6775)
+ nct6775_update_fan_div(data);
+}
+
+static void nct6775_init_fan_div(struct nct6775_data *data)
+{
+ int i;
+
+ nct6775_update_fan_div_common(data);
+ /*
+ * For all fans, start with highest divider value if the divider
+ * register is not initialized. This ensures that we get a
+ * reading from the fan count register, even if it is not optimal.
+ * We'll compute a better divider later on.
+ */
+ for (i = 0; i < ARRAY_SIZE(data->fan_div); i++) {
+ if (!(data->has_fan & (1 << i)))
+ continue;
+ if (data->fan_div[i] == 0) {
+ data->fan_div[i] = 7;
+ nct6775_write_fan_div_common(data, i);
+ }
+ }
+}
+
+static void nct6775_init_fan_common(struct device *dev,
+ struct nct6775_data *data)
+{
+ int i;
+ u8 reg;
+
+ if (data->has_fan_div)
+ nct6775_init_fan_div(data);
+
+ /*
+ * If fan_min is not set (0), set it to 0xff to disable it. This
+ * prevents the unnecessary warning when fanX_min is reported as 0.
+ */
+ for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) {
+ if (data->has_fan_min & (1 << i)) {
+ reg = nct6775_read_value(data, data->REG_FAN_MIN[i]);
+ if (!reg)
+ nct6775_write_value(data, data->REG_FAN_MIN[i],
+ data->has_fan_div ? 0xff
+ : 0xff1f);
+ }
+ }
+}
+
+static void nct6775_select_fan_div(struct device *dev,
+ struct nct6775_data *data, int nr, u16 reg)
+{
+ u8 fan_div = data->fan_div[nr];
+ u16 fan_min;
+
+ if (!data->has_fan_div)
+ return;
+
+ /*
+ * If we failed to measure the fan speed, or the reported value is not
+ * in the optimal range, and the clock divider can be modified,
+ * let's try that for next time.
+ */
+ if (reg == 0x00 && fan_div < 0x07)
+ fan_div++;
+ else if (reg != 0x00 && reg < 0x30 && fan_div > 0)
+ fan_div--;
+
+ if (fan_div != data->fan_div[nr]) {
+ dev_dbg(dev, "Modifying fan%d clock divider from %u to %u\n",
+ nr + 1, div_from_reg(data->fan_div[nr]),
+ div_from_reg(fan_div));
+
+ /* Preserve min limit if possible */
+ if (data->has_fan_min & (1 << nr)) {
+ fan_min = data->fan_min[nr];
+ if (fan_div > data->fan_div[nr]) {
+ if (fan_min != 255 && fan_min > 1)
+ fan_min >>= 1;
+ } else {
+ if (fan_min != 255) {
+ fan_min <<= 1;
+ if (fan_min > 254)
+ fan_min = 254;
+ }
+ }
+ if (fan_min != data->fan_min[nr]) {
+ data->fan_min[nr] = fan_min;
+ nct6775_write_value(data, data->REG_FAN_MIN[nr],
+ fan_min);
+ }
+ }
+ data->fan_div[nr] = fan_div;
+ nct6775_write_fan_div_common(data, nr);
+ }
+}
+
+static void nct6775_update_pwm(struct device *dev)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ int i, j;
+ int fanmodecfg, reg;
+ bool duty_is_dc;
+
+ for (i = 0; i < data->pwm_num; i++) {
+ if (!(data->has_pwm & (1 << i)))
+ continue;
+
+ duty_is_dc = data->REG_PWM_MODE[i] &&
+ (nct6775_read_value(data, data->REG_PWM_MODE[i])
+ & data->PWM_MODE_MASK[i]);
+ data->pwm_mode[i] = duty_is_dc;
+
+ fanmodecfg = nct6775_read_value(data, data->REG_FAN_MODE[i]);
+ for (j = 0; j < ARRAY_SIZE(data->REG_PWM); j++) {
+ if (data->REG_PWM[j] && data->REG_PWM[j][i]) {
+ data->pwm[j][i]
+ = nct6775_read_value(data,
+ data->REG_PWM[j][i]);
+ }
+ }
+
+ data->pwm_enable[i] = reg_to_pwm_enable(data->pwm[0][i],
+ (fanmodecfg >> 4) & 7);
+
+ if (!data->temp_tolerance[0][i] ||
+ data->pwm_enable[i] != speed_cruise)
+ data->temp_tolerance[0][i] = fanmodecfg & 0x0f;
+ if (!data->target_speed_tolerance[i] ||
+ data->pwm_enable[i] == speed_cruise) {
+ u8 t = fanmodecfg & 0x0f;
+ if (data->REG_TOLERANCE_H) {
+ t |= (nct6775_read_value(data,
+ data->REG_TOLERANCE_H[i]) & 0x70) >> 1;
+ }
+ data->target_speed_tolerance[i] = t;
+ }
+
+ data->temp_tolerance[1][i] =
+ nct6775_read_value(data,
+ data->REG_CRITICAL_TEMP_TOLERANCE[i]);
+
+ reg = nct6775_read_value(data, data->REG_TEMP_SEL[i]);
+ data->pwm_temp_sel[i] = reg & 0x1f;
+ /* If fan can stop, report floor as 0 */
+ if (reg & 0x80)
+ data->pwm[2][i] = 0;
+
+ reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[i]);
+ data->pwm_weight_temp_sel[i] = reg & 0x1f;
+ /* If weight is disabled, report weight source as 0 */
+ if (j == 1 && !(reg & 0x80))
+ data->pwm_weight_temp_sel[i] = 0;
+
+ /* Weight temp data */
+ for (j = 0; j < ARRAY_SIZE(data->weight_temp); j++) {
+ data->weight_temp[j][i]
+ = nct6775_read_value(data,
+ data->REG_WEIGHT_TEMP[j][i]);
+ }
+ }
+}
+
+static void nct6775_update_pwm_limits(struct device *dev)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ int i, j;
+ u8 reg;
+ u16 reg_t;
+
+ for (i = 0; i < data->pwm_num; i++) {
+ if (!(data->has_pwm & (1 << i)))
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(data->fan_time); j++) {
+ data->fan_time[j][i] =
+ nct6775_read_value(data, data->REG_FAN_TIME[j][i]);
+ }
+
+ reg_t = nct6775_read_value(data, data->REG_TARGET[i]);
+ /* Update only in matching mode or if never updated */
+ if (!data->target_temp[i] ||
+ data->pwm_enable[i] == thermal_cruise)
+ data->target_temp[i] = reg_t & data->target_temp_mask;
+ if (!data->target_speed[i] ||
+ data->pwm_enable[i] == speed_cruise) {
+ if (data->REG_TOLERANCE_H) {
+ reg_t |= (nct6775_read_value(data,
+ data->REG_TOLERANCE_H[i]) & 0x0f) << 8;
+ }
+ data->target_speed[i] = reg_t;
+ }
+
+ for (j = 0; j < data->auto_pwm_num; j++) {
+ data->auto_pwm[i][j] =
+ nct6775_read_value(data,
+ NCT6775_AUTO_PWM(data, i, j));
+ data->auto_temp[i][j] =
+ nct6775_read_value(data,
+ NCT6775_AUTO_TEMP(data, i, j));
+ }
+
+ /* critical auto_pwm temperature data */
+ data->auto_temp[i][data->auto_pwm_num] =
+ nct6775_read_value(data, data->REG_CRITICAL_TEMP[i]);
+
+ switch (data->kind) {
+ case nct6775:
+ reg = nct6775_read_value(data,
+ NCT6775_REG_CRITICAL_ENAB[i]);
+ data->auto_pwm[i][data->auto_pwm_num] =
+ (reg & 0x02) ? 0xff : 0x00;
+ break;
+ case nct6776:
+ data->auto_pwm[i][data->auto_pwm_num] = 0xff;
+ break;
+ case nct6779:
+ reg = nct6775_read_value(data,
+ NCT6779_REG_CRITICAL_PWM_ENABLE[i]);
+ if (reg & 1)
+ data->auto_pwm[i][data->auto_pwm_num] =
+ nct6775_read_value(data,
+ NCT6779_REG_CRITICAL_PWM[i]);
+ else
+ data->auto_pwm[i][data->auto_pwm_num] = 0xff;
+ break;
+ }
+ }
+}
+
+static struct nct6775_data *nct6775_update_device(struct device *dev)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ int i, j;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+ || !data->valid) {
+ /* Fan clock dividers */
+ nct6775_update_fan_div_common(data);
+
+ /* Measured voltages and limits */
+ for (i = 0; i < data->in_num; i++) {
+ if (!(data->have_in & (1 << i)))
+ continue;
+
+ data->in[i][0] = nct6775_read_value(data,
+ data->REG_VIN[i]);
+ data->in[i][1] = nct6775_read_value(data,
+ data->REG_IN_MINMAX[0][i]);
+ data->in[i][2] = nct6775_read_value(data,
+ data->REG_IN_MINMAX[1][i]);
+ }
+
+ /* Measured fan speeds and limits */
+ for (i = 0; i < ARRAY_SIZE(data->rpm); i++) {
+ u16 reg;
+
+ if (!(data->has_fan & (1 << i)))
+ continue;
+
+ reg = nct6775_read_value(data, data->REG_FAN[i]);
+ data->rpm[i] = data->fan_from_reg(reg,
+ data->fan_div[i]);
+
+ if (data->has_fan_min & (1 << i))
+ data->fan_min[i] = nct6775_read_value(data,
+ data->REG_FAN_MIN[i]);
+ data->fan_pulses[i] =
+ nct6775_read_value(data, data->REG_FAN_PULSES[i]);
+
+ nct6775_select_fan_div(dev, data, i, reg);
+ }
+
+ nct6775_update_pwm(dev);
+ nct6775_update_pwm_limits(dev);
+
+ /* Measured temperatures and limits */
+ for (i = 0; i < NUM_TEMP; i++) {
+ if (!(data->have_temp & (1 << i)))
+ continue;
+ for (j = 0; j < ARRAY_SIZE(data->reg_temp); j++) {
+ if (data->reg_temp[j][i])
+ data->temp[j][i]
+ = nct6775_read_temp(data,
+ data->reg_temp[j][i]);
+ }
+ if (!(data->have_temp_fixed & (1 << i)))
+ continue;
+ data->temp_offset[i]
+ = nct6775_read_value(data, data->REG_TEMP_OFFSET[i]);
+ }
+
+ data->alarms = 0;
+ for (i = 0; i < NUM_REG_ALARM; i++) {
+ u8 alarm;
+ if (!data->REG_ALARM[i])
+ continue;
+ alarm = nct6775_read_value(data, data->REG_ALARM[i]);
+ data->alarms |= ((u64)alarm) << (i << 3);
+ }
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->update_lock);
+ return data;
+}
+
+/*
+ * Sysfs callback functions
+ */
+static ssize_t
+show_in_reg(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int index = sattr->index;
+ return sprintf(buf, "%ld\n", in_from_reg(data->in[nr][index], nr));
+}
+
+static ssize_t
+store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int index = sattr->index;
+ unsigned long val;
+ int err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+ mutex_lock(&data->update_lock);
+ data->in[nr][index] = in_to_reg(val, nr);
+ nct6775_write_value(data, data->REG_IN_MINMAX[index - 1][nr],
+ data->in[nr][index]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+show_alarm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = data->ALARM_BITS[sattr->index];
+ return sprintf(buf, "%u\n",
+ (unsigned int)((data->alarms >> nr) & 0x01));
+}
+
+static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in_reg, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in_reg, NULL, 1, 0);
+static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in_reg, NULL, 2, 0);
+static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in_reg, NULL, 3, 0);
+static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in_reg, NULL, 4, 0);
+static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, show_in_reg, NULL, 5, 0);
+static SENSOR_DEVICE_ATTR_2(in6_input, S_IRUGO, show_in_reg, NULL, 6, 0);
+static SENSOR_DEVICE_ATTR_2(in7_input, S_IRUGO, show_in_reg, NULL, 7, 0);
+static SENSOR_DEVICE_ATTR_2(in8_input, S_IRUGO, show_in_reg, NULL, 8, 0);
+static SENSOR_DEVICE_ATTR_2(in9_input, S_IRUGO, show_in_reg, NULL, 9, 0);
+static SENSOR_DEVICE_ATTR_2(in10_input, S_IRUGO, show_in_reg, NULL, 10, 0);
+static SENSOR_DEVICE_ATTR_2(in11_input, S_IRUGO, show_in_reg, NULL, 11, 0);
+static SENSOR_DEVICE_ATTR_2(in12_input, S_IRUGO, show_in_reg, NULL, 12, 0);
+static SENSOR_DEVICE_ATTR_2(in13_input, S_IRUGO, show_in_reg, NULL, 13, 0);
+static SENSOR_DEVICE_ATTR_2(in14_input, S_IRUGO, show_in_reg, NULL, 14, 0);
+
+static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 5);
+static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 7);
+static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 8);
+static SENSOR_DEVICE_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, 9);
+static SENSOR_DEVICE_ATTR(in10_alarm, S_IRUGO, show_alarm, NULL, 10);
+static SENSOR_DEVICE_ATTR(in11_alarm, S_IRUGO, show_alarm, NULL, 11);
+static SENSOR_DEVICE_ATTR(in12_alarm, S_IRUGO, show_alarm, NULL, 12);
+static SENSOR_DEVICE_ATTR(in13_alarm, S_IRUGO, show_alarm, NULL, 13);
+static SENSOR_DEVICE_ATTR(in14_alarm, S_IRUGO, show_alarm, NULL, 14);
+
+static SENSOR_DEVICE_ATTR_2(in0_min, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 0, 1);
+static SENSOR_DEVICE_ATTR_2(in1_min, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 1, 1);
+static SENSOR_DEVICE_ATTR_2(in2_min, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 2, 1);
+static SENSOR_DEVICE_ATTR_2(in3_min, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 3, 1);
+static SENSOR_DEVICE_ATTR_2(in4_min, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 4, 1);
+static SENSOR_DEVICE_ATTR_2(in5_min, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 5, 1);
+static SENSOR_DEVICE_ATTR_2(in6_min, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 6, 1);
+static SENSOR_DEVICE_ATTR_2(in7_min, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 7, 1);
+static SENSOR_DEVICE_ATTR_2(in8_min, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 8, 1);
+static SENSOR_DEVICE_ATTR_2(in9_min, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 9, 1);
+static SENSOR_DEVICE_ATTR_2(in10_min, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 10, 1);
+static SENSOR_DEVICE_ATTR_2(in11_min, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 11, 1);
+static SENSOR_DEVICE_ATTR_2(in12_min, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 12, 1);
+static SENSOR_DEVICE_ATTR_2(in13_min, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 13, 1);
+static SENSOR_DEVICE_ATTR_2(in14_min, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 14, 1);
+
+static SENSOR_DEVICE_ATTR_2(in0_max, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 0, 2);
+static SENSOR_DEVICE_ATTR_2(in1_max, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 1, 2);
+static SENSOR_DEVICE_ATTR_2(in2_max, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 2, 2);
+static SENSOR_DEVICE_ATTR_2(in3_max, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 3, 2);
+static SENSOR_DEVICE_ATTR_2(in4_max, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 4, 2);
+static SENSOR_DEVICE_ATTR_2(in5_max, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 5, 2);
+static SENSOR_DEVICE_ATTR_2(in6_max, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 6, 2);
+static SENSOR_DEVICE_ATTR_2(in7_max, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 7, 2);
+static SENSOR_DEVICE_ATTR_2(in8_max, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 8, 2);
+static SENSOR_DEVICE_ATTR_2(in9_max, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 9, 2);
+static SENSOR_DEVICE_ATTR_2(in10_max, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 10, 2);
+static SENSOR_DEVICE_ATTR_2(in11_max, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 11, 2);
+static SENSOR_DEVICE_ATTR_2(in12_max, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 12, 2);
+static SENSOR_DEVICE_ATTR_2(in13_max, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 13, 2);
+static SENSOR_DEVICE_ATTR_2(in14_max, S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 14, 2);
+
+static struct attribute *nct6775_attributes_in[15][5] = {
+ {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_alarm.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in8_min.dev_attr.attr,
+ &sensor_dev_attr_in8_max.dev_attr.attr,
+ &sensor_dev_attr_in8_alarm.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_in9_input.dev_attr.attr,
+ &sensor_dev_attr_in9_min.dev_attr.attr,
+ &sensor_dev_attr_in9_max.dev_attr.attr,
+ &sensor_dev_attr_in9_alarm.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_in10_input.dev_attr.attr,
+ &sensor_dev_attr_in10_min.dev_attr.attr,
+ &sensor_dev_attr_in10_max.dev_attr.attr,
+ &sensor_dev_attr_in10_alarm.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_in11_input.dev_attr.attr,
+ &sensor_dev_attr_in11_min.dev_attr.attr,
+ &sensor_dev_attr_in11_max.dev_attr.attr,
+ &sensor_dev_attr_in11_alarm.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_in12_input.dev_attr.attr,
+ &sensor_dev_attr_in12_min.dev_attr.attr,
+ &sensor_dev_attr_in12_max.dev_attr.attr,
+ &sensor_dev_attr_in12_alarm.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_in13_input.dev_attr.attr,
+ &sensor_dev_attr_in13_min.dev_attr.attr,
+ &sensor_dev_attr_in13_max.dev_attr.attr,
+ &sensor_dev_attr_in13_alarm.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_in14_input.dev_attr.attr,
+ &sensor_dev_attr_in14_min.dev_attr.attr,
+ &sensor_dev_attr_in14_max.dev_attr.attr,
+ &sensor_dev_attr_in14_alarm.dev_attr.attr,
+ NULL
+ },
+};
+
+static const struct attribute_group nct6775_group_in[15] = {
+ { .attrs = nct6775_attributes_in[0] },
+ { .attrs = nct6775_attributes_in[1] },
+ { .attrs = nct6775_attributes_in[2] },
+ { .attrs = nct6775_attributes_in[3] },
+ { .attrs = nct6775_attributes_in[4] },
+ { .attrs = nct6775_attributes_in[5] },
+ { .attrs = nct6775_attributes_in[6] },
+ { .attrs = nct6775_attributes_in[7] },
+ { .attrs = nct6775_attributes_in[8] },
+ { .attrs = nct6775_attributes_in[9] },
+ { .attrs = nct6775_attributes_in[10] },
+ { .attrs = nct6775_attributes_in[11] },
+ { .attrs = nct6775_attributes_in[12] },
+ { .attrs = nct6775_attributes_in[13] },
+ { .attrs = nct6775_attributes_in[14] },
+};
+
+static ssize_t
+show_fan(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ return sprintf(buf, "%d\n", data->rpm[nr]);
+}
+
+static ssize_t
+show_fan_min(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ return sprintf(buf, "%d\n",
+ data->fan_from_reg_min(data->fan_min[nr],
+ data->fan_div[nr]));
+}
+
+static ssize_t
+show_fan_div(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr]));
+}
+
+static ssize_t
+store_fan_min(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ int err;
+ unsigned int reg;
+ u8 new_div;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ if (!data->has_fan_div) {
+ /* NCT6776F or NCT6779D; we know this is a 13 bit register */
+ if (!val) {
+ val = 0xff1f;
+ } else {
+ if (val > 1350000U)
+ val = 135000U;
+ val = 1350000U / val;
+ val = (val & 0x1f) | ((val << 3) & 0xff00);
+ }
+ data->fan_min[nr] = val;
+ goto write_min; /* Leave fan divider alone */
+ }
+ if (!val) {
+ /* No min limit, alarm disabled */
+ data->fan_min[nr] = 255;
+ new_div = data->fan_div[nr]; /* No change */
+ dev_info(dev, "fan%u low limit and alarm disabled\n", nr + 1);
+ goto write_div;
+ }
+ reg = 1350000U / val;
+ if (reg >= 128 * 255) {
+ /*
+ * Speed below this value cannot possibly be represented,
+ * even with the highest divider (128)
+ */
+ data->fan_min[nr] = 254;
+ new_div = 7; /* 128 == (1 << 7) */
+ dev_warn(dev,
+ "fan%u low limit %lu below minimum %u, set to minimum\n",
+ nr + 1, val, data->fan_from_reg_min(254, 7));
+ } else if (!reg) {
+ /*
+ * Speed above this value cannot possibly be represented,
+ * even with the lowest divider (1)
+ */
+ data->fan_min[nr] = 1;
+ new_div = 0; /* 1 == (1 << 0) */
+ dev_warn(dev,
+ "fan%u low limit %lu above maximum %u, set to maximum\n",
+ nr + 1, val, data->fan_from_reg_min(1, 0));
+ } else {
+ /*
+ * Automatically pick the best divider, i.e. the one such
+ * that the min limit will correspond to a register value
+ * in the 96..192 range
+ */
+ new_div = 0;
+ while (reg > 192 && new_div < 7) {
+ reg >>= 1;
+ new_div++;
+ }
+ data->fan_min[nr] = reg;
+ }
+
+write_div:
+ /*
+ * Write both the fan clock divider (if it changed) and the new
+ * fan min (unconditionally)
+ */
+ if (new_div != data->fan_div[nr]) {
+ dev_dbg(dev, "fan%u clock divider changed from %u to %u\n",
+ nr + 1, div_from_reg(data->fan_div[nr]),
+ div_from_reg(new_div));
+ data->fan_div[nr] = new_div;
+ nct6775_write_fan_div_common(data, nr);
+ /* Give the chip time to sample a new speed value */
+ data->last_updated = jiffies;
+ }
+
+write_min:
+ nct6775_write_value(data, data->REG_FAN_MIN[nr], data->fan_min[nr]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t
+show_fan_pulses(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int p = data->fan_pulses[sattr->index];
+
+ return sprintf(buf, "%d\n", p ? : 4);
+}
+
+static ssize_t
+store_fan_pulses(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ if (val > 4)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->fan_pulses[nr] = val & 3;
+ nct6775_write_value(data, data->REG_FAN_PULSES[nr], val & 3);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static struct sensor_device_attribute sda_fan_input[] = {
+ SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0),
+ SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1),
+ SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2),
+ SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3),
+ SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4),
+};
+
+static struct sensor_device_attribute sda_fan_alarm[] = {
+ SENSOR_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE),
+ SENSOR_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 1),
+ SENSOR_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 2),
+ SENSOR_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 3),
+ SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 4),
+};
+
+static struct sensor_device_attribute sda_fan_min[] = {
+ SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
+ store_fan_min, 0),
+ SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min,
+ store_fan_min, 1),
+ SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min,
+ store_fan_min, 2),
+ SENSOR_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min,
+ store_fan_min, 3),
+ SENSOR_ATTR(fan5_min, S_IWUSR | S_IRUGO, show_fan_min,
+ store_fan_min, 4),
+};
+
+static struct sensor_device_attribute sda_fan_pulses[] = {
+ SENSOR_ATTR(fan1_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
+ store_fan_pulses, 0),
+ SENSOR_ATTR(fan2_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
+ store_fan_pulses, 1),
+ SENSOR_ATTR(fan3_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
+ store_fan_pulses, 2),
+ SENSOR_ATTR(fan4_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
+ store_fan_pulses, 3),
+ SENSOR_ATTR(fan5_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
+ store_fan_pulses, 4),
+};
+
+static struct sensor_device_attribute sda_fan_div[] = {
+ SENSOR_ATTR(fan1_div, S_IRUGO, show_fan_div, NULL, 0),
+ SENSOR_ATTR(fan2_div, S_IRUGO, show_fan_div, NULL, 1),
+ SENSOR_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2),
+ SENSOR_ATTR(fan4_div, S_IRUGO, show_fan_div, NULL, 3),
+ SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4),
+};
+
+static ssize_t
+show_temp_label(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]);
+}
+
+static ssize_t
+show_temp(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int index = sattr->index;
+
+ return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->temp[index][nr]));
+}
+
+static ssize_t
+store_temp(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int index = sattr->index;
+ int err;
+ long val;
+
+ err = kstrtol(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&data->update_lock);
+ data->temp[index][nr] = LM75_TEMP_TO_REG(val);
+ nct6775_write_temp(data, data->reg_temp[index][nr],
+ data->temp[index][nr]);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+show_temp_offset(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+
+ return sprintf(buf, "%d\n", data->temp_offset[sattr->index] * 1000);
+}
+
+static ssize_t
+store_temp_offset(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127);
+
+ mutex_lock(&data->update_lock);
+ data->temp_offset[nr] = val;
+ nct6775_write_value(data, data->REG_TEMP_OFFSET[nr], val);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t
+show_temp_type(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ return sprintf(buf, "%d\n", (int)data->temp_type[nr]);
+}
+
+static ssize_t
+store_temp_type(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ int err;
+ u8 vbat, diode, bit;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ if (val != 1 && val != 3 && val != 4)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+
+ data->temp_type[nr] = val;
+ vbat = nct6775_read_value(data, data->REG_VBAT) & ~(0x02 << nr);
+ diode = nct6775_read_value(data, data->REG_DIODE) & ~(0x02 << nr);
+ bit = 0x02 << nr;
+ switch (val) {
+ case 1: /* CPU diode (diode, current mode) */
+ vbat |= bit;
+ diode |= bit;
+ break;
+ case 3: /* diode, voltage mode */
+ vbat |= bit;
+ break;
+ case 4: /* thermistor */
+ break;
+ }
+ nct6775_write_value(data, data->REG_VBAT, vbat);
+ nct6775_write_value(data, data->REG_DIODE, diode);
+
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static struct sensor_device_attribute_2 sda_temp_input[] = {
+ SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0),
+ SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 1, 0),
+ SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 2, 0),
+ SENSOR_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 3, 0),
+ SENSOR_ATTR_2(temp5_input, S_IRUGO, show_temp, NULL, 4, 0),
+ SENSOR_ATTR_2(temp6_input, S_IRUGO, show_temp, NULL, 5, 0),
+ SENSOR_ATTR_2(temp7_input, S_IRUGO, show_temp, NULL, 6, 0),
+ SENSOR_ATTR_2(temp8_input, S_IRUGO, show_temp, NULL, 7, 0),
+ SENSOR_ATTR_2(temp9_input, S_IRUGO, show_temp, NULL, 8, 0),
+ SENSOR_ATTR_2(temp10_input, S_IRUGO, show_temp, NULL, 9, 0),
+};
+
+static struct sensor_device_attribute sda_temp_label[] = {
+ SENSOR_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL, 0),
+ SENSOR_ATTR(temp2_label, S_IRUGO, show_temp_label, NULL, 1),
+ SENSOR_ATTR(temp3_label, S_IRUGO, show_temp_label, NULL, 2),
+ SENSOR_ATTR(temp4_label, S_IRUGO, show_temp_label, NULL, 3),
+ SENSOR_ATTR(temp5_label, S_IRUGO, show_temp_label, NULL, 4),
+ SENSOR_ATTR(temp6_label, S_IRUGO, show_temp_label, NULL, 5),
+ SENSOR_ATTR(temp7_label, S_IRUGO, show_temp_label, NULL, 6),
+ SENSOR_ATTR(temp8_label, S_IRUGO, show_temp_label, NULL, 7),
+ SENSOR_ATTR(temp9_label, S_IRUGO, show_temp_label, NULL, 8),
+ SENSOR_ATTR(temp10_label, S_IRUGO, show_temp_label, NULL, 9),
+};
+
+static struct sensor_device_attribute_2 sda_temp_max[] = {
+ SENSOR_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 0, 1),
+ SENSOR_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 1, 1),
+ SENSOR_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 2, 1),
+ SENSOR_ATTR_2(temp4_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 3, 1),
+ SENSOR_ATTR_2(temp5_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 4, 1),
+ SENSOR_ATTR_2(temp6_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 5, 1),
+ SENSOR_ATTR_2(temp7_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 6, 1),
+ SENSOR_ATTR_2(temp8_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 7, 1),
+ SENSOR_ATTR_2(temp9_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 8, 1),
+ SENSOR_ATTR_2(temp10_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 9, 1),
+};
+
+static struct sensor_device_attribute_2 sda_temp_max_hyst[] = {
+ SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 0, 2),
+ SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 1, 2),
+ SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 2, 2),
+ SENSOR_ATTR_2(temp4_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 3, 2),
+ SENSOR_ATTR_2(temp5_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 4, 2),
+ SENSOR_ATTR_2(temp6_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 5, 2),
+ SENSOR_ATTR_2(temp7_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 6, 2),
+ SENSOR_ATTR_2(temp8_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 7, 2),
+ SENSOR_ATTR_2(temp9_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 8, 2),
+ SENSOR_ATTR_2(temp10_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 9, 2),
+};
+
+static struct sensor_device_attribute_2 sda_temp_crit[] = {
+ SENSOR_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 0, 3),
+ SENSOR_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 1, 3),
+ SENSOR_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 2, 3),
+ SENSOR_ATTR_2(temp4_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 3, 3),
+ SENSOR_ATTR_2(temp5_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 4, 3),
+ SENSOR_ATTR_2(temp6_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 5, 3),
+ SENSOR_ATTR_2(temp7_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 6, 3),
+ SENSOR_ATTR_2(temp8_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 7, 3),
+ SENSOR_ATTR_2(temp9_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 8, 3),
+ SENSOR_ATTR_2(temp10_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+ 9, 3),
+};
+
+static struct sensor_device_attribute sda_temp_offset[] = {
+ SENSOR_ATTR(temp1_offset, S_IRUGO | S_IWUSR, show_temp_offset,
+ store_temp_offset, 0),
+ SENSOR_ATTR(temp2_offset, S_IRUGO | S_IWUSR, show_temp_offset,
+ store_temp_offset, 1),
+ SENSOR_ATTR(temp3_offset, S_IRUGO | S_IWUSR, show_temp_offset,
+ store_temp_offset, 2),
+ SENSOR_ATTR(temp4_offset, S_IRUGO | S_IWUSR, show_temp_offset,
+ store_temp_offset, 3),
+ SENSOR_ATTR(temp5_offset, S_IRUGO | S_IWUSR, show_temp_offset,
+ store_temp_offset, 4),
+ SENSOR_ATTR(temp6_offset, S_IRUGO | S_IWUSR, show_temp_offset,
+ store_temp_offset, 5),
+};
+
+static struct sensor_device_attribute sda_temp_type[] = {
+ SENSOR_ATTR(temp1_type, S_IRUGO | S_IWUSR, show_temp_type,
+ store_temp_type, 0),
+ SENSOR_ATTR(temp2_type, S_IRUGO | S_IWUSR, show_temp_type,
+ store_temp_type, 1),
+ SENSOR_ATTR(temp3_type, S_IRUGO | S_IWUSR, show_temp_type,
+ store_temp_type, 2),
+ SENSOR_ATTR(temp4_type, S_IRUGO | S_IWUSR, show_temp_type,
+ store_temp_type, 3),
+ SENSOR_ATTR(temp5_type, S_IRUGO | S_IWUSR, show_temp_type,
+ store_temp_type, 4),
+ SENSOR_ATTR(temp6_type, S_IRUGO | S_IWUSR, show_temp_type,
+ store_temp_type, 5),
+};
+
+static struct sensor_device_attribute sda_temp_alarm[] = {
+ SENSOR_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL,
+ TEMP_ALARM_BASE),
+ SENSOR_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL,
+ TEMP_ALARM_BASE + 1),
+ SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL,
+ TEMP_ALARM_BASE + 2),
+ SENSOR_ATTR(temp4_alarm, S_IRUGO, show_alarm, NULL,
+ TEMP_ALARM_BASE + 3),
+ SENSOR_ATTR(temp5_alarm, S_IRUGO, show_alarm, NULL,
+ TEMP_ALARM_BASE + 4),
+ SENSOR_ATTR(temp6_alarm, S_IRUGO, show_alarm, NULL,
+ TEMP_ALARM_BASE + 5),
+};
+
+#define NUM_TEMP_ALARM ARRAY_SIZE(sda_temp_alarm)
+
+static ssize_t
+show_pwm_mode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+
+ return sprintf(buf, "%d\n", !data->pwm_mode[sattr->index]);
+}
+
+static ssize_t
+store_pwm_mode(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ int err;
+ u8 reg;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ if (val > 1)
+ return -EINVAL;
+
+ /* Setting DC mode is not supported for all chips/channels */
+ if (data->REG_PWM_MODE[nr] == 0) {
+ if (val)
+ return -EINVAL;
+ return count;
+ }
+
+ mutex_lock(&data->update_lock);
+ data->pwm_mode[nr] = val;
+ reg = nct6775_read_value(data, data->REG_PWM_MODE[nr]);
+ reg &= ~data->PWM_MODE_MASK[nr];
+ if (val)
+ reg |= data->PWM_MODE_MASK[nr];
+ nct6775_write_value(data, data->REG_PWM_MODE[nr], reg);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+show_pwm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int index = sattr->index;
+ int pwm;
+
+ /*
+ * For automatic fan control modes, show current pwm readings.
+ * Otherwise, show the configured value.
+ */
+ if (index == 0 && data->pwm_enable[nr] > manual)
+ pwm = nct6775_read_value(data, data->REG_PWM_READ[nr]);
+ else
+ pwm = data->pwm[index][nr];
+
+ return sprintf(buf, "%d\n", pwm);
+}
+
+static ssize_t
+store_pwm(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int index = sattr->index;
+ unsigned long val;
+ int minval[7] = { 0, 1, 1, data->pwm[2][nr], 0, 0, 0 };
+ int maxval[7]
+ = { 255, 255, data->pwm[3][nr] ? : 255, 255, 255, 255, 255 };
+ int err;
+ u8 reg;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+ val = clamp_val(val, minval[index], maxval[index]);
+
+ mutex_lock(&data->update_lock);
+ data->pwm[index][nr] = val;
+ nct6775_write_value(data, data->REG_PWM[index][nr], val);
+ if (index == 2) { /* floor: disable if val == 0 */
+ reg = nct6775_read_value(data, data->REG_TEMP_SEL[nr]);
+ reg &= 0x7f;
+ if (val)
+ reg |= 0x80;
+ nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg);
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/* Returns 0 if OK, -EINVAL otherwise */
+static int check_trip_points(struct nct6775_data *data, int nr)
+{
+ int i;
+
+ for (i = 0; i < data->auto_pwm_num - 1; i++) {
+ if (data->auto_temp[nr][i] > data->auto_temp[nr][i + 1])
+ return -EINVAL;
+ }
+ for (i = 0; i < data->auto_pwm_num - 1; i++) {
+ if (data->auto_pwm[nr][i] > data->auto_pwm[nr][i + 1])
+ return -EINVAL;
+ }
+ /* validate critical temperature and pwm if enabled (pwm > 0) */
+ if (data->auto_pwm[nr][data->auto_pwm_num]) {
+ if (data->auto_temp[nr][data->auto_pwm_num - 1] >
+ data->auto_temp[nr][data->auto_pwm_num] ||
+ data->auto_pwm[nr][data->auto_pwm_num - 1] >
+ data->auto_pwm[nr][data->auto_pwm_num])
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void pwm_update_registers(struct nct6775_data *data, int nr)
+{
+ u8 reg;
+
+ switch (data->pwm_enable[nr]) {
+ case off:
+ case manual:
+ break;
+ case speed_cruise:
+ reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]);
+ reg = (reg & ~data->tolerance_mask) |
+ (data->target_speed_tolerance[nr] & data->tolerance_mask);
+ nct6775_write_value(data, data->REG_FAN_MODE[nr], reg);
+ nct6775_write_value(data, data->REG_TARGET[nr],
+ data->target_speed[nr] & 0xff);
+ if (data->REG_TOLERANCE_H) {
+ reg = (data->target_speed[nr] >> 8) & 0x0f;
+ reg |= (data->target_speed_tolerance[nr] & 0x38) << 1;
+ nct6775_write_value(data,
+ data->REG_TOLERANCE_H[nr],
+ reg);
+ }
+ break;
+ case thermal_cruise:
+ nct6775_write_value(data, data->REG_TARGET[nr],
+ data->target_temp[nr]);
+ /* intentional */
+ default:
+ reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]);
+ reg = (reg & ~data->tolerance_mask) |
+ data->temp_tolerance[0][nr];
+ nct6775_write_value(data, data->REG_FAN_MODE[nr], reg);
+ break;
+ }
+}
+
+static ssize_t
+show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+
+ return sprintf(buf, "%d\n", data->pwm_enable[sattr->index]);
+}
+
+static ssize_t
+store_pwm_enable(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ int err;
+ u16 reg;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ if (val > sf4)
+ return -EINVAL;
+
+ if (val == sf3 && data->kind != nct6775)
+ return -EINVAL;
+
+ if (val == sf4 && check_trip_points(data, nr)) {
+ dev_err(dev, "Inconsistent trip points, not switching to SmartFan IV mode\n");
+ dev_err(dev, "Adjust trip points and try again\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->update_lock);
+ data->pwm_enable[nr] = val;
+ if (val == off) {
+ /*
+ * turn off pwm control: select manual mode, set pwm to maximum
+ */
+ data->pwm[0][nr] = 255;
+ nct6775_write_value(data, data->REG_PWM[0][nr], 255);
+ }
+ pwm_update_registers(data, nr);
+ reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]);
+ reg &= 0x0f;
+ reg |= pwm_enable_to_reg(val) << 4;
+ nct6775_write_value(data, data->REG_FAN_MODE[nr], reg);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+show_pwm_temp_sel_common(struct nct6775_data *data, char *buf, int src)
+{
+ int i, sel = 0;
+
+ for (i = 0; i < NUM_TEMP; i++) {
+ if (!(data->have_temp & (1 << i)))
+ continue;
+ if (src == data->temp_src[i]) {
+ sel = i + 1;
+ break;
+ }
+ }
+
+ return sprintf(buf, "%d\n", sel);
+}
+
+static ssize_t
+show_pwm_temp_sel(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int index = sattr->index;
+
+ return show_pwm_temp_sel_common(data, buf, data->pwm_temp_sel[index]);
+}
+
+static ssize_t
+store_pwm_temp_sel(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ int err, reg, src;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+ if (val == 0 || val > NUM_TEMP)
+ return -EINVAL;
+ if (!(data->have_temp & (1 << (val - 1))) || !data->temp_src[val - 1])
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ src = data->temp_src[val - 1];
+ data->pwm_temp_sel[nr] = src;
+ reg = nct6775_read_value(data, data->REG_TEMP_SEL[nr]);
+ reg &= 0xe0;
+ reg |= src;
+ nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t
+show_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int index = sattr->index;
+
+ return show_pwm_temp_sel_common(data, buf,
+ data->pwm_weight_temp_sel[index]);
+}
+
+static ssize_t
+store_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ int err, reg, src;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+ if (val > NUM_TEMP)
+ return -EINVAL;
+ if (val && (!(data->have_temp & (1 << (val - 1))) ||
+ !data->temp_src[val - 1]))
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ if (val) {
+ src = data->temp_src[val - 1];
+ data->pwm_weight_temp_sel[nr] = src;
+ reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr]);
+ reg &= 0xe0;
+ reg |= (src | 0x80);
+ nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg);
+ } else {
+ data->pwm_weight_temp_sel[nr] = 0;
+ reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr]);
+ reg &= 0x7f;
+ nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg);
+ }
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t
+show_target_temp(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+
+ return sprintf(buf, "%d\n", data->target_temp[sattr->index] * 1000);
+}
+
+static ssize_t
+store_target_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0,
+ data->target_temp_mask);
+
+ mutex_lock(&data->update_lock);
+ data->target_temp[nr] = val;
+ pwm_update_registers(data, nr);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+show_target_speed(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+
+ return sprintf(buf, "%d\n",
+ fan_from_reg16(data->target_speed[nr],
+ data->fan_div[nr]));
+}
+
+static ssize_t
+store_target_speed(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ int err;
+ u16 speed;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = clamp_val(val, 0, 1350000U);
+ speed = fan_to_reg(val, data->fan_div[nr]);
+
+ mutex_lock(&data->update_lock);
+ data->target_speed[nr] = speed;
+ pwm_update_registers(data, nr);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+show_temp_tolerance(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int index = sattr->index;
+
+ return sprintf(buf, "%d\n", data->temp_tolerance[index][nr] * 1000);
+}
+
+static ssize_t
+store_temp_tolerance(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int index = sattr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ /* Limit tolerance as needed */
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, data->tolerance_mask);
+
+ mutex_lock(&data->update_lock);
+ data->temp_tolerance[index][nr] = val;
+ if (index)
+ pwm_update_registers(data, nr);
+ else
+ nct6775_write_value(data,
+ data->REG_CRITICAL_TEMP_TOLERANCE[nr],
+ val);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/*
+ * Fan speed tolerance is a tricky beast, since the associated register is
+ * a tick counter, but the value is reported and configured as rpm.
+ * Compute resulting low and high rpm values and report the difference.
+ */
+static ssize_t
+show_speed_tolerance(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ int low = data->target_speed[nr] - data->target_speed_tolerance[nr];
+ int high = data->target_speed[nr] + data->target_speed_tolerance[nr];
+ int tolerance;
+
+ if (low <= 0)
+ low = 1;
+ if (high > 0xffff)
+ high = 0xffff;
+ if (high < low)
+ high = low;
+
+ tolerance = (fan_from_reg16(low, data->fan_div[nr])
+ - fan_from_reg16(high, data->fan_div[nr])) / 2;
+
+ return sprintf(buf, "%d\n", tolerance);
+}
+
+static ssize_t
+store_speed_tolerance(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ int nr = sattr->index;
+ unsigned long val;
+ int err;
+ int low, high;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ high = fan_from_reg16(data->target_speed[nr],
+ data->fan_div[nr]) + val;
+ low = fan_from_reg16(data->target_speed[nr],
+ data->fan_div[nr]) - val;
+ if (low <= 0)
+ low = 1;
+ if (high < low)
+ high = low;
+
+ val = (fan_to_reg(low, data->fan_div[nr]) -
+ fan_to_reg(high, data->fan_div[nr])) / 2;
+
+ /* Limit tolerance as needed */
+ val = clamp_val(val, 0, data->speed_tolerance_limit);
+
+ mutex_lock(&data->update_lock);
+ data->target_speed_tolerance[nr] = val;
+ pwm_update_registers(data, nr);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR_2(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 0);
+static SENSOR_DEVICE_ATTR_2(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1, 0);
+static SENSOR_DEVICE_ATTR_2(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2, 0);
+static SENSOR_DEVICE_ATTR_2(pwm4, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3, 0);
+static SENSOR_DEVICE_ATTR_2(pwm5, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 4, 0);
+
+static SENSOR_DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+ store_pwm_mode, 0);
+static SENSOR_DEVICE_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+ store_pwm_mode, 1);
+static SENSOR_DEVICE_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+ store_pwm_mode, 2);
+static SENSOR_DEVICE_ATTR(pwm4_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+ store_pwm_mode, 3);
+static SENSOR_DEVICE_ATTR(pwm5_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+ store_pwm_mode, 4);
+
+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+ store_pwm_enable, 0);
+static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+ store_pwm_enable, 1);
+static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+ store_pwm_enable, 2);
+static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+ store_pwm_enable, 3);
+static SENSOR_DEVICE_ATTR(pwm5_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+ store_pwm_enable, 4);
+
+static SENSOR_DEVICE_ATTR(pwm1_temp_sel, S_IWUSR | S_IRUGO,
+ show_pwm_temp_sel, store_pwm_temp_sel, 0);
+static SENSOR_DEVICE_ATTR(pwm2_temp_sel, S_IWUSR | S_IRUGO,
+ show_pwm_temp_sel, store_pwm_temp_sel, 1);
+static SENSOR_DEVICE_ATTR(pwm3_temp_sel, S_IWUSR | S_IRUGO,
+ show_pwm_temp_sel, store_pwm_temp_sel, 2);
+static SENSOR_DEVICE_ATTR(pwm4_temp_sel, S_IWUSR | S_IRUGO,
+ show_pwm_temp_sel, store_pwm_temp_sel, 3);
+static SENSOR_DEVICE_ATTR(pwm5_temp_sel, S_IWUSR | S_IRUGO,
+ show_pwm_temp_sel, store_pwm_temp_sel, 4);
+
+static SENSOR_DEVICE_ATTR(pwm1_target_temp, S_IWUSR | S_IRUGO, show_target_temp,
+ store_target_temp, 0);
+static SENSOR_DEVICE_ATTR(pwm2_target_temp, S_IWUSR | S_IRUGO, show_target_temp,
+ store_target_temp, 1);
+static SENSOR_DEVICE_ATTR(pwm3_target_temp, S_IWUSR | S_IRUGO, show_target_temp,
+ store_target_temp, 2);
+static SENSOR_DEVICE_ATTR(pwm4_target_temp, S_IWUSR | S_IRUGO, show_target_temp,
+ store_target_temp, 3);
+static SENSOR_DEVICE_ATTR(pwm5_target_temp, S_IWUSR | S_IRUGO, show_target_temp,
+ store_target_temp, 4);
+
+static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, show_target_speed,
+ store_target_speed, 0);
+static SENSOR_DEVICE_ATTR(fan2_target, S_IWUSR | S_IRUGO, show_target_speed,
+ store_target_speed, 1);
+static SENSOR_DEVICE_ATTR(fan3_target, S_IWUSR | S_IRUGO, show_target_speed,
+ store_target_speed, 2);
+static SENSOR_DEVICE_ATTR(fan4_target, S_IWUSR | S_IRUGO, show_target_speed,
+ store_target_speed, 3);
+static SENSOR_DEVICE_ATTR(fan5_target, S_IWUSR | S_IRUGO, show_target_speed,
+ store_target_speed, 4);
+
+static SENSOR_DEVICE_ATTR(fan1_tolerance, S_IWUSR | S_IRUGO,
+ show_speed_tolerance, store_speed_tolerance, 0);
+static SENSOR_DEVICE_ATTR(fan2_tolerance, S_IWUSR | S_IRUGO,
+ show_speed_tolerance, store_speed_tolerance, 1);
+static SENSOR_DEVICE_ATTR(fan3_tolerance, S_IWUSR | S_IRUGO,
+ show_speed_tolerance, store_speed_tolerance, 2);
+static SENSOR_DEVICE_ATTR(fan4_tolerance, S_IWUSR | S_IRUGO,
+ show_speed_tolerance, store_speed_tolerance, 3);
+static SENSOR_DEVICE_ATTR(fan5_tolerance, S_IWUSR | S_IRUGO,
+ show_speed_tolerance, store_speed_tolerance, 4);
+
+/* Smart Fan registers */
+
+static ssize_t
+show_weight_temp(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int index = sattr->index;
+
+ return sprintf(buf, "%d\n", data->weight_temp[index][nr] * 1000);
+}
+
+static ssize_t
+store_weight_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int index = sattr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 255);
+
+ mutex_lock(&data->update_lock);
+ data->weight_temp[index][nr] = val;
+ nct6775_write_value(data, data->REG_WEIGHT_TEMP[index][nr], val);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(pwm1_weight_temp_sel, S_IWUSR | S_IRUGO,
+ show_pwm_weight_temp_sel, store_pwm_weight_temp_sel,
+ 0);
+static SENSOR_DEVICE_ATTR(pwm2_weight_temp_sel, S_IWUSR | S_IRUGO,
+ show_pwm_weight_temp_sel, store_pwm_weight_temp_sel,
+ 1);
+static SENSOR_DEVICE_ATTR(pwm3_weight_temp_sel, S_IWUSR | S_IRUGO,
+ show_pwm_weight_temp_sel, store_pwm_weight_temp_sel,
+ 2);
+static SENSOR_DEVICE_ATTR(pwm4_weight_temp_sel, S_IWUSR | S_IRUGO,
+ show_pwm_weight_temp_sel, store_pwm_weight_temp_sel,
+ 3);
+static SENSOR_DEVICE_ATTR(pwm5_weight_temp_sel, S_IWUSR | S_IRUGO,
+ show_pwm_weight_temp_sel, store_pwm_weight_temp_sel,
+ 4);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_weight_temp_step, S_IWUSR | S_IRUGO,
+ show_weight_temp, store_weight_temp, 0, 0);
+static SENSOR_DEVICE_ATTR_2(pwm2_weight_temp_step, S_IWUSR | S_IRUGO,
+ show_weight_temp, store_weight_temp, 1, 0);
+static SENSOR_DEVICE_ATTR_2(pwm3_weight_temp_step, S_IWUSR | S_IRUGO,
+ show_weight_temp, store_weight_temp, 2, 0);
+static SENSOR_DEVICE_ATTR_2(pwm4_weight_temp_step, S_IWUSR | S_IRUGO,
+ show_weight_temp, store_weight_temp, 3, 0);
+static SENSOR_DEVICE_ATTR_2(pwm5_weight_temp_step, S_IWUSR | S_IRUGO,
+ show_weight_temp, store_weight_temp, 4, 0);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_weight_temp_step_tol, S_IWUSR | S_IRUGO,
+ show_weight_temp, store_weight_temp, 0, 1);
+static SENSOR_DEVICE_ATTR_2(pwm2_weight_temp_step_tol, S_IWUSR | S_IRUGO,
+ show_weight_temp, store_weight_temp, 1, 1);
+static SENSOR_DEVICE_ATTR_2(pwm3_weight_temp_step_tol, S_IWUSR | S_IRUGO,
+ show_weight_temp, store_weight_temp, 2, 1);
+static SENSOR_DEVICE_ATTR_2(pwm4_weight_temp_step_tol, S_IWUSR | S_IRUGO,
+ show_weight_temp, store_weight_temp, 3, 1);
+static SENSOR_DEVICE_ATTR_2(pwm5_weight_temp_step_tol, S_IWUSR | S_IRUGO,
+ show_weight_temp, store_weight_temp, 4, 1);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_weight_temp_step_base, S_IWUSR | S_IRUGO,
+ show_weight_temp, store_weight_temp, 0, 2);
+static SENSOR_DEVICE_ATTR_2(pwm2_weight_temp_step_base, S_IWUSR | S_IRUGO,
+ show_weight_temp, store_weight_temp, 1, 2);
+static SENSOR_DEVICE_ATTR_2(pwm3_weight_temp_step_base, S_IWUSR | S_IRUGO,
+ show_weight_temp, store_weight_temp, 2, 2);
+static SENSOR_DEVICE_ATTR_2(pwm4_weight_temp_step_base, S_IWUSR | S_IRUGO,
+ show_weight_temp, store_weight_temp, 3, 2);
+static SENSOR_DEVICE_ATTR_2(pwm5_weight_temp_step_base, S_IWUSR | S_IRUGO,
+ show_weight_temp, store_weight_temp, 4, 2);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_weight_duty_step, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 0, 5);
+static SENSOR_DEVICE_ATTR_2(pwm2_weight_duty_step, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 1, 5);
+static SENSOR_DEVICE_ATTR_2(pwm3_weight_duty_step, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 2, 5);
+static SENSOR_DEVICE_ATTR_2(pwm4_weight_duty_step, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 3, 5);
+static SENSOR_DEVICE_ATTR_2(pwm5_weight_duty_step, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 4, 5);
+
+/* duty_base is not supported on all chips */
+static struct sensor_device_attribute_2 sda_weight_duty_base[] = {
+ SENSOR_ATTR_2(pwm1_weight_duty_base, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 0, 6),
+ SENSOR_ATTR_2(pwm2_weight_duty_base, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 1, 6),
+ SENSOR_ATTR_2(pwm3_weight_duty_base, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 2, 6),
+ SENSOR_ATTR_2(pwm4_weight_duty_base, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 3, 6),
+ SENSOR_ATTR_2(pwm5_weight_duty_base, S_IWUSR | S_IRUGO,
+ show_pwm, store_pwm, 4, 6),
+};
+
+static ssize_t
+show_fan_time(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int index = sattr->index;
+
+ return sprintf(buf, "%d\n",
+ step_time_from_reg(data->fan_time[index][nr],
+ data->pwm_mode[nr]));
+}
+
+static ssize_t
+store_fan_time(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int index = sattr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = step_time_to_reg(val, data->pwm_mode[nr]);
+ mutex_lock(&data->update_lock);
+ data->fan_time[index][nr] = val;
+ nct6775_write_value(data, data->REG_FAN_TIME[index][nr], val);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+show_name(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", data->name);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_time,
+ store_fan_time, 0, 0);
+static SENSOR_DEVICE_ATTR_2(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_time,
+ store_fan_time, 1, 0);
+static SENSOR_DEVICE_ATTR_2(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_time,
+ store_fan_time, 2, 0);
+static SENSOR_DEVICE_ATTR_2(pwm4_stop_time, S_IWUSR | S_IRUGO, show_fan_time,
+ store_fan_time, 3, 0);
+static SENSOR_DEVICE_ATTR_2(pwm5_stop_time, S_IWUSR | S_IRUGO, show_fan_time,
+ store_fan_time, 4, 0);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_step_up_time, S_IWUSR | S_IRUGO, show_fan_time,
+ store_fan_time, 0, 1);
+static SENSOR_DEVICE_ATTR_2(pwm2_step_up_time, S_IWUSR | S_IRUGO, show_fan_time,
+ store_fan_time, 1, 1);
+static SENSOR_DEVICE_ATTR_2(pwm3_step_up_time, S_IWUSR | S_IRUGO, show_fan_time,
+ store_fan_time, 2, 1);
+static SENSOR_DEVICE_ATTR_2(pwm4_step_up_time, S_IWUSR | S_IRUGO, show_fan_time,
+ store_fan_time, 3, 1);
+static SENSOR_DEVICE_ATTR_2(pwm5_step_up_time, S_IWUSR | S_IRUGO, show_fan_time,
+ store_fan_time, 4, 1);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_step_down_time, S_IWUSR | S_IRUGO,
+ show_fan_time, store_fan_time, 0, 2);
+static SENSOR_DEVICE_ATTR_2(pwm2_step_down_time, S_IWUSR | S_IRUGO,
+ show_fan_time, store_fan_time, 1, 2);
+static SENSOR_DEVICE_ATTR_2(pwm3_step_down_time, S_IWUSR | S_IRUGO,
+ show_fan_time, store_fan_time, 2, 2);
+static SENSOR_DEVICE_ATTR_2(pwm4_step_down_time, S_IWUSR | S_IRUGO,
+ show_fan_time, store_fan_time, 3, 2);
+static SENSOR_DEVICE_ATTR_2(pwm5_step_down_time, S_IWUSR | S_IRUGO,
+ show_fan_time, store_fan_time, 4, 2);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_start, S_IWUSR | S_IRUGO, show_pwm,
+ store_pwm, 0, 1);
+static SENSOR_DEVICE_ATTR_2(pwm2_start, S_IWUSR | S_IRUGO, show_pwm,
+ store_pwm, 1, 1);
+static SENSOR_DEVICE_ATTR_2(pwm3_start, S_IWUSR | S_IRUGO, show_pwm,
+ store_pwm, 2, 1);
+static SENSOR_DEVICE_ATTR_2(pwm4_start, S_IWUSR | S_IRUGO, show_pwm,
+ store_pwm, 3, 1);
+static SENSOR_DEVICE_ATTR_2(pwm5_start, S_IWUSR | S_IRUGO, show_pwm,
+ store_pwm, 4, 1);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_floor, S_IWUSR | S_IRUGO, show_pwm,
+ store_pwm, 0, 2);
+static SENSOR_DEVICE_ATTR_2(pwm2_floor, S_IWUSR | S_IRUGO, show_pwm,
+ store_pwm, 1, 2);
+static SENSOR_DEVICE_ATTR_2(pwm3_floor, S_IWUSR | S_IRUGO, show_pwm,
+ store_pwm, 2, 2);
+static SENSOR_DEVICE_ATTR_2(pwm4_floor, S_IWUSR | S_IRUGO, show_pwm,
+ store_pwm, 3, 2);
+static SENSOR_DEVICE_ATTR_2(pwm5_floor, S_IWUSR | S_IRUGO, show_pwm,
+ store_pwm, 4, 2);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_temp_tolerance, S_IWUSR | S_IRUGO,
+ show_temp_tolerance, store_temp_tolerance, 0, 0);
+static SENSOR_DEVICE_ATTR_2(pwm2_temp_tolerance, S_IWUSR | S_IRUGO,
+ show_temp_tolerance, store_temp_tolerance, 1, 0);
+static SENSOR_DEVICE_ATTR_2(pwm3_temp_tolerance, S_IWUSR | S_IRUGO,
+ show_temp_tolerance, store_temp_tolerance, 2, 0);
+static SENSOR_DEVICE_ATTR_2(pwm4_temp_tolerance, S_IWUSR | S_IRUGO,
+ show_temp_tolerance, store_temp_tolerance, 3, 0);
+static SENSOR_DEVICE_ATTR_2(pwm5_temp_tolerance, S_IWUSR | S_IRUGO,
+ show_temp_tolerance, store_temp_tolerance, 4, 0);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_crit_temp_tolerance, S_IWUSR | S_IRUGO,
+ show_temp_tolerance, store_temp_tolerance, 0, 1);
+static SENSOR_DEVICE_ATTR_2(pwm2_crit_temp_tolerance, S_IWUSR | S_IRUGO,
+ show_temp_tolerance, store_temp_tolerance, 1, 1);
+static SENSOR_DEVICE_ATTR_2(pwm3_crit_temp_tolerance, S_IWUSR | S_IRUGO,
+ show_temp_tolerance, store_temp_tolerance, 2, 1);
+static SENSOR_DEVICE_ATTR_2(pwm4_crit_temp_tolerance, S_IWUSR | S_IRUGO,
+ show_temp_tolerance, store_temp_tolerance, 3, 1);
+static SENSOR_DEVICE_ATTR_2(pwm5_crit_temp_tolerance, S_IWUSR | S_IRUGO,
+ show_temp_tolerance, store_temp_tolerance, 4, 1);
+
+/* pwm_max is not supported on all chips */
+static struct sensor_device_attribute_2 sda_pwm_max[] = {
+ SENSOR_ATTR_2(pwm1_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm,
+ 0, 3),
+ SENSOR_ATTR_2(pwm2_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm,
+ 1, 3),
+ SENSOR_ATTR_2(pwm3_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm,
+ 2, 3),
+ SENSOR_ATTR_2(pwm4_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm,
+ 3, 3),
+ SENSOR_ATTR_2(pwm5_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm,
+ 4, 3),
+};
+
+/* pwm_step is not supported on all chips */
+static struct sensor_device_attribute_2 sda_pwm_step[] = {
+ SENSOR_ATTR_2(pwm1_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 4),
+ SENSOR_ATTR_2(pwm2_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1, 4),
+ SENSOR_ATTR_2(pwm3_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2, 4),
+ SENSOR_ATTR_2(pwm4_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3, 4),
+ SENSOR_ATTR_2(pwm5_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 4, 4),
+};
+
+static struct attribute *nct6775_attributes_pwm[5][20] = {
+ {
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm1_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1_temp_sel.dev_attr.attr,
+ &sensor_dev_attr_pwm1_temp_tolerance.dev_attr.attr,
+ &sensor_dev_attr_pwm1_crit_temp_tolerance.dev_attr.attr,
+ &sensor_dev_attr_pwm1_target_temp.dev_attr.attr,
+ &sensor_dev_attr_fan1_target.dev_attr.attr,
+ &sensor_dev_attr_fan1_tolerance.dev_attr.attr,
+ &sensor_dev_attr_pwm1_stop_time.dev_attr.attr,
+ &sensor_dev_attr_pwm1_step_up_time.dev_attr.attr,
+ &sensor_dev_attr_pwm1_step_down_time.dev_attr.attr,
+ &sensor_dev_attr_pwm1_start.dev_attr.attr,
+ &sensor_dev_attr_pwm1_floor.dev_attr.attr,
+ &sensor_dev_attr_pwm1_weight_temp_sel.dev_attr.attr,
+ &sensor_dev_attr_pwm1_weight_temp_step.dev_attr.attr,
+ &sensor_dev_attr_pwm1_weight_temp_step_tol.dev_attr.attr,
+ &sensor_dev_attr_pwm1_weight_temp_step_base.dev_attr.attr,
+ &sensor_dev_attr_pwm1_weight_duty_step.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm2_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2_temp_sel.dev_attr.attr,
+ &sensor_dev_attr_pwm2_temp_tolerance.dev_attr.attr,
+ &sensor_dev_attr_pwm2_crit_temp_tolerance.dev_attr.attr,
+ &sensor_dev_attr_pwm2_target_temp.dev_attr.attr,
+ &sensor_dev_attr_fan2_target.dev_attr.attr,
+ &sensor_dev_attr_fan2_tolerance.dev_attr.attr,
+ &sensor_dev_attr_pwm2_stop_time.dev_attr.attr,
+ &sensor_dev_attr_pwm2_step_up_time.dev_attr.attr,
+ &sensor_dev_attr_pwm2_step_down_time.dev_attr.attr,
+ &sensor_dev_attr_pwm2_start.dev_attr.attr,
+ &sensor_dev_attr_pwm2_floor.dev_attr.attr,
+ &sensor_dev_attr_pwm2_weight_temp_sel.dev_attr.attr,
+ &sensor_dev_attr_pwm2_weight_temp_step.dev_attr.attr,
+ &sensor_dev_attr_pwm2_weight_temp_step_tol.dev_attr.attr,
+ &sensor_dev_attr_pwm2_weight_temp_step_base.dev_attr.attr,
+ &sensor_dev_attr_pwm2_weight_duty_step.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+ &sensor_dev_attr_pwm3_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm3_temp_sel.dev_attr.attr,
+ &sensor_dev_attr_pwm3_temp_tolerance.dev_attr.attr,
+ &sensor_dev_attr_pwm3_crit_temp_tolerance.dev_attr.attr,
+ &sensor_dev_attr_pwm3_target_temp.dev_attr.attr,
+ &sensor_dev_attr_fan3_target.dev_attr.attr,
+ &sensor_dev_attr_fan3_tolerance.dev_attr.attr,
+ &sensor_dev_attr_pwm3_stop_time.dev_attr.attr,
+ &sensor_dev_attr_pwm3_step_up_time.dev_attr.attr,
+ &sensor_dev_attr_pwm3_step_down_time.dev_attr.attr,
+ &sensor_dev_attr_pwm3_start.dev_attr.attr,
+ &sensor_dev_attr_pwm3_floor.dev_attr.attr,
+ &sensor_dev_attr_pwm3_weight_temp_sel.dev_attr.attr,
+ &sensor_dev_attr_pwm3_weight_temp_step.dev_attr.attr,
+ &sensor_dev_attr_pwm3_weight_temp_step_tol.dev_attr.attr,
+ &sensor_dev_attr_pwm3_weight_temp_step_base.dev_attr.attr,
+ &sensor_dev_attr_pwm3_weight_duty_step.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_pwm4.dev_attr.attr,
+ &sensor_dev_attr_pwm4_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm4_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm4_temp_sel.dev_attr.attr,
+ &sensor_dev_attr_pwm4_temp_tolerance.dev_attr.attr,
+ &sensor_dev_attr_pwm4_crit_temp_tolerance.dev_attr.attr,
+ &sensor_dev_attr_pwm4_target_temp.dev_attr.attr,
+ &sensor_dev_attr_fan4_target.dev_attr.attr,
+ &sensor_dev_attr_fan4_tolerance.dev_attr.attr,
+ &sensor_dev_attr_pwm4_stop_time.dev_attr.attr,
+ &sensor_dev_attr_pwm4_step_up_time.dev_attr.attr,
+ &sensor_dev_attr_pwm4_step_down_time.dev_attr.attr,
+ &sensor_dev_attr_pwm4_start.dev_attr.attr,
+ &sensor_dev_attr_pwm4_floor.dev_attr.attr,
+ &sensor_dev_attr_pwm4_weight_temp_sel.dev_attr.attr,
+ &sensor_dev_attr_pwm4_weight_temp_step.dev_attr.attr,
+ &sensor_dev_attr_pwm4_weight_temp_step_tol.dev_attr.attr,
+ &sensor_dev_attr_pwm4_weight_temp_step_base.dev_attr.attr,
+ &sensor_dev_attr_pwm4_weight_duty_step.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_pwm5.dev_attr.attr,
+ &sensor_dev_attr_pwm5_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm5_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm5_temp_sel.dev_attr.attr,
+ &sensor_dev_attr_pwm5_temp_tolerance.dev_attr.attr,
+ &sensor_dev_attr_pwm5_crit_temp_tolerance.dev_attr.attr,
+ &sensor_dev_attr_pwm5_target_temp.dev_attr.attr,
+ &sensor_dev_attr_fan5_target.dev_attr.attr,
+ &sensor_dev_attr_fan5_tolerance.dev_attr.attr,
+ &sensor_dev_attr_pwm5_stop_time.dev_attr.attr,
+ &sensor_dev_attr_pwm5_step_up_time.dev_attr.attr,
+ &sensor_dev_attr_pwm5_step_down_time.dev_attr.attr,
+ &sensor_dev_attr_pwm5_start.dev_attr.attr,
+ &sensor_dev_attr_pwm5_floor.dev_attr.attr,
+ &sensor_dev_attr_pwm5_weight_temp_sel.dev_attr.attr,
+ &sensor_dev_attr_pwm5_weight_temp_step.dev_attr.attr,
+ &sensor_dev_attr_pwm5_weight_temp_step_tol.dev_attr.attr,
+ &sensor_dev_attr_pwm5_weight_temp_step_base.dev_attr.attr,
+ &sensor_dev_attr_pwm5_weight_duty_step.dev_attr.attr,
+ NULL
+ },
+};
+
+static const struct attribute_group nct6775_group_pwm[5] = {
+ { .attrs = nct6775_attributes_pwm[0] },
+ { .attrs = nct6775_attributes_pwm[1] },
+ { .attrs = nct6775_attributes_pwm[2] },
+ { .attrs = nct6775_attributes_pwm[3] },
+ { .attrs = nct6775_attributes_pwm[4] },
+};
+
+static ssize_t
+show_auto_pwm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+
+ return sprintf(buf, "%d\n", data->auto_pwm[sattr->nr][sattr->index]);
+}
+
+static ssize_t
+store_auto_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int point = sattr->index;
+ unsigned long val;
+ int err;
+ u8 reg;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+ if (val > 255)
+ return -EINVAL;
+
+ if (point == data->auto_pwm_num) {
+ if (data->kind != nct6775 && !val)
+ return -EINVAL;
+ if (data->kind != nct6779 && val)
+ val = 0xff;
+ }
+
+ mutex_lock(&data->update_lock);
+ data->auto_pwm[nr][point] = val;
+ if (point < data->auto_pwm_num) {
+ nct6775_write_value(data,
+ NCT6775_AUTO_PWM(data, nr, point),
+ data->auto_pwm[nr][point]);
+ } else {
+ switch (data->kind) {
+ case nct6775:
+ /* disable if needed (pwm == 0) */
+ reg = nct6775_read_value(data,
+ NCT6775_REG_CRITICAL_ENAB[nr]);
+ if (val)
+ reg |= 0x02;
+ else
+ reg &= ~0x02;
+ nct6775_write_value(data, NCT6775_REG_CRITICAL_ENAB[nr],
+ reg);
+ break;
+ case nct6776:
+ break; /* always enabled, nothing to do */
+ case nct6779:
+ nct6775_write_value(data, NCT6779_REG_CRITICAL_PWM[nr],
+ val);
+ reg = nct6775_read_value(data,
+ NCT6779_REG_CRITICAL_PWM_ENABLE[nr]);
+ if (val == 255)
+ reg &= ~0x01;
+ else
+ reg |= 0x01;
+ nct6775_write_value(data,
+ NCT6779_REG_CRITICAL_PWM_ENABLE[nr],
+ reg);
+ break;
+ }
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+show_auto_temp(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int point = sattr->index;
+
+ /*
+ * We don't know for sure if the temperature is signed or unsigned.
+ * Assume it is unsigned.
+ */
+ return sprintf(buf, "%d\n", data->auto_temp[nr][point] * 1000);
+}
+
+static ssize_t
+store_auto_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int nr = sattr->nr;
+ int point = sattr->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+ if (val > 255000)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ data->auto_temp[nr][point] = DIV_ROUND_CLOSEST(val, 1000);
+ if (point < data->auto_pwm_num) {
+ nct6775_write_value(data,
+ NCT6775_AUTO_TEMP(data, nr, point),
+ data->auto_temp[nr][point]);
+ } else {
+ nct6775_write_value(data, data->REG_CRITICAL_TEMP[nr],
+ data->auto_temp[nr][point]);
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/*
+ * The number of auto-point trip points is chip dependent.
+ * Need to check support while generating/removing attribute files.
+ */
+static struct sensor_device_attribute_2 sda_auto_pwm_arrays[] = {
+ SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 0, 0),
+ SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 0, 0),
+ SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 0, 1),
+ SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 0, 1),
+ SENSOR_ATTR_2(pwm1_auto_point3_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 0, 2),
+ SENSOR_ATTR_2(pwm1_auto_point3_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 0, 2),
+ SENSOR_ATTR_2(pwm1_auto_point4_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 0, 3),
+ SENSOR_ATTR_2(pwm1_auto_point4_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 0, 3),
+ SENSOR_ATTR_2(pwm1_auto_point5_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 0, 4),
+ SENSOR_ATTR_2(pwm1_auto_point5_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 0, 4),
+ SENSOR_ATTR_2(pwm1_auto_point6_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 0, 5),
+ SENSOR_ATTR_2(pwm1_auto_point6_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 0, 5),
+ SENSOR_ATTR_2(pwm1_auto_point7_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 0, 6),
+ SENSOR_ATTR_2(pwm1_auto_point7_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 0, 6),
+
+ SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 1, 0),
+ SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 1, 0),
+ SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 1, 1),
+ SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 1, 1),
+ SENSOR_ATTR_2(pwm2_auto_point3_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 1, 2),
+ SENSOR_ATTR_2(pwm2_auto_point3_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 1, 2),
+ SENSOR_ATTR_2(pwm2_auto_point4_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 1, 3),
+ SENSOR_ATTR_2(pwm2_auto_point4_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 1, 3),
+ SENSOR_ATTR_2(pwm2_auto_point5_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 1, 4),
+ SENSOR_ATTR_2(pwm2_auto_point5_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 1, 4),
+ SENSOR_ATTR_2(pwm2_auto_point6_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 1, 5),
+ SENSOR_ATTR_2(pwm2_auto_point6_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 1, 5),
+ SENSOR_ATTR_2(pwm2_auto_point7_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 1, 6),
+ SENSOR_ATTR_2(pwm2_auto_point7_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 1, 6),
+
+ SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 2, 0),
+ SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 2, 0),
+ SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 2, 1),
+ SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 2, 1),
+ SENSOR_ATTR_2(pwm3_auto_point3_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 2, 2),
+ SENSOR_ATTR_2(pwm3_auto_point3_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 2, 2),
+ SENSOR_ATTR_2(pwm3_auto_point4_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 2, 3),
+ SENSOR_ATTR_2(pwm3_auto_point4_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 2, 3),
+ SENSOR_ATTR_2(pwm3_auto_point5_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 2, 4),
+ SENSOR_ATTR_2(pwm3_auto_point5_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 2, 4),
+ SENSOR_ATTR_2(pwm3_auto_point6_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 2, 5),
+ SENSOR_ATTR_2(pwm3_auto_point6_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 2, 5),
+ SENSOR_ATTR_2(pwm3_auto_point7_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 2, 6),
+ SENSOR_ATTR_2(pwm3_auto_point7_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 2, 6),
+
+ SENSOR_ATTR_2(pwm4_auto_point1_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 3, 0),
+ SENSOR_ATTR_2(pwm4_auto_point1_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 3, 0),
+ SENSOR_ATTR_2(pwm4_auto_point2_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 3, 1),
+ SENSOR_ATTR_2(pwm4_auto_point2_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 3, 1),
+ SENSOR_ATTR_2(pwm4_auto_point3_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 3, 2),
+ SENSOR_ATTR_2(pwm4_auto_point3_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 3, 2),
+ SENSOR_ATTR_2(pwm4_auto_point4_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 3, 3),
+ SENSOR_ATTR_2(pwm4_auto_point4_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 3, 3),
+ SENSOR_ATTR_2(pwm4_auto_point5_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 3, 4),
+ SENSOR_ATTR_2(pwm4_auto_point5_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 3, 4),
+ SENSOR_ATTR_2(pwm4_auto_point6_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 3, 5),
+ SENSOR_ATTR_2(pwm4_auto_point6_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 3, 5),
+ SENSOR_ATTR_2(pwm4_auto_point7_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 3, 6),
+ SENSOR_ATTR_2(pwm4_auto_point7_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 3, 6),
+
+ SENSOR_ATTR_2(pwm5_auto_point1_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 4, 0),
+ SENSOR_ATTR_2(pwm5_auto_point1_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 4, 0),
+ SENSOR_ATTR_2(pwm5_auto_point2_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 4, 1),
+ SENSOR_ATTR_2(pwm5_auto_point2_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 4, 1),
+ SENSOR_ATTR_2(pwm5_auto_point3_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 4, 2),
+ SENSOR_ATTR_2(pwm5_auto_point3_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 4, 2),
+ SENSOR_ATTR_2(pwm5_auto_point4_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 4, 3),
+ SENSOR_ATTR_2(pwm5_auto_point4_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 4, 3),
+ SENSOR_ATTR_2(pwm5_auto_point5_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 4, 4),
+ SENSOR_ATTR_2(pwm5_auto_point5_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 4, 4),
+ SENSOR_ATTR_2(pwm5_auto_point6_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 4, 5),
+ SENSOR_ATTR_2(pwm5_auto_point6_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 4, 5),
+ SENSOR_ATTR_2(pwm5_auto_point7_pwm, S_IWUSR | S_IRUGO,
+ show_auto_pwm, store_auto_pwm, 4, 6),
+ SENSOR_ATTR_2(pwm5_auto_point7_temp, S_IWUSR | S_IRUGO,
+ show_auto_temp, store_auto_temp, 4, 6),
+};
+
+static ssize_t
+show_vid(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
+}
+
+static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+
+/* Case open detection */
+
+static ssize_t
+clear_caseopen(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct nct6775_sio_data *sio_data = dev->platform_data;
+ int nr = to_sensor_dev_attr(attr)->index - INTRUSION_ALARM_BASE;
+ unsigned long val;
+ u8 reg;
+ int ret;
+
+ if (kstrtoul(buf, 10, &val) || val != 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+
+ /*
+ * Use CR registers to clear caseopen status.
+ * The CR registers are the same for all chips, and not all chips
+ * support clearing the caseopen status through "regular" registers.
+ */
+ ret = superio_enter(sio_data->sioreg);
+ if (ret) {
+ count = ret;
+ goto error;
+ }
+
+ superio_select(sio_data->sioreg, NCT6775_LD_ACPI);
+ reg = superio_inb(sio_data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr]);
+ reg |= NCT6775_CR_CASEOPEN_CLR_MASK[nr];
+ superio_outb(sio_data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg);
+ reg &= ~NCT6775_CR_CASEOPEN_CLR_MASK[nr];
+ superio_outb(sio_data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg);
+ superio_exit(sio_data->sioreg);
+
+ data->valid = false; /* Force cache refresh */
+error:
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static struct sensor_device_attribute sda_caseopen[] = {
+ SENSOR_ATTR(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm,
+ clear_caseopen, INTRUSION_ALARM_BASE),
+ SENSOR_ATTR(intrusion1_alarm, S_IWUSR | S_IRUGO, show_alarm,
+ clear_caseopen, INTRUSION_ALARM_BASE + 1),
+};
+
+/*
+ * Driver and device management
+ */
+
+static void nct6775_device_remove_files(struct device *dev)
+{
+ /*
+ * some entries in the following arrays may not have been used in
+ * device_create_file(), but device_remove_file() will ignore them
+ */
+ int i;
+ struct nct6775_data *data = dev_get_drvdata(dev);
+
+ for (i = 0; i < data->pwm_num; i++)
+ sysfs_remove_group(&dev->kobj, &nct6775_group_pwm[i]);
+
+ for (i = 0; i < ARRAY_SIZE(sda_pwm_max); i++)
+ device_remove_file(dev, &sda_pwm_max[i].dev_attr);
+
+ for (i = 0; i < ARRAY_SIZE(sda_pwm_step); i++)
+ device_remove_file(dev, &sda_pwm_step[i].dev_attr);
+
+ for (i = 0; i < ARRAY_SIZE(sda_weight_duty_base); i++)
+ device_remove_file(dev, &sda_weight_duty_base[i].dev_attr);
+
+ for (i = 0; i < ARRAY_SIZE(sda_auto_pwm_arrays); i++)
+ device_remove_file(dev, &sda_auto_pwm_arrays[i].dev_attr);
+
+ for (i = 0; i < data->in_num; i++)
+ sysfs_remove_group(&dev->kobj, &nct6775_group_in[i]);
+
+ for (i = 0; i < 5; i++) {
+ device_remove_file(dev, &sda_fan_input[i].dev_attr);
+ device_remove_file(dev, &sda_fan_alarm[i].dev_attr);
+ device_remove_file(dev, &sda_fan_div[i].dev_attr);
+ device_remove_file(dev, &sda_fan_min[i].dev_attr);
+ device_remove_file(dev, &sda_fan_pulses[i].dev_attr);
+ }
+ for (i = 0; i < NUM_TEMP; i++) {
+ if (!(data->have_temp & (1 << i)))
+ continue;
+ device_remove_file(dev, &sda_temp_input[i].dev_attr);
+ device_remove_file(dev, &sda_temp_label[i].dev_attr);
+ device_remove_file(dev, &sda_temp_max[i].dev_attr);
+ device_remove_file(dev, &sda_temp_max_hyst[i].dev_attr);
+ device_remove_file(dev, &sda_temp_crit[i].dev_attr);
+ if (!(data->have_temp_fixed & (1 << i)))
+ continue;
+ device_remove_file(dev, &sda_temp_type[i].dev_attr);
+ device_remove_file(dev, &sda_temp_offset[i].dev_attr);
+ if (i >= NUM_TEMP_ALARM)
+ continue;
+ device_remove_file(dev, &sda_temp_alarm[i].dev_attr);
+ }
+
+ device_remove_file(dev, &sda_caseopen[0].dev_attr);
+ device_remove_file(dev, &sda_caseopen[1].dev_attr);
+
+ device_remove_file(dev, &dev_attr_name);
+ device_remove_file(dev, &dev_attr_cpu0_vid);
+}
+
+/* Get the monitoring functions started */
+static inline void nct6775_init_device(struct nct6775_data *data)
+{
+ int i;
+ u8 tmp, diode;
+
+ /* Start monitoring if needed */
+ if (data->REG_CONFIG) {
+ tmp = nct6775_read_value(data, data->REG_CONFIG);
+ if (!(tmp & 0x01))
+ nct6775_write_value(data, data->REG_CONFIG, tmp | 0x01);
+ }
+
+ /* Enable temperature sensors if needed */
+ for (i = 0; i < NUM_TEMP; i++) {
+ if (!(data->have_temp & (1 << i)))
+ continue;
+ if (!data->reg_temp_config[i])
+ continue;
+ tmp = nct6775_read_value(data, data->reg_temp_config[i]);
+ if (tmp & 0x01)
+ nct6775_write_value(data, data->reg_temp_config[i],
+ tmp & 0xfe);
+ }
+
+ /* Enable VBAT monitoring if needed */
+ tmp = nct6775_read_value(data, data->REG_VBAT);
+ if (!(tmp & 0x01))
+ nct6775_write_value(data, data->REG_VBAT, tmp | 0x01);
+
+ diode = nct6775_read_value(data, data->REG_DIODE);
+
+ for (i = 0; i < data->temp_fixed_num; i++) {
+ if (!(data->have_temp_fixed & (1 << i)))
+ continue;
+ if ((tmp & (0x02 << i))) /* diode */
+ data->temp_type[i] = 3 - ((diode >> i) & 0x02);
+ else /* thermistor */
+ data->temp_type[i] = 4;
+ }
+}
+
+static int
+nct6775_check_fan_inputs(const struct nct6775_sio_data *sio_data,
+ struct nct6775_data *data)
+{
+ int regval;
+ bool fan3pin, fan3min, fan4pin, fan4min, fan5pin;
+ bool pwm3pin, pwm4pin, pwm5pin;
+ int ret;
+
+ ret = superio_enter(sio_data->sioreg);
+ if (ret)
+ return ret;
+
+ /* fan4 and fan5 share some pins with the GPIO and serial flash */
+ if (data->kind == nct6775) {
+ regval = superio_inb(sio_data->sioreg, 0x2c);
+
+ fan3pin = regval & (1 << 6);
+ fan3min = fan3pin;
+ pwm3pin = regval & (1 << 7);
+
+ /* On NCT6775, fan4 shares pins with the fdc interface */
+ fan4pin = !(superio_inb(sio_data->sioreg, 0x2A) & 0x80);
+ fan4min = 0;
+ fan5pin = 0;
+ pwm4pin = 0;
+ pwm5pin = 0;
+ } else if (data->kind == nct6776) {
+ bool gpok = superio_inb(sio_data->sioreg, 0x27) & 0x80;
+
+ superio_select(sio_data->sioreg, NCT6775_LD_HWM);
+ regval = superio_inb(sio_data->sioreg, SIO_REG_ENABLE);
+
+ if (regval & 0x80)
+ fan3pin = gpok;
+ else
+ fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40);
+
+ if (regval & 0x40)
+ fan4pin = gpok;
+ else
+ fan4pin = superio_inb(sio_data->sioreg, 0x1C) & 0x01;
+
+ if (regval & 0x20)
+ fan5pin = gpok;
+ else
+ fan5pin = superio_inb(sio_data->sioreg, 0x1C) & 0x02;
+
+ fan4min = fan4pin;
+ fan3min = fan3pin;
+ pwm3pin = fan3pin;
+ pwm4pin = 0;
+ pwm5pin = 0;
+ } else { /* NCT6779D */
+ regval = superio_inb(sio_data->sioreg, 0x1c);
+
+ fan3pin = !(regval & (1 << 5));
+ fan4pin = !(regval & (1 << 6));
+ fan5pin = !(regval & (1 << 7));
+
+ pwm3pin = !(regval & (1 << 0));
+ pwm4pin = !(regval & (1 << 1));
+ pwm5pin = !(regval & (1 << 2));
+
+ fan3min = fan3pin;
+ fan4min = fan4pin;
+ }
+
+ superio_exit(sio_data->sioreg);
+
+ data->has_fan = data->has_fan_min = 0x03; /* fan1 and fan2 */
+ data->has_fan |= fan3pin << 2;
+ data->has_fan_min |= fan3min << 2;
+
+ data->has_fan |= (fan4pin << 3) | (fan5pin << 4);
+ data->has_fan_min |= (fan4min << 3) | (fan5pin << 4);
+
+ data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) | (pwm5pin << 4);
+
+ return 0;
+}
+
+static void add_temp_sensors(struct nct6775_data *data, const u16 *regp,
+ int *available, int *mask)
+{
+ int i;
+ u8 src;
+
+ for (i = 0; i < data->pwm_num && *available; i++) {
+ int index;
+
+ if (!regp[i])
+ continue;
+ src = nct6775_read_value(data, regp[i]);
+ src &= 0x1f;
+ if (!src || (*mask & (1 << src)))
+ continue;
+ if (src >= data->temp_label_num ||
+ !strlen(data->temp_label[src]))
+ continue;
+
+ index = __ffs(*available);
+ nct6775_write_value(data, data->REG_TEMP_SOURCE[index], src);
+ *available &= ~(1 << index);
+ *mask |= 1 << src;
+ }
+}
+
+static int nct6775_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct nct6775_sio_data *sio_data = dev->platform_data;
+ struct nct6775_data *data;
+ struct resource *res;
+ int i, s, err = 0;
+ int src, mask, available;
+ const u16 *reg_temp, *reg_temp_over, *reg_temp_hyst, *reg_temp_config;
+ const u16 *reg_temp_alternate, *reg_temp_crit;
+ int num_reg_temp;
+ bool have_vid = false;
+ u8 cr2a;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH,
+ DRVNAME))
+ return -EBUSY;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct nct6775_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->kind = sio_data->kind;
+ data->addr = res->start;
+ mutex_init(&data->update_lock);
+ data->name = nct6775_device_names[data->kind];
+ data->bank = 0xff; /* Force initial bank selection */
+ platform_set_drvdata(pdev, data);
+
+ switch (data->kind) {
+ case nct6775:
+ data->in_num = 9;
+ data->pwm_num = 3;
+ data->auto_pwm_num = 6;
+ data->has_fan_div = true;
+ data->temp_fixed_num = 3;
+
+ data->ALARM_BITS = NCT6775_ALARM_BITS;
+
+ data->fan_from_reg = fan_from_reg16;
+ data->fan_from_reg_min = fan_from_reg8;
+ data->target_temp_mask = 0x7f;
+ data->tolerance_mask = 0x0f;
+ data->speed_tolerance_limit = 15;
+
+ data->temp_label = nct6775_temp_label;
+ data->temp_label_num = ARRAY_SIZE(nct6775_temp_label);
+
+ data->REG_CONFIG = NCT6775_REG_CONFIG;
+ data->REG_VBAT = NCT6775_REG_VBAT;
+ data->REG_DIODE = NCT6775_REG_DIODE;
+ data->REG_VIN = NCT6775_REG_IN;
+ data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN;
+ data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX;
+ data->REG_TARGET = NCT6775_REG_TARGET;
+ data->REG_FAN = NCT6775_REG_FAN;
+ data->REG_FAN_MODE = NCT6775_REG_FAN_MODE;
+ data->REG_FAN_MIN = NCT6775_REG_FAN_MIN;
+ data->REG_FAN_PULSES = NCT6775_REG_FAN_PULSES;
+ data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME;
+ data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME;
+ data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME;
+ data->REG_PWM[0] = NCT6775_REG_PWM;
+ data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT;
+ data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT;
+ data->REG_PWM[3] = NCT6775_REG_FAN_MAX_OUTPUT;
+ data->REG_PWM[4] = NCT6775_REG_FAN_STEP_OUTPUT;
+ data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP;
+ data->REG_PWM_READ = NCT6775_REG_PWM_READ;
+ data->REG_PWM_MODE = NCT6775_REG_PWM_MODE;
+ data->PWM_MODE_MASK = NCT6775_PWM_MODE_MASK;
+ data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP;
+ data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM;
+ data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP;
+ data->REG_CRITICAL_TEMP_TOLERANCE
+ = NCT6775_REG_CRITICAL_TEMP_TOLERANCE;
+ data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET;
+ data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE;
+ data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL;
+ data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL;
+ data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP;
+ data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL;
+ data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE;
+ data->REG_ALARM = NCT6775_REG_ALARM;
+
+ reg_temp = NCT6775_REG_TEMP;
+ num_reg_temp = ARRAY_SIZE(NCT6775_REG_TEMP);
+ reg_temp_over = NCT6775_REG_TEMP_OVER;
+ reg_temp_hyst = NCT6775_REG_TEMP_HYST;
+ reg_temp_config = NCT6775_REG_TEMP_CONFIG;
+ reg_temp_alternate = NCT6775_REG_TEMP_ALTERNATE;
+ reg_temp_crit = NCT6775_REG_TEMP_CRIT;
+
+ break;
+ case nct6776:
+ data->in_num = 9;
+ data->pwm_num = 3;
+ data->auto_pwm_num = 4;
+ data->has_fan_div = false;
+ data->temp_fixed_num = 3;
+
+ data->ALARM_BITS = NCT6776_ALARM_BITS;
+
+ data->fan_from_reg = fan_from_reg13;
+ data->fan_from_reg_min = fan_from_reg13;
+ data->target_temp_mask = 0xff;
+ data->tolerance_mask = 0x07;
+ data->speed_tolerance_limit = 63;
+
+ data->temp_label = nct6776_temp_label;
+ data->temp_label_num = ARRAY_SIZE(nct6776_temp_label);
+
+ data->REG_CONFIG = NCT6775_REG_CONFIG;
+ data->REG_VBAT = NCT6775_REG_VBAT;
+ data->REG_DIODE = NCT6775_REG_DIODE;
+ data->REG_VIN = NCT6775_REG_IN;
+ data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN;
+ data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX;
+ data->REG_TARGET = NCT6775_REG_TARGET;
+ data->REG_FAN = NCT6775_REG_FAN;
+ data->REG_FAN_MODE = NCT6775_REG_FAN_MODE;
+ data->REG_FAN_MIN = NCT6776_REG_FAN_MIN;
+ data->REG_FAN_PULSES = NCT6776_REG_FAN_PULSES;
+ data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME;
+ data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME;
+ data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME;
+ data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H;
+ data->REG_PWM[0] = NCT6775_REG_PWM;
+ data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT;
+ data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT;
+ data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP;
+ data->REG_PWM[6] = NCT6776_REG_WEIGHT_DUTY_BASE;
+ data->REG_PWM_READ = NCT6775_REG_PWM_READ;
+ data->REG_PWM_MODE = NCT6776_REG_PWM_MODE;
+ data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK;
+ data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP;
+ data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM;
+ data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP;
+ data->REG_CRITICAL_TEMP_TOLERANCE
+ = NCT6775_REG_CRITICAL_TEMP_TOLERANCE;
+ data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET;
+ data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE;
+ data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL;
+ data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL;
+ data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP;
+ data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL;
+ data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE;
+ data->REG_ALARM = NCT6775_REG_ALARM;
+
+ reg_temp = NCT6775_REG_TEMP;
+ num_reg_temp = ARRAY_SIZE(NCT6775_REG_TEMP);
+ reg_temp_over = NCT6775_REG_TEMP_OVER;
+ reg_temp_hyst = NCT6775_REG_TEMP_HYST;
+ reg_temp_config = NCT6776_REG_TEMP_CONFIG;
+ reg_temp_alternate = NCT6776_REG_TEMP_ALTERNATE;
+ reg_temp_crit = NCT6776_REG_TEMP_CRIT;
+
+ break;
+ case nct6779:
+ data->in_num = 15;
+ data->pwm_num = 5;
+ data->auto_pwm_num = 4;
+ data->has_fan_div = false;
+ data->temp_fixed_num = 6;
+
+ data->ALARM_BITS = NCT6779_ALARM_BITS;
+
+ data->fan_from_reg = fan_from_reg13;
+ data->fan_from_reg_min = fan_from_reg13;
+ data->target_temp_mask = 0xff;
+ data->tolerance_mask = 0x07;
+ data->speed_tolerance_limit = 63;
+
+ data->temp_label = nct6779_temp_label;
+ data->temp_label_num = ARRAY_SIZE(nct6779_temp_label);
+
+ data->REG_CONFIG = NCT6775_REG_CONFIG;
+ data->REG_VBAT = NCT6775_REG_VBAT;
+ data->REG_DIODE = NCT6775_REG_DIODE;
+ data->REG_VIN = NCT6779_REG_IN;
+ data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN;
+ data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX;
+ data->REG_TARGET = NCT6775_REG_TARGET;
+ data->REG_FAN = NCT6779_REG_FAN;
+ data->REG_FAN_MODE = NCT6775_REG_FAN_MODE;
+ data->REG_FAN_MIN = NCT6776_REG_FAN_MIN;
+ data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES;
+ data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME;
+ data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME;
+ data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME;
+ data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H;
+ data->REG_PWM[0] = NCT6775_REG_PWM;
+ data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT;
+ data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT;
+ data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP;
+ data->REG_PWM[6] = NCT6776_REG_WEIGHT_DUTY_BASE;
+ data->REG_PWM_READ = NCT6775_REG_PWM_READ;
+ data->REG_PWM_MODE = NCT6776_REG_PWM_MODE;
+ data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK;
+ data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP;
+ data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM;
+ data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP;
+ data->REG_CRITICAL_TEMP_TOLERANCE
+ = NCT6775_REG_CRITICAL_TEMP_TOLERANCE;
+ data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET;
+ data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE;
+ data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL;
+ data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL;
+ data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP;
+ data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL;
+ data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE;
+ data->REG_ALARM = NCT6779_REG_ALARM;
+
+ reg_temp = NCT6779_REG_TEMP;
+ num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP);
+ reg_temp_over = NCT6779_REG_TEMP_OVER;
+ reg_temp_hyst = NCT6779_REG_TEMP_HYST;
+ reg_temp_config = NCT6779_REG_TEMP_CONFIG;
+ reg_temp_alternate = NCT6779_REG_TEMP_ALTERNATE;
+ reg_temp_crit = NCT6779_REG_TEMP_CRIT;
+
+ break;
+ default:
+ return -ENODEV;
+ }
+ data->have_in = (1 << data->in_num) - 1;
+ data->have_temp = 0;
+
+ /*
+ * On some boards, not all available temperature sources are monitored,
+ * even though some of the monitoring registers are unused.
+ * Get list of unused monitoring registers, then detect if any fan
+ * controls are configured to use unmonitored temperature sources.
+ * If so, assign the unmonitored temperature sources to available
+ * monitoring registers.
+ */
+ mask = 0;
+ available = 0;
+ for (i = 0; i < num_reg_temp; i++) {
+ if (reg_temp[i] == 0)
+ continue;
+
+ src = nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f;
+ if (!src || (mask & (1 << src)))
+ available |= 1 << i;
+
+ mask |= 1 << src;
+ }
+
+ /*
+ * Now find unmonitored temperature registers and enable monitoring
+ * if additional monitoring registers are available.
+ */
+ add_temp_sensors(data, data->REG_TEMP_SEL, &available, &mask);
+ add_temp_sensors(data, data->REG_WEIGHT_TEMP_SEL, &available, &mask);
+
+ mask = 0;
+ s = NUM_TEMP_FIXED; /* First dynamic temperature attribute */
+ for (i = 0; i < num_reg_temp; i++) {
+ if (reg_temp[i] == 0)
+ continue;
+
+ src = nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f;
+ if (!src || (mask & (1 << src)))
+ continue;
+
+ if (src >= data->temp_label_num ||
+ !strlen(data->temp_label[src])) {
+ dev_info(dev,
+ "Invalid temperature source %d at index %d, source register 0x%x, temp register 0x%x\n",
+ src, i, data->REG_TEMP_SOURCE[i], reg_temp[i]);
+ continue;
+ }
+
+ mask |= 1 << src;
+
+ /* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */
+ if (src <= data->temp_fixed_num) {
+ data->have_temp |= 1 << (src - 1);
+ data->have_temp_fixed |= 1 << (src - 1);
+ data->reg_temp[0][src - 1] = reg_temp[i];
+ data->reg_temp[1][src - 1] = reg_temp_over[i];
+ data->reg_temp[2][src - 1] = reg_temp_hyst[i];
+ data->reg_temp_config[src - 1] = reg_temp_config[i];
+ data->temp_src[src - 1] = src;
+ continue;
+ }
+
+ if (s >= NUM_TEMP)
+ continue;
+
+ /* Use dynamic index for other sources */
+ data->have_temp |= 1 << s;
+ data->reg_temp[0][s] = reg_temp[i];
+ data->reg_temp[1][s] = reg_temp_over[i];
+ data->reg_temp[2][s] = reg_temp_hyst[i];
+ data->reg_temp_config[s] = reg_temp_config[i];
+ if (reg_temp_crit[src - 1])
+ data->reg_temp[3][s] = reg_temp_crit[src - 1];
+
+ data->temp_src[s] = src;
+ s++;
+ }
+
+#ifdef USE_ALTERNATE
+ /*
+ * Go through the list of alternate temp registers and enable
+ * if possible.
+ * The temperature is already monitored if the respective bit in <mask>
+ * is set.
+ */
+ for (i = 0; i < data->temp_label_num - 1; i++) {
+ if (!reg_temp_alternate[i])
+ continue;
+ if (mask & (1 << (i + 1)))
+ continue;
+ if (i < data->temp_fixed_num) {
+ if (data->have_temp & (1 << i))
+ continue;
+ data->have_temp |= 1 << i;
+ data->have_temp_fixed |= 1 << i;
+ data->reg_temp[0][i] = reg_temp_alternate[i];
+ data->reg_temp[1][i] = reg_temp_over[i];
+ data->reg_temp[2][i] = reg_temp_hyst[i];
+ data->temp_src[i] = i + 1;
+ continue;
+ }
+
+ if (s >= NUM_TEMP) /* Abort if no more space */
+ break;
+
+ data->have_temp |= 1 << s;
+ data->reg_temp[0][s] = reg_temp_alternate[i];
+ data->temp_src[s] = i + 1;
+ s++;
+ }
+#endif /* USE_ALTERNATE */
+
+ /* Initialize the chip */
+ nct6775_init_device(data);
+
+ err = superio_enter(sio_data->sioreg);
+ if (err)
+ return err;
+
+ cr2a = superio_inb(sio_data->sioreg, 0x2a);
+ switch (data->kind) {
+ case nct6775:
+ have_vid = (cr2a & 0x40);
+ break;
+ case nct6776:
+ have_vid = (cr2a & 0x60) == 0x40;
+ break;
+ case nct6779:
+ break;
+ }
+
+ /*
+ * Read VID value
+ * We can get the VID input values directly at logical device D 0xe3.
+ */
+ if (have_vid) {
+ superio_select(sio_data->sioreg, NCT6775_LD_VID);
+ data->vid = superio_inb(sio_data->sioreg, 0xe3);
+ data->vrm = vid_which_vrm();
+ }
+
+ if (fan_debounce) {
+ u8 tmp;
+
+ superio_select(sio_data->sioreg, NCT6775_LD_HWM);
+ tmp = superio_inb(sio_data->sioreg,
+ NCT6775_REG_CR_FAN_DEBOUNCE);
+ switch (data->kind) {
+ case nct6775:
+ tmp |= 0x1e;
+ break;
+ case nct6776:
+ case nct6779:
+ tmp |= 0x3e;
+ break;
+ }
+ superio_outb(sio_data->sioreg, NCT6775_REG_CR_FAN_DEBOUNCE,
+ tmp);
+ dev_info(&pdev->dev, "Enabled fan debounce for chip %s\n",
+ data->name);
+ }
+
+ superio_exit(sio_data->sioreg);
+
+ if (have_vid) {
+ err = device_create_file(dev, &dev_attr_cpu0_vid);
+ if (err)
+ return err;
+ }
+
+ err = nct6775_check_fan_inputs(sio_data, data);
+ if (err)
+ goto exit_remove;
+
+ /* Read fan clock dividers immediately */
+ nct6775_init_fan_common(dev, data);
+
+ /* Register sysfs hooks */
+ for (i = 0; i < data->pwm_num; i++) {
+ if (!(data->has_pwm & (1 << i)))
+ continue;
+
+ err = sysfs_create_group(&dev->kobj, &nct6775_group_pwm[i]);
+ if (err)
+ goto exit_remove;
+
+ if (data->REG_PWM[3]) {
+ err = device_create_file(dev,
+ &sda_pwm_max[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+ if (data->REG_PWM[4]) {
+ err = device_create_file(dev,
+ &sda_pwm_step[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+ if (data->REG_PWM[6]) {
+ err = device_create_file(dev,
+ &sda_weight_duty_base[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(sda_auto_pwm_arrays); i++) {
+ struct sensor_device_attribute_2 *attr =
+ &sda_auto_pwm_arrays[i];
+
+ if (!(data->has_pwm & (1 << attr->nr)))
+ continue;
+ if (attr->index > data->auto_pwm_num)
+ continue;
+ err = device_create_file(dev, &attr->dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+
+ for (i = 0; i < data->in_num; i++) {
+ if (!(data->have_in & (1 << i)))
+ continue;
+ err = sysfs_create_group(&dev->kobj, &nct6775_group_in[i]);
+ if (err)
+ goto exit_remove;
+ }
+
+ for (i = 0; i < 5; i++) {
+ if (data->has_fan & (1 << i)) {
+ err = device_create_file(dev,
+ &sda_fan_input[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ err = device_create_file(dev,
+ &sda_fan_alarm[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ if (data->kind != nct6776 &&
+ data->kind != nct6779) {
+ err = device_create_file(dev,
+ &sda_fan_div[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+ if (data->has_fan_min & (1 << i)) {
+ err = device_create_file(dev,
+ &sda_fan_min[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+ err = device_create_file(dev,
+ &sda_fan_pulses[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+ }
+
+ for (i = 0; i < NUM_TEMP; i++) {
+ if (!(data->have_temp & (1 << i)))
+ continue;
+ err = device_create_file(dev, &sda_temp_input[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ if (data->temp_label) {
+ err = device_create_file(dev,
+ &sda_temp_label[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+ if (data->reg_temp[1][i]) {
+ err = device_create_file(dev,
+ &sda_temp_max[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+ if (data->reg_temp[2][i]) {
+ err = device_create_file(dev,
+ &sda_temp_max_hyst[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+ if (data->reg_temp[3][i]) {
+ err = device_create_file(dev,
+ &sda_temp_crit[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+ if (!(data->have_temp_fixed & (1 << i)))
+ continue;
+ err = device_create_file(dev, &sda_temp_type[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ err = device_create_file(dev, &sda_temp_offset[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ if (i >= NUM_TEMP_ALARM ||
+ data->ALARM_BITS[TEMP_ALARM_BASE + i] < 0)
+ continue;
+ err = device_create_file(dev, &sda_temp_alarm[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sda_caseopen); i++) {
+ if (data->ALARM_BITS[INTRUSION_ALARM_BASE + i] < 0)
+ continue;
+ err = device_create_file(dev, &sda_caseopen[i].dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+
+ err = device_create_file(dev, &dev_attr_name);
+ if (err)
+ goto exit_remove;
+
+ data->hwmon_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove;
+ }
+
+ return 0;
+
+exit_remove:
+ nct6775_device_remove_files(dev);
+ return err;
+}
+
+static int nct6775_remove(struct platform_device *pdev)
+{
+ struct nct6775_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ nct6775_device_remove_files(&pdev->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int nct6775_suspend(struct device *dev)
+{
+ struct nct6775_data *data = nct6775_update_device(dev);
+ struct nct6775_sio_data *sio_data = dev->platform_data;
+
+ mutex_lock(&data->update_lock);
+ data->vbat = nct6775_read_value(data, data->REG_VBAT);
+ if (sio_data->kind == nct6775) {
+ data->fandiv1 = nct6775_read_value(data, NCT6775_REG_FANDIV1);
+ data->fandiv2 = nct6775_read_value(data, NCT6775_REG_FANDIV2);
+ }
+ mutex_unlock(&data->update_lock);
+
+ return 0;
+}
+
+static int nct6775_resume(struct device *dev)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct nct6775_sio_data *sio_data = dev->platform_data;
+ int i, j;
+
+ mutex_lock(&data->update_lock);
+ data->bank = 0xff; /* Force initial bank selection */
+
+ /* Restore limits */
+ for (i = 0; i < data->in_num; i++) {
+ if (!(data->have_in & (1 << i)))
+ continue;
+
+ nct6775_write_value(data, data->REG_IN_MINMAX[0][i],
+ data->in[i][1]);
+ nct6775_write_value(data, data->REG_IN_MINMAX[1][i],
+ data->in[i][2]);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) {
+ if (!(data->has_fan_min & (1 << i)))
+ continue;
+
+ nct6775_write_value(data, data->REG_FAN_MIN[i],
+ data->fan_min[i]);
+ }
+
+ for (i = 0; i < NUM_TEMP; i++) {
+ if (!(data->have_temp & (1 << i)))
+ continue;
+
+ for (j = 1; j < ARRAY_SIZE(data->reg_temp); j++)
+ if (data->reg_temp[j][i])
+ nct6775_write_temp(data, data->reg_temp[j][i],
+ data->temp[j][i]);
+ }
+
+ /* Restore other settings */
+ nct6775_write_value(data, data->REG_VBAT, data->vbat);
+ if (sio_data->kind == nct6775) {
+ nct6775_write_value(data, NCT6775_REG_FANDIV1, data->fandiv1);
+ nct6775_write_value(data, NCT6775_REG_FANDIV2, data->fandiv2);
+ }
+
+ /* Force re-reading all values */
+ data->valid = false;
+ mutex_unlock(&data->update_lock);
+
+ return 0;
+}
+
+static const struct dev_pm_ops nct6775_dev_pm_ops = {
+ .suspend = nct6775_suspend,
+ .resume = nct6775_resume,
+};
+
+#define NCT6775_DEV_PM_OPS (&nct6775_dev_pm_ops)
+#else
+#define NCT6775_DEV_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver nct6775_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRVNAME,
+ .pm = NCT6775_DEV_PM_OPS,
+ },
+ .probe = nct6775_probe,
+ .remove = nct6775_remove,
+};
+
+static const char * const nct6775_sio_names[] __initconst = {
+ "NCT6775F",
+ "NCT6776D/F",
+ "NCT6779D",
+};
+
+/* nct6775_find() looks for a '627 in the Super-I/O config space */
+static int __init nct6775_find(int sioaddr, unsigned short *addr,
+ struct nct6775_sio_data *sio_data)
+{
+ u16 val;
+ int err;
+
+ err = superio_enter(sioaddr);
+ if (err)
+ return err;
+
+ if (force_id)
+ val = force_id;
+ else
+ val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8)
+ | superio_inb(sioaddr, SIO_REG_DEVID + 1);
+ switch (val & SIO_ID_MASK) {
+ case SIO_NCT6775_ID:
+ sio_data->kind = nct6775;
+ break;
+ case SIO_NCT6776_ID:
+ sio_data->kind = nct6776;
+ break;
+ case SIO_NCT6779_ID:
+ sio_data->kind = nct6779;
+ break;
+ default:
+ if (val != 0xffff)
+ pr_debug("unsupported chip ID: 0x%04x\n", val);
+ superio_exit(sioaddr);
+ return -ENODEV;
+ }
+
+ /* We have a known chip, find the HWM I/O address */
+ superio_select(sioaddr, NCT6775_LD_HWM);
+ val = (superio_inb(sioaddr, SIO_REG_ADDR) << 8)
+ | superio_inb(sioaddr, SIO_REG_ADDR + 1);
+ *addr = val & IOREGION_ALIGNMENT;
+ if (*addr == 0) {
+ pr_err("Refusing to enable a Super-I/O device with a base I/O port 0\n");
+ superio_exit(sioaddr);
+ return -ENODEV;
+ }
+
+ /* Activate logical device if needed */
+ val = superio_inb(sioaddr, SIO_REG_ENABLE);
+ if (!(val & 0x01)) {
+ pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n");
+ superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01);
+ }
+
+ superio_exit(sioaddr);
+ pr_info("Found %s or compatible chip at %#x\n",
+ nct6775_sio_names[sio_data->kind], *addr);
+ sio_data->sioreg = sioaddr;
+
+ return 0;
+}
+
+/*
+ * when Super-I/O functions move to a separate file, the Super-I/O
+ * bus will manage the lifetime of the device and this module will only keep
+ * track of the nct6775 driver. But since we platform_device_alloc(), we
+ * must keep track of the device
+ */
+static struct platform_device *pdev;
+
+static int __init sensors_nct6775_init(void)
+{
+ int err;
+ unsigned short address;
+ struct resource res;
+ struct nct6775_sio_data sio_data;
+
+ /*
+ * initialize sio_data->kind and sio_data->sioreg.
+ *
+ * when Super-I/O functions move to a separate file, the Super-I/O
+ * driver will probe 0x2e and 0x4e and auto-detect the presence of a
+ * nct6775 hardware monitor, and call probe()
+ */
+ if (nct6775_find(0x2e, &address, &sio_data) &&
+ nct6775_find(0x4e, &address, &sio_data))
+ return -ENODEV;
+
+ err = platform_driver_register(&nct6775_driver);
+ if (err)
+ goto exit;
+
+ pdev = platform_device_alloc(DRVNAME, address);
+ if (!pdev) {
+ err = -ENOMEM;
+ pr_err("Device allocation failed\n");
+ goto exit_unregister;
+ }
+
+ err = platform_device_add_data(pdev, &sio_data,
+ sizeof(struct nct6775_sio_data));
+ if (err) {
+ pr_err("Platform data allocation failed\n");
+ goto exit_device_put;
+ }
+
+ memset(&res, 0, sizeof(res));
+ res.name = DRVNAME;
+ res.start = address + IOREGION_OFFSET;
+ res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1;
+ res.flags = IORESOURCE_IO;
+
+ err = acpi_check_resource_conflict(&res);
+ if (err)
+ goto exit_device_put;
+
+ err = platform_device_add_resources(pdev, &res, 1);
+ if (err) {
+ pr_err("Device resource addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ /* platform_device_add calls probe() */
+ err = platform_device_add(pdev);
+ if (err) {
+ pr_err("Device addition failed (%d)\n", err);
+ goto exit_device_put;
+ }
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(pdev);
+exit_unregister:
+ platform_driver_unregister(&nct6775_driver);
+exit:
+ return err;
+}
+
+static void __exit sensors_nct6775_exit(void)
+{
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&nct6775_driver);
+}
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("NCT6775F/NCT6776F/NCT6779D driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_nct6775_init);
+module_exit(sensors_nct6775_exit);
diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c
index b5f63f9c0ce1..d6d640a733d5 100644
--- a/drivers/hwmon/ntc_thermistor.c
+++ b/drivers/hwmon/ntc_thermistor.c
@@ -26,17 +26,33 @@
#include <linux/math64.h>
#include <linux/platform_device.h>
#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_data/ntc_thermistor.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/consumer.h>
+
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
struct ntc_compensation {
- int temp_C;
+ int temp_c;
unsigned int ohm;
};
+static const struct platform_device_id ntc_thermistor_id[] = {
+ { "ncp15wb473", TYPE_NCPXXWB473 },
+ { "ncp18wb473", TYPE_NCPXXWB473 },
+ { "ncp21wb473", TYPE_NCPXXWB473 },
+ { "ncp03wb473", TYPE_NCPXXWB473 },
+ { "ncp15wl333", TYPE_NCPXXWL333 },
+ { },
+};
+
/*
* A compensation table should be sorted by the values of .ohm
* in descending order.
@@ -44,76 +60,76 @@ struct ntc_compensation {
* Thermistors Datasheet
*/
static const struct ntc_compensation ncpXXwb473[] = {
- { .temp_C = -40, .ohm = 1747920 },
- { .temp_C = -35, .ohm = 1245428 },
- { .temp_C = -30, .ohm = 898485 },
- { .temp_C = -25, .ohm = 655802 },
- { .temp_C = -20, .ohm = 483954 },
- { .temp_C = -15, .ohm = 360850 },
- { .temp_C = -10, .ohm = 271697 },
- { .temp_C = -5, .ohm = 206463 },
- { .temp_C = 0, .ohm = 158214 },
- { .temp_C = 5, .ohm = 122259 },
- { .temp_C = 10, .ohm = 95227 },
- { .temp_C = 15, .ohm = 74730 },
- { .temp_C = 20, .ohm = 59065 },
- { .temp_C = 25, .ohm = 47000 },
- { .temp_C = 30, .ohm = 37643 },
- { .temp_C = 35, .ohm = 30334 },
- { .temp_C = 40, .ohm = 24591 },
- { .temp_C = 45, .ohm = 20048 },
- { .temp_C = 50, .ohm = 16433 },
- { .temp_C = 55, .ohm = 13539 },
- { .temp_C = 60, .ohm = 11209 },
- { .temp_C = 65, .ohm = 9328 },
- { .temp_C = 70, .ohm = 7798 },
- { .temp_C = 75, .ohm = 6544 },
- { .temp_C = 80, .ohm = 5518 },
- { .temp_C = 85, .ohm = 4674 },
- { .temp_C = 90, .ohm = 3972 },
- { .temp_C = 95, .ohm = 3388 },
- { .temp_C = 100, .ohm = 2902 },
- { .temp_C = 105, .ohm = 2494 },
- { .temp_C = 110, .ohm = 2150 },
- { .temp_C = 115, .ohm = 1860 },
- { .temp_C = 120, .ohm = 1615 },
- { .temp_C = 125, .ohm = 1406 },
+ { .temp_c = -40, .ohm = 1747920 },
+ { .temp_c = -35, .ohm = 1245428 },
+ { .temp_c = -30, .ohm = 898485 },
+ { .temp_c = -25, .ohm = 655802 },
+ { .temp_c = -20, .ohm = 483954 },
+ { .temp_c = -15, .ohm = 360850 },
+ { .temp_c = -10, .ohm = 271697 },
+ { .temp_c = -5, .ohm = 206463 },
+ { .temp_c = 0, .ohm = 158214 },
+ { .temp_c = 5, .ohm = 122259 },
+ { .temp_c = 10, .ohm = 95227 },
+ { .temp_c = 15, .ohm = 74730 },
+ { .temp_c = 20, .ohm = 59065 },
+ { .temp_c = 25, .ohm = 47000 },
+ { .temp_c = 30, .ohm = 37643 },
+ { .temp_c = 35, .ohm = 30334 },
+ { .temp_c = 40, .ohm = 24591 },
+ { .temp_c = 45, .ohm = 20048 },
+ { .temp_c = 50, .ohm = 16433 },
+ { .temp_c = 55, .ohm = 13539 },
+ { .temp_c = 60, .ohm = 11209 },
+ { .temp_c = 65, .ohm = 9328 },
+ { .temp_c = 70, .ohm = 7798 },
+ { .temp_c = 75, .ohm = 6544 },
+ { .temp_c = 80, .ohm = 5518 },
+ { .temp_c = 85, .ohm = 4674 },
+ { .temp_c = 90, .ohm = 3972 },
+ { .temp_c = 95, .ohm = 3388 },
+ { .temp_c = 100, .ohm = 2902 },
+ { .temp_c = 105, .ohm = 2494 },
+ { .temp_c = 110, .ohm = 2150 },
+ { .temp_c = 115, .ohm = 1860 },
+ { .temp_c = 120, .ohm = 1615 },
+ { .temp_c = 125, .ohm = 1406 },
};
static const struct ntc_compensation ncpXXwl333[] = {
- { .temp_C = -40, .ohm = 1610154 },
- { .temp_C = -35, .ohm = 1130850 },
- { .temp_C = -30, .ohm = 802609 },
- { .temp_C = -25, .ohm = 575385 },
- { .temp_C = -20, .ohm = 416464 },
- { .temp_C = -15, .ohm = 304219 },
- { .temp_C = -10, .ohm = 224193 },
- { .temp_C = -5, .ohm = 166623 },
- { .temp_C = 0, .ohm = 124850 },
- { .temp_C = 5, .ohm = 94287 },
- { .temp_C = 10, .ohm = 71747 },
- { .temp_C = 15, .ohm = 54996 },
- { .temp_C = 20, .ohm = 42455 },
- { .temp_C = 25, .ohm = 33000 },
- { .temp_C = 30, .ohm = 25822 },
- { .temp_C = 35, .ohm = 20335 },
- { .temp_C = 40, .ohm = 16115 },
- { .temp_C = 45, .ohm = 12849 },
- { .temp_C = 50, .ohm = 10306 },
- { .temp_C = 55, .ohm = 8314 },
- { .temp_C = 60, .ohm = 6746 },
- { .temp_C = 65, .ohm = 5503 },
- { .temp_C = 70, .ohm = 4513 },
- { .temp_C = 75, .ohm = 3721 },
- { .temp_C = 80, .ohm = 3084 },
- { .temp_C = 85, .ohm = 2569 },
- { .temp_C = 90, .ohm = 2151 },
- { .temp_C = 95, .ohm = 1809 },
- { .temp_C = 100, .ohm = 1529 },
- { .temp_C = 105, .ohm = 1299 },
- { .temp_C = 110, .ohm = 1108 },
- { .temp_C = 115, .ohm = 949 },
- { .temp_C = 120, .ohm = 817 },
- { .temp_C = 125, .ohm = 707 },
+ { .temp_c = -40, .ohm = 1610154 },
+ { .temp_c = -35, .ohm = 1130850 },
+ { .temp_c = -30, .ohm = 802609 },
+ { .temp_c = -25, .ohm = 575385 },
+ { .temp_c = -20, .ohm = 416464 },
+ { .temp_c = -15, .ohm = 304219 },
+ { .temp_c = -10, .ohm = 224193 },
+ { .temp_c = -5, .ohm = 166623 },
+ { .temp_c = 0, .ohm = 124850 },
+ { .temp_c = 5, .ohm = 94287 },
+ { .temp_c = 10, .ohm = 71747 },
+ { .temp_c = 15, .ohm = 54996 },
+ { .temp_c = 20, .ohm = 42455 },
+ { .temp_c = 25, .ohm = 33000 },
+ { .temp_c = 30, .ohm = 25822 },
+ { .temp_c = 35, .ohm = 20335 },
+ { .temp_c = 40, .ohm = 16115 },
+ { .temp_c = 45, .ohm = 12849 },
+ { .temp_c = 50, .ohm = 10306 },
+ { .temp_c = 55, .ohm = 8314 },
+ { .temp_c = 60, .ohm = 6746 },
+ { .temp_c = 65, .ohm = 5503 },
+ { .temp_c = 70, .ohm = 4513 },
+ { .temp_c = 75, .ohm = 3721 },
+ { .temp_c = 80, .ohm = 3084 },
+ { .temp_c = 85, .ohm = 2569 },
+ { .temp_c = 90, .ohm = 2151 },
+ { .temp_c = 95, .ohm = 1809 },
+ { .temp_c = 100, .ohm = 1529 },
+ { .temp_c = 105, .ohm = 1299 },
+ { .temp_c = 110, .ohm = 1108 },
+ { .temp_c = 115, .ohm = 949 },
+ { .temp_c = 120, .ohm = 817 },
+ { .temp_c = 125, .ohm = 707 },
};
struct ntc_data {
@@ -125,6 +141,92 @@ struct ntc_data {
char name[PLATFORM_NAME_SIZE];
};
+#ifdef CONFIG_OF
+static int ntc_adc_iio_read(struct ntc_thermistor_platform_data *pdata)
+{
+ struct iio_channel *channel = pdata->chan;
+ unsigned int result;
+ int val, ret;
+
+ ret = iio_read_channel_raw(channel, &val);
+ if (ret < 0) {
+ pr_err("read channel() error: %d\n", ret);
+ return ret;
+ }
+
+ /* unit: mV */
+ result = pdata->pullup_uv * val;
+ result >>= 12;
+
+ return result;
+}
+
+static const struct of_device_id ntc_match[] = {
+ { .compatible = "ntc,ncp15wb473",
+ .data = &ntc_thermistor_id[TYPE_NCPXXWB473] },
+ { .compatible = "ntc,ncp18wb473",
+ .data = &ntc_thermistor_id[TYPE_NCPXXWB473] },
+ { .compatible = "ntc,ncp21wb473",
+ .data = &ntc_thermistor_id[TYPE_NCPXXWB473] },
+ { .compatible = "ntc,ncp03wb473",
+ .data = &ntc_thermistor_id[TYPE_NCPXXWB473] },
+ { .compatible = "ntc,ncp15wl333",
+ .data = &ntc_thermistor_id[TYPE_NCPXXWL333] },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ntc_match);
+
+static struct ntc_thermistor_platform_data *
+ntc_thermistor_parse_dt(struct platform_device *pdev)
+{
+ struct iio_channel *chan;
+ struct device_node *np = pdev->dev.of_node;
+ struct ntc_thermistor_platform_data *pdata;
+
+ if (!np)
+ return NULL;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ chan = iio_channel_get(&pdev->dev, NULL);
+ if (IS_ERR(chan))
+ return ERR_CAST(chan);
+
+ if (of_property_read_u32(np, "pullup-uv", &pdata->pullup_uv))
+ return ERR_PTR(-ENODEV);
+ if (of_property_read_u32(np, "pullup-ohm", &pdata->pullup_ohm))
+ return ERR_PTR(-ENODEV);
+ if (of_property_read_u32(np, "pulldown-ohm", &pdata->pulldown_ohm))
+ return ERR_PTR(-ENODEV);
+
+ if (of_find_property(np, "connected-positive", NULL))
+ pdata->connect = NTC_CONNECTED_POSITIVE;
+ else /* status change should be possible if not always on. */
+ pdata->connect = NTC_CONNECTED_GROUND;
+
+ pdata->chan = chan;
+ pdata->read_uv = ntc_adc_iio_read;
+
+ return pdata;
+}
+static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata)
+{
+ if (pdata->chan)
+ iio_channel_release(pdata->chan);
+}
+#else
+static struct ntc_thermistor_platform_data *
+ntc_thermistor_parse_dt(struct platform_device *pdev)
+{
+ return NULL;
+}
+
+static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata)
+{ }
+#endif
+
static inline u64 div64_u64_safe(u64 dividend, u64 divisor)
{
if (divisor == 0 && dividend == 0)
@@ -134,37 +236,37 @@ static inline u64 div64_u64_safe(u64 dividend, u64 divisor)
return div64_u64(dividend, divisor);
}
-static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uV)
+static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uv)
{
struct ntc_thermistor_platform_data *pdata = data->pdata;
- u64 mV = uV / 1000;
- u64 pmV = pdata->pullup_uV / 1000;
- u64 N, puO, pdO;
- puO = pdata->pullup_ohm;
- pdO = pdata->pulldown_ohm;
+ u64 mv = uv / 1000;
+ u64 pmv = pdata->pullup_uv / 1000;
+ u64 n, puo, pdo;
+ puo = pdata->pullup_ohm;
+ pdo = pdata->pulldown_ohm;
- if (mV == 0) {
+ if (mv == 0) {
if (pdata->connect == NTC_CONNECTED_POSITIVE)
return INT_MAX;
return 0;
}
- if (mV >= pmV)
+ if (mv >= pmv)
return (pdata->connect == NTC_CONNECTED_POSITIVE) ?
0 : INT_MAX;
- if (pdata->connect == NTC_CONNECTED_POSITIVE && puO == 0)
- N = div64_u64_safe(pdO * (pmV - mV), mV);
- else if (pdata->connect == NTC_CONNECTED_GROUND && pdO == 0)
- N = div64_u64_safe(puO * mV, pmV - mV);
+ if (pdata->connect == NTC_CONNECTED_POSITIVE && puo == 0)
+ n = div64_u64_safe(pdo * (pmv - mv), mv);
+ else if (pdata->connect == NTC_CONNECTED_GROUND && pdo == 0)
+ n = div64_u64_safe(puo * mv, pmv - mv);
else if (pdata->connect == NTC_CONNECTED_POSITIVE)
- N = div64_u64_safe(pdO * puO * (pmV - mV),
- puO * mV - pdO * (pmV - mV));
+ n = div64_u64_safe(pdo * puo * (pmv - mv),
+ puo * mv - pdo * (pmv - mv));
else
- N = div64_u64_safe(pdO * puO * mV, pdO * (pmV - mV) - puO * mV);
+ n = div64_u64_safe(pdo * puo * mv, pdo * (pmv - mv) - puo * mv);
- if (N > INT_MAX)
- N = INT_MAX;
- return N;
+ if (n > INT_MAX)
+ n = INT_MAX;
+ return n;
}
static void lookup_comp(struct ntc_data *data, unsigned int ohm,
@@ -233,7 +335,7 @@ static void lookup_comp(struct ntc_data *data, unsigned int ohm,
*i_high = end - 1;
}
-static int get_temp_mC(struct ntc_data *data, unsigned int ohm)
+static int get_temp_mc(struct ntc_data *data, unsigned int ohm)
{
int low, high;
int temp;
@@ -241,10 +343,10 @@ static int get_temp_mC(struct ntc_data *data, unsigned int ohm)
lookup_comp(data, ohm, &low, &high);
if (low == high) {
/* Unable to use linear approximation */
- temp = data->comp[low].temp_C * 1000;
+ temp = data->comp[low].temp_c * 1000;
} else {
- temp = data->comp[low].temp_C * 1000 +
- ((data->comp[high].temp_C - data->comp[low].temp_C) *
+ temp = data->comp[low].temp_c * 1000 +
+ ((data->comp[high].temp_c - data->comp[low].temp_c) *
1000 * ((int)ohm - (int)data->comp[low].ohm)) /
((int)data->comp[high].ohm - (int)data->comp[low].ohm);
}
@@ -253,16 +355,16 @@ static int get_temp_mC(struct ntc_data *data, unsigned int ohm)
static int ntc_thermistor_get_ohm(struct ntc_data *data)
{
- int read_uV;
+ int read_uv;
if (data->pdata->read_ohm)
return data->pdata->read_ohm();
- if (data->pdata->read_uV) {
- read_uV = data->pdata->read_uV();
- if (read_uV < 0)
- return read_uV;
- return get_ohm_of_thermistor(data, read_uV);
+ if (data->pdata->read_uv) {
+ read_uv = data->pdata->read_uv(data->pdata);
+ if (read_uv < 0)
+ return read_uv;
+ return get_ohm_of_thermistor(data, read_uv);
}
return -EINVAL;
}
@@ -291,7 +393,7 @@ static ssize_t ntc_show_temp(struct device *dev,
if (ohm < 0)
return ohm;
- return sprintf(buf, "%d\n", get_temp_mC(data, ohm));
+ return sprintf(buf, "%d\n", get_temp_mc(data, ohm));
}
static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0);
@@ -311,9 +413,18 @@ static const struct attribute_group ntc_attr_group = {
static int ntc_thermistor_probe(struct platform_device *pdev)
{
+ const struct of_device_id *of_id =
+ of_match_device(of_match_ptr(ntc_match), &pdev->dev);
+ const struct platform_device_id *pdev_id;
+ struct ntc_thermistor_platform_data *pdata;
struct ntc_data *data;
- struct ntc_thermistor_platform_data *pdata = pdev->dev.platform_data;
- int ret = 0;
+ int ret;
+
+ pdata = ntc_thermistor_parse_dt(pdev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ else if (pdata == NULL)
+ pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "No platform init data supplied.\n");
@@ -321,19 +432,19 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
}
/* Either one of the two is required. */
- if (!pdata->read_uV && !pdata->read_ohm) {
+ if (!pdata->read_uv && !pdata->read_ohm) {
dev_err(&pdev->dev,
- "Both read_uV and read_ohm missing. Need either one of the two.\n");
+ "Both read_uv and read_ohm missing. Need either one of the two.\n");
return -EINVAL;
}
- if (pdata->read_uV && pdata->read_ohm) {
+ if (pdata->read_uv && pdata->read_ohm) {
dev_warn(&pdev->dev,
- "Only one of read_uV and read_ohm is needed; ignoring read_uV.\n");
- pdata->read_uV = NULL;
+ "Only one of read_uv and read_ohm is needed; ignoring read_uv.\n");
+ pdata->read_uv = NULL;
}
- if (pdata->read_uV && (pdata->pullup_uV == 0 ||
+ if (pdata->read_uv && (pdata->pullup_uv == 0 ||
(pdata->pullup_ohm == 0 && pdata->connect ==
NTC_CONNECTED_GROUND) ||
(pdata->pulldown_ohm == 0 && pdata->connect ==
@@ -341,7 +452,7 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
(pdata->connect != NTC_CONNECTED_POSITIVE &&
pdata->connect != NTC_CONNECTED_GROUND))) {
dev_err(&pdev->dev,
- "Required data to use read_uV not supplied.\n");
+ "Required data to use read_uv not supplied.\n");
return -EINVAL;
}
@@ -349,11 +460,13 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
if (!data)
return -ENOMEM;
+ pdev_id = of_id ? of_id->data : platform_get_device_id(pdev);
+
data->dev = &pdev->dev;
data->pdata = pdata;
- strlcpy(data->name, pdev->id_entry->name, sizeof(data->name));
+ strlcpy(data->name, pdev_id->name, sizeof(data->name));
- switch (pdev->id_entry->driver_data) {
+ switch (pdev_id->driver_data) {
case TYPE_NCPXXWB473:
data->comp = ncpXXwb473;
data->n_comp = ARRAY_SIZE(ncpXXwb473);
@@ -364,8 +477,7 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
break;
default:
dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n",
- pdev->id_entry->driver_data,
- pdev->id_entry->name);
+ pdev_id->driver_data, pdev_id->name);
return -EINVAL;
}
@@ -384,39 +496,34 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
goto err_after_sysfs;
}
- dev_info(&pdev->dev, "Thermistor %s:%d (type: %s/%lu) successfully probed.\n",
- pdev->name, pdev->id, pdev->id_entry->name,
- pdev->id_entry->driver_data);
+ dev_info(&pdev->dev, "Thermistor type: %s successfully probed.\n",
+ pdev->name);
+
return 0;
err_after_sysfs:
sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
+ ntc_iio_channel_release(pdata);
return ret;
}
static int ntc_thermistor_remove(struct platform_device *pdev)
{
struct ntc_data *data = platform_get_drvdata(pdev);
+ struct ntc_thermistor_platform_data *pdata = data->pdata;
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
+ ntc_iio_channel_release(pdata);
platform_set_drvdata(pdev, NULL);
return 0;
}
-static const struct platform_device_id ntc_thermistor_id[] = {
- { "ncp15wb473", TYPE_NCPXXWB473 },
- { "ncp18wb473", TYPE_NCPXXWB473 },
- { "ncp21wb473", TYPE_NCPXXWB473 },
- { "ncp03wb473", TYPE_NCPXXWB473 },
- { "ncp15wl333", TYPE_NCPXXWL333 },
- { },
-};
-
static struct platform_driver ntc_thermistor_driver = {
.driver = {
.name = "ntc-thermistor",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(ntc_match),
},
.probe = ntc_thermistor_probe,
.remove = ntc_thermistor_remove,
diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c
index e35856bb79b4..aa615ba73d4b 100644
--- a/drivers/hwmon/pc87360.c
+++ b/drivers/hwmon/pc87360.c
@@ -1190,8 +1190,7 @@ static int __init pc87360_find(int sioaddr, u8 *devid,
confreg[3] = superio_inb(sioaddr, 0x25);
if (confreg[2] & 0x40) {
- pr_info("Using thermistors for "
- "temperature monitoring\n");
+ pr_info("Using thermistors for temperature monitoring\n");
}
if (confreg[3] & 0xE0) {
pr_info("VID inputs routed (mode %u)\n",
@@ -1271,9 +1270,9 @@ static int pc87360_probe(struct platform_device *pdev)
if (data->address[i]
&& !devm_request_region(dev, extra_isa[i], PC87360_EXTENT,
pc87360_driver.driver.name)) {
- dev_err(dev, "Region 0x%x-0x%x already "
- "in use!\n", extra_isa[i],
- extra_isa[i]+PC87360_EXTENT-1);
+ dev_err(dev,
+ "Region 0x%x-0x%x already in use!\n",
+ extra_isa[i], extra_isa[i]+PC87360_EXTENT-1);
return -EBUSY;
}
}
@@ -1435,8 +1434,8 @@ static void pc87360_init_device(struct platform_device *pdev,
if (init >= 2 && data->innr) {
reg = pc87360_read_value(data, LD_IN, NO_BANK,
PC87365_REG_IN_CONVRATE);
- dev_info(&pdev->dev, "VLM conversion set to "
- "1s period, 160us delay\n");
+ dev_info(&pdev->dev,
+ "VLM conversion set to 1s period, 160us delay\n");
pc87360_write_value(data, LD_IN, NO_BANK,
PC87365_REG_IN_CONVRATE,
(reg & 0xC0) | 0x11);
@@ -1450,8 +1449,8 @@ static void pc87360_init_device(struct platform_device *pdev,
if (init >= init_in[i]) {
/* Forcibly enable voltage channel */
if (!(reg & CHAN_ENA)) {
- dev_dbg(&pdev->dev, "Forcibly "
- "enabling in%d\n", i);
+ dev_dbg(&pdev->dev, "Forcibly enabling in%d\n",
+ i);
pc87360_write_value(data, LD_IN, i,
PC87365_REG_IN_STATUS,
(reg & 0x68) | 0x87);
@@ -1575,8 +1574,8 @@ static void pc87360_autodiv(struct device *dev, int nr)
data->fan_status[nr] += 0x20;
data->fan_min[nr] >>= 1;
data->fan[nr] >>= 1;
- dev_dbg(dev, "Increasing "
- "clock divider to %d for fan %d\n",
+ dev_dbg(dev,
+ "Increasing clock divider to %d for fan %d\n",
FAN_DIV_FROM_REG(data->fan_status[nr]), nr + 1);
}
} else {
@@ -1587,8 +1586,8 @@ static void pc87360_autodiv(struct device *dev, int nr)
data->fan_status[nr] -= 0x20;
data->fan_min[nr] <<= 1;
data->fan[nr] <<= 1;
- dev_dbg(dev, "Decreasing "
- "clock divider to %d for fan %d\n",
+ dev_dbg(dev,
+ "Decreasing clock divider to %d for fan %d\n",
FAN_DIV_FROM_REG(data->fan_status[nr]),
nr + 1);
}
diff --git a/drivers/hwmon/pc87427.c b/drivers/hwmon/pc87427.c
index 6086ad039d7d..ea606860d2b2 100644
--- a/drivers/hwmon/pc87427.c
+++ b/drivers/hwmon/pc87427.c
@@ -627,8 +627,9 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute
pc87427_readall_pwm(data, nr);
mode = data->pwm_enable[nr] & PWM_ENABLE_MODE_MASK;
if (mode != PWM_MODE_MANUAL && mode != PWM_MODE_OFF) {
- dev_notice(dev, "Can't set PWM%d duty cycle while not in "
- "manual mode\n", nr + 1);
+ dev_notice(dev,
+ "Can't set PWM%d duty cycle while not in manual mode\n",
+ nr + 1);
mutex_unlock(&data->lock);
return -EPERM;
}
@@ -1245,16 +1246,16 @@ static int __init pc87427_find(int sioaddr, struct pc87427_sio_data *sio_data)
val = superio_inb(sioaddr, SIOREG_MAP);
if (val & 0x01) {
- pr_warn("Logical device 0x%02x is memory-mapped, "
- "can't use\n", logdev[i]);
+ pr_warn("Logical device 0x%02x is memory-mapped, can't use\n",
+ logdev[i]);
continue;
}
val = (superio_inb(sioaddr, SIOREG_IOBASE) << 8)
| superio_inb(sioaddr, SIOREG_IOBASE + 1);
if (!val) {
- pr_info("I/O base address not set for logical device "
- "0x%02x\n", logdev[i]);
+ pr_info("I/O base address not set for logical device 0x%02x\n",
+ logdev[i]);
continue;
}
sio_data->address[i] = val;
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index 4f9eb0af5229..39cc63edfbb0 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -42,17 +42,17 @@ config SENSORS_LM25066
default n
help
If you say yes here you get hardware monitoring support for National
- Semiconductor LM25066, LM5064, and LM5066.
+ Semiconductor LM25056, LM25066, LM5064, and LM5066.
This driver can also be built as a module. If so, the module will
be called lm25066.
config SENSORS_LTC2978
- tristate "Linear Technologies LTC2978 and LTC3880"
+ tristate "Linear Technologies LTC2974, LTC2978, LTC3880, and LTC3883"
default n
help
If you say yes here you get hardware monitoring support for Linear
- Technology LTC2978 and LTC3880.
+ Technology LTC2974, LTC2978, LTC3880, and LTC3883.
This driver can also be built as a module. If so, the module will
be called ltc2978.
diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c
index c299392716af..6a9d6edaacb3 100644
--- a/drivers/hwmon/pmbus/lm25066.c
+++ b/drivers/hwmon/pmbus/lm25066.c
@@ -1,7 +1,8 @@
/*
- * Hardware monitoring driver for LM25066 / LM5064 / LM5066
+ * Hardware monitoring driver for LM25056 / LM25066 / LM5064 / LM5066
*
* Copyright (c) 2011 Ericsson AB.
+ * Copyright (c) 2013 Guenter Roeck
*
* 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
@@ -26,7 +27,7 @@
#include <linux/i2c.h>
#include "pmbus.h"
-enum chips { lm25066, lm5064, lm5066 };
+enum chips { lm25056, lm25066, lm5064, lm5066 };
#define LM25066_READ_VAUX 0xd0
#define LM25066_MFR_READ_IIN 0xd1
@@ -43,6 +44,138 @@ enum chips { lm25066, lm5064, lm5066 };
#define LM25066_DEV_SETUP_CL (1 << 4) /* Current limit */
+/* LM25056 only */
+
+#define LM25056_VAUX_OV_WARN_LIMIT 0xe3
+#define LM25056_VAUX_UV_WARN_LIMIT 0xe4
+
+#define LM25056_MFR_STS_VAUX_OV_WARN (1 << 1)
+#define LM25056_MFR_STS_VAUX_UV_WARN (1 << 0)
+
+struct __coeff {
+ short m, b, R;
+};
+
+#define PSC_CURRENT_IN_L (PSC_NUM_CLASSES)
+#define PSC_POWER_L (PSC_NUM_CLASSES + 1)
+
+static struct __coeff lm25066_coeff[4][PSC_NUM_CLASSES + 2] = {
+ [lm25056] = {
+ [PSC_VOLTAGE_IN] = {
+ .m = 16296,
+ .R = -2,
+ },
+ [PSC_CURRENT_IN] = {
+ .m = 13797,
+ .R = -2,
+ },
+ [PSC_CURRENT_IN_L] = {
+ .m = 6726,
+ .R = -2,
+ },
+ [PSC_POWER] = {
+ .m = 5501,
+ .R = -3,
+ },
+ [PSC_POWER_L] = {
+ .m = 26882,
+ .R = -4,
+ },
+ [PSC_TEMPERATURE] = {
+ .m = 1580,
+ .b = -14500,
+ .R = -2,
+ },
+ },
+ [lm25066] = {
+ [PSC_VOLTAGE_IN] = {
+ .m = 22070,
+ .R = -2,
+ },
+ [PSC_VOLTAGE_OUT] = {
+ .m = 22070,
+ .R = -2,
+ },
+ [PSC_CURRENT_IN] = {
+ .m = 13661,
+ .R = -2,
+ },
+ [PSC_CURRENT_IN_L] = {
+ .m = 6852,
+ .R = -2,
+ },
+ [PSC_POWER] = {
+ .m = 736,
+ .R = -2,
+ },
+ [PSC_POWER_L] = {
+ .m = 369,
+ .R = -2,
+ },
+ [PSC_TEMPERATURE] = {
+ .m = 16,
+ },
+ },
+ [lm5064] = {
+ [PSC_VOLTAGE_IN] = {
+ .m = 4611,
+ .R = -2,
+ },
+ [PSC_VOLTAGE_OUT] = {
+ .m = 4621,
+ .R = -2,
+ },
+ [PSC_CURRENT_IN] = {
+ .m = 10742,
+ .R = -2,
+ },
+ [PSC_CURRENT_IN_L] = {
+ .m = 5456,
+ .R = -2,
+ },
+ [PSC_POWER] = {
+ .m = 1204,
+ .R = -3,
+ },
+ [PSC_POWER_L] = {
+ .m = 612,
+ .R = -3,
+ },
+ [PSC_TEMPERATURE] = {
+ .m = 16,
+ },
+ },
+ [lm5066] = {
+ [PSC_VOLTAGE_IN] = {
+ .m = 4587,
+ .R = -2,
+ },
+ [PSC_VOLTAGE_OUT] = {
+ .m = 4587,
+ .R = -2,
+ },
+ [PSC_CURRENT_IN] = {
+ .m = 10753,
+ .R = -2,
+ },
+ [PSC_CURRENT_IN_L] = {
+ .m = 5405,
+ .R = -2,
+ },
+ [PSC_POWER] = {
+ .m = 1204,
+ .R = -3,
+ },
+ [PSC_POWER_L] = {
+ .m = 605,
+ .R = -3,
+ },
+ [PSC_TEMPERATURE] = {
+ .m = 16,
+ },
+ },
+};
+
struct lm25066_data {
int id;
struct pmbus_driver_info info;
@@ -56,42 +189,31 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg)
const struct lm25066_data *data = to_lm25066_data(info);
int ret;
- if (page > 1)
- return -ENXIO;
-
- /* Map READ_VAUX into READ_VOUT register on page 1 */
- if (page == 1) {
- switch (reg) {
- case PMBUS_READ_VOUT:
- ret = pmbus_read_word_data(client, 0,
- LM25066_READ_VAUX);
- if (ret < 0)
- break;
- /* Adjust returned value to match VOUT coefficients */
- switch (data->id) {
- case lm25066:
- /* VOUT: 4.54 mV VAUX: 283.2 uV LSB */
- ret = DIV_ROUND_CLOSEST(ret * 2832, 45400);
- break;
- case lm5064:
- /* VOUT: 4.53 mV VAUX: 700 uV LSB */
- ret = DIV_ROUND_CLOSEST(ret * 70, 453);
- break;
- case lm5066:
- /* VOUT: 2.18 mV VAUX: 725 uV LSB */
- ret = DIV_ROUND_CLOSEST(ret * 725, 2180);
- break;
- }
+ switch (reg) {
+ case PMBUS_VIRT_READ_VMON:
+ ret = pmbus_read_word_data(client, 0, LM25066_READ_VAUX);
+ if (ret < 0)
+ break;
+ /* Adjust returned value to match VIN coefficients */
+ switch (data->id) {
+ case lm25056:
+ /* VIN: 6.14 mV VAUX: 293 uV LSB */
+ ret = DIV_ROUND_CLOSEST(ret * 293, 6140);
break;
- default:
- /* No other valid registers on page 1 */
- ret = -ENXIO;
+ case lm25066:
+ /* VIN: 4.54 mV VAUX: 283.2 uV LSB */
+ ret = DIV_ROUND_CLOSEST(ret * 2832, 45400);
+ break;
+ case lm5064:
+ /* VIN: 4.53 mV VAUX: 700 uV LSB */
+ ret = DIV_ROUND_CLOSEST(ret * 70, 453);
+ break;
+ case lm5066:
+ /* VIN: 2.18 mV VAUX: 725 uV LSB */
+ ret = DIV_ROUND_CLOSEST(ret * 725, 2180);
break;
}
- goto done;
- }
-
- switch (reg) {
+ break;
case PMBUS_READ_IIN:
ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_IIN);
break;
@@ -128,7 +250,58 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg)
ret = -ENODATA;
break;
}
-done:
+ return ret;
+}
+
+static int lm25056_read_word_data(struct i2c_client *client, int page, int reg)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
+ ret = pmbus_read_word_data(client, 0,
+ LM25056_VAUX_UV_WARN_LIMIT);
+ if (ret < 0)
+ break;
+ /* Adjust returned value to match VIN coefficients */
+ ret = DIV_ROUND_CLOSEST(ret * 293, 6140);
+ break;
+ case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
+ ret = pmbus_read_word_data(client, 0,
+ LM25056_VAUX_OV_WARN_LIMIT);
+ if (ret < 0)
+ break;
+ /* Adjust returned value to match VIN coefficients */
+ ret = DIV_ROUND_CLOSEST(ret * 293, 6140);
+ break;
+ default:
+ ret = lm25066_read_word_data(client, page, reg);
+ break;
+ }
+ return ret;
+}
+
+static int lm25056_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ int ret, s;
+
+ switch (reg) {
+ case PMBUS_VIRT_STATUS_VMON:
+ ret = pmbus_read_byte_data(client, 0,
+ PMBUS_STATUS_MFR_SPECIFIC);
+ if (ret < 0)
+ break;
+ s = 0;
+ if (ret & LM25056_MFR_STS_VAUX_UV_WARN)
+ s |= PB_VOLTAGE_UV_WARNING;
+ if (ret & LM25056_MFR_STS_VAUX_OV_WARN)
+ s |= PB_VOLTAGE_OV_WARNING;
+ ret = s;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
return ret;
}
@@ -137,19 +310,45 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg,
{
int ret;
- if (page > 1)
- return -ENXIO;
-
switch (reg) {
+ case PMBUS_VOUT_UV_WARN_LIMIT:
+ case PMBUS_OT_FAULT_LIMIT:
+ case PMBUS_OT_WARN_LIMIT:
+ case PMBUS_VIN_UV_WARN_LIMIT:
+ case PMBUS_VIN_OV_WARN_LIMIT:
+ word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff);
+ ret = pmbus_write_word_data(client, 0, reg, word);
+ pmbus_clear_cache(client);
+ break;
case PMBUS_IIN_OC_WARN_LIMIT:
+ word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff);
ret = pmbus_write_word_data(client, 0,
LM25066_MFR_IIN_OC_WARN_LIMIT,
word);
+ pmbus_clear_cache(client);
break;
case PMBUS_PIN_OP_WARN_LIMIT:
+ word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff);
ret = pmbus_write_word_data(client, 0,
LM25066_MFR_PIN_OP_WARN_LIMIT,
word);
+ pmbus_clear_cache(client);
+ break;
+ case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
+ /* Adjust from VIN coefficients (for LM25056) */
+ word = DIV_ROUND_CLOSEST((int)word * 6140, 293);
+ word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff);
+ ret = pmbus_write_word_data(client, 0,
+ LM25056_VAUX_UV_WARN_LIMIT, word);
+ pmbus_clear_cache(client);
+ break;
+ case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
+ /* Adjust from VIN coefficients (for LM25056) */
+ word = DIV_ROUND_CLOSEST((int)word * 6140, 293);
+ word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff);
+ ret = pmbus_write_word_data(client, 0,
+ LM25056_VAUX_OV_WARN_LIMIT, word);
+ pmbus_clear_cache(client);
break;
case PMBUS_VIRT_RESET_PIN_HISTORY:
ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK);
@@ -161,23 +360,13 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg,
return ret;
}
-static int lm25066_write_byte(struct i2c_client *client, int page, u8 value)
-{
- if (page > 1)
- return -ENXIO;
-
- if (page <= 0)
- return pmbus_write_byte(client, page, value);
-
- return 0;
-}
-
static int lm25066_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int config;
struct lm25066_data *data;
struct pmbus_driver_info *info;
+ struct __coeff *coeff;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_BYTE_DATA))
@@ -195,107 +384,54 @@ static int lm25066_probe(struct i2c_client *client,
data->id = id->driver_data;
info = &data->info;
- info->pages = 2;
+ info->pages = 1;
info->format[PSC_VOLTAGE_IN] = direct;
info->format[PSC_VOLTAGE_OUT] = direct;
info->format[PSC_CURRENT_IN] = direct;
info->format[PSC_TEMPERATURE] = direct;
info->format[PSC_POWER] = direct;
- info->m[PSC_TEMPERATURE] = 16;
- info->b[PSC_TEMPERATURE] = 0;
- info->R[PSC_TEMPERATURE] = 0;
-
- info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT
- | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_PIN | PMBUS_HAVE_IIN
- | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
- info->func[1] = PMBUS_HAVE_VOUT;
-
- info->read_word_data = lm25066_read_word_data;
+ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VMON
+ | PMBUS_HAVE_PIN | PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT
+ | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+
+ if (data->id == lm25056) {
+ info->func[0] |= PMBUS_HAVE_STATUS_VMON;
+ info->read_word_data = lm25056_read_word_data;
+ info->read_byte_data = lm25056_read_byte_data;
+ } else {
+ info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+ info->read_word_data = lm25066_read_word_data;
+ }
info->write_word_data = lm25066_write_word_data;
- info->write_byte = lm25066_write_byte;
-
- switch (id->driver_data) {
- case lm25066:
- info->m[PSC_VOLTAGE_IN] = 22070;
- info->b[PSC_VOLTAGE_IN] = 0;
- info->R[PSC_VOLTAGE_IN] = -2;
- info->m[PSC_VOLTAGE_OUT] = 22070;
- info->b[PSC_VOLTAGE_OUT] = 0;
- info->R[PSC_VOLTAGE_OUT] = -2;
-
- if (config & LM25066_DEV_SETUP_CL) {
- info->m[PSC_CURRENT_IN] = 6852;
- info->b[PSC_CURRENT_IN] = 0;
- info->R[PSC_CURRENT_IN] = -2;
- info->m[PSC_POWER] = 369;
- info->b[PSC_POWER] = 0;
- info->R[PSC_POWER] = -2;
- } else {
- info->m[PSC_CURRENT_IN] = 13661;
- info->b[PSC_CURRENT_IN] = 0;
- info->R[PSC_CURRENT_IN] = -2;
- info->m[PSC_POWER] = 736;
- info->b[PSC_POWER] = 0;
- info->R[PSC_POWER] = -2;
- }
- break;
- case lm5064:
- info->m[PSC_VOLTAGE_IN] = 22075;
- info->b[PSC_VOLTAGE_IN] = 0;
- info->R[PSC_VOLTAGE_IN] = -2;
- info->m[PSC_VOLTAGE_OUT] = 22075;
- info->b[PSC_VOLTAGE_OUT] = 0;
- info->R[PSC_VOLTAGE_OUT] = -2;
-
- if (config & LM25066_DEV_SETUP_CL) {
- info->m[PSC_CURRENT_IN] = 6713;
- info->b[PSC_CURRENT_IN] = 0;
- info->R[PSC_CURRENT_IN] = -2;
- info->m[PSC_POWER] = 3619;
- info->b[PSC_POWER] = 0;
- info->R[PSC_POWER] = -3;
- } else {
- info->m[PSC_CURRENT_IN] = 13426;
- info->b[PSC_CURRENT_IN] = 0;
- info->R[PSC_CURRENT_IN] = -2;
- info->m[PSC_POWER] = 7238;
- info->b[PSC_POWER] = 0;
- info->R[PSC_POWER] = -3;
- }
- break;
- case lm5066:
- info->m[PSC_VOLTAGE_IN] = 4587;
- info->b[PSC_VOLTAGE_IN] = 0;
- info->R[PSC_VOLTAGE_IN] = -2;
- info->m[PSC_VOLTAGE_OUT] = 4587;
- info->b[PSC_VOLTAGE_OUT] = 0;
- info->R[PSC_VOLTAGE_OUT] = -2;
-
- if (config & LM25066_DEV_SETUP_CL) {
- info->m[PSC_CURRENT_IN] = 10753;
- info->b[PSC_CURRENT_IN] = 0;
- info->R[PSC_CURRENT_IN] = -2;
- info->m[PSC_POWER] = 1204;
- info->b[PSC_POWER] = 0;
- info->R[PSC_POWER] = -3;
- } else {
- info->m[PSC_CURRENT_IN] = 5405;
- info->b[PSC_CURRENT_IN] = 0;
- info->R[PSC_CURRENT_IN] = -2;
- info->m[PSC_POWER] = 605;
- info->b[PSC_POWER] = 0;
- info->R[PSC_POWER] = -3;
- }
- break;
- default:
- return -ENODEV;
+
+ coeff = &lm25066_coeff[data->id][0];
+ info->m[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].m;
+ info->b[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].b;
+ info->R[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].R;
+ info->m[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].m;
+ info->b[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].b;
+ info->R[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].R;
+ info->m[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].m;
+ info->b[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].b;
+ info->R[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].R;
+ info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].b;
+ info->R[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].R;
+ info->b[PSC_POWER] = coeff[PSC_POWER].b;
+ info->R[PSC_POWER] = coeff[PSC_POWER].R;
+ if (config & LM25066_DEV_SETUP_CL) {
+ info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].m;
+ info->m[PSC_POWER] = coeff[PSC_POWER_L].m;
+ } else {
+ info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].m;
+ info->m[PSC_POWER] = coeff[PSC_POWER].m;
}
return pmbus_do_probe(client, id, info);
}
static const struct i2c_device_id lm25066_id[] = {
+ {"lm25056", lm25056},
{"lm25066", lm25066},
{"lm5064", lm5064},
{"lm5066", lm5066},
@@ -317,5 +453,5 @@ static struct i2c_driver lm25066_driver = {
module_i2c_driver(lm25066_driver);
MODULE_AUTHOR("Guenter Roeck");
-MODULE_DESCRIPTION("PMBus driver for LM25066/LM5064/LM5066");
+MODULE_DESCRIPTION("PMBus driver for LM25056/LM25066/LM5064/LM5066");
MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c
index 6d6130752f94..586a89ef9e0f 100644
--- a/drivers/hwmon/pmbus/ltc2978.c
+++ b/drivers/hwmon/pmbus/ltc2978.c
@@ -1,7 +1,8 @@
/*
- * Hardware monitoring driver for LTC2978 and LTC3880
+ * Hardware monitoring driver for LTC2974, LTC2978, LTC3880, and LTC3883
*
* Copyright (c) 2011 Ericsson AB.
+ * Copyright (c) 2013 Guenter Roeck
*
* 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
@@ -26,28 +27,43 @@
#include <linux/i2c.h>
#include "pmbus.h"
-enum chips { ltc2978, ltc3880 };
+enum chips { ltc2974, ltc2978, ltc3880, ltc3883 };
-/* LTC2978 and LTC3880 */
+/* Common for all chips */
#define LTC2978_MFR_VOUT_PEAK 0xdd
#define LTC2978_MFR_VIN_PEAK 0xde
#define LTC2978_MFR_TEMPERATURE_PEAK 0xdf
#define LTC2978_MFR_SPECIAL_ID 0xe7
-/* LTC2978 only */
+/* LTC2974 and LTC2978 */
#define LTC2978_MFR_VOUT_MIN 0xfb
#define LTC2978_MFR_VIN_MIN 0xfc
#define LTC2978_MFR_TEMPERATURE_MIN 0xfd
-/* LTC3880 only */
+/* LTC2974 only */
+#define LTC2974_MFR_IOUT_PEAK 0xd7
+#define LTC2974_MFR_IOUT_MIN 0xd8
+
+/* LTC3880 and LTC3883 */
#define LTC3880_MFR_IOUT_PEAK 0xd7
#define LTC3880_MFR_CLEAR_PEAKS 0xe3
#define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4
+/* LTC3883 only */
+#define LTC3883_MFR_IIN_PEAK 0xe1
+
+#define LTC2974_ID 0x0212
#define LTC2978_ID_REV1 0x0121
#define LTC2978_ID_REV2 0x0122
#define LTC3880_ID 0x4000
#define LTC3880_ID_MASK 0xff00
+#define LTC3883_ID 0x4300
+#define LTC3883_ID_MASK 0xff00
+
+#define LTC2974_NUM_PAGES 4
+#define LTC2978_NUM_PAGES 8
+#define LTC3880_NUM_PAGES 2
+#define LTC3883_NUM_PAGES 1
/*
* LTC2978 clears peak data whenever the CLEAR_FAULTS command is executed, which
@@ -56,13 +72,15 @@ enum chips { ltc2978, ltc3880 };
* internal cache of measured peak data, which is only cleared if an explicit
* "clear peak" command is executed for the sensor in question.
*/
+
struct ltc2978_data {
enum chips id;
- int vin_min, vin_max;
- int temp_min, temp_max[2];
- int vout_min[8], vout_max[8];
- int iout_max[2];
- int temp2_max;
+ u16 vin_min, vin_max;
+ u16 temp_min[LTC2974_NUM_PAGES], temp_max[LTC2974_NUM_PAGES];
+ u16 vout_min[LTC2978_NUM_PAGES], vout_max[LTC2978_NUM_PAGES];
+ u16 iout_min[LTC2974_NUM_PAGES], iout_max[LTC2974_NUM_PAGES];
+ u16 iin_max;
+ u16 temp2_max;
struct pmbus_driver_info info;
};
@@ -167,9 +185,9 @@ static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg)
LTC2978_MFR_TEMPERATURE_MIN);
if (ret >= 0) {
if (lin11_to_val(ret)
- < lin11_to_val(data->temp_min))
- data->temp_min = ret;
- ret = data->temp_min;
+ < lin11_to_val(data->temp_min[page]))
+ data->temp_min[page] = ret;
+ ret = data->temp_min[page];
}
break;
case PMBUS_VIRT_READ_IOUT_MAX:
@@ -185,6 +203,41 @@ static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg)
return ret;
}
+static int ltc2974_read_word_data(struct i2c_client *client, int page, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct ltc2978_data *data = to_ltc2978_data(info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_IOUT_MAX:
+ ret = pmbus_read_word_data(client, page, LTC2974_MFR_IOUT_PEAK);
+ if (ret >= 0) {
+ if (lin11_to_val(ret)
+ > lin11_to_val(data->iout_max[page]))
+ data->iout_max[page] = ret;
+ ret = data->iout_max[page];
+ }
+ break;
+ case PMBUS_VIRT_READ_IOUT_MIN:
+ ret = pmbus_read_word_data(client, page, LTC2974_MFR_IOUT_MIN);
+ if (ret >= 0) {
+ if (lin11_to_val(ret)
+ < lin11_to_val(data->iout_min[page]))
+ data->iout_min[page] = ret;
+ ret = data->iout_min[page];
+ }
+ break;
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ ret = 0;
+ break;
+ default:
+ ret = ltc2978_read_word_data(client, page, reg);
+ break;
+ }
+ return ret;
+}
+
static int ltc3880_read_word_data(struct i2c_client *client, int page, int reg)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
@@ -226,15 +279,41 @@ static int ltc3880_read_word_data(struct i2c_client *client, int page, int reg)
return ret;
}
+static int ltc3883_read_word_data(struct i2c_client *client, int page, int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct ltc2978_data *data = to_ltc2978_data(info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_IIN_MAX:
+ ret = pmbus_read_word_data(client, page, LTC3883_MFR_IIN_PEAK);
+ if (ret >= 0) {
+ if (lin11_to_val(ret)
+ > lin11_to_val(data->iin_max))
+ data->iin_max = ret;
+ ret = data->iin_max;
+ }
+ break;
+ case PMBUS_VIRT_RESET_IIN_HISTORY:
+ ret = 0;
+ break;
+ default:
+ ret = ltc3880_read_word_data(client, page, reg);
+ break;
+ }
+ return ret;
+}
+
static int ltc2978_clear_peaks(struct i2c_client *client, int page,
enum chips id)
{
int ret;
- if (id == ltc2978)
- ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS);
- else
+ if (id == ltc3880 || id == ltc3883)
ret = pmbus_write_byte(client, 0, LTC3880_MFR_CLEAR_PEAKS);
+ else
+ ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS);
return ret;
}
@@ -247,8 +326,13 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page,
int ret;
switch (reg) {
+ case PMBUS_VIRT_RESET_IIN_HISTORY:
+ data->iin_max = 0x7c00;
+ ret = ltc2978_clear_peaks(client, page, data->id);
+ break;
case PMBUS_VIRT_RESET_IOUT_HISTORY:
data->iout_max[page] = 0x7c00;
+ data->iout_min[page] = 0xfbff;
ret = ltc2978_clear_peaks(client, page, data->id);
break;
case PMBUS_VIRT_RESET_TEMP2_HISTORY:
@@ -266,7 +350,7 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page,
ret = ltc2978_clear_peaks(client, page, data->id);
break;
case PMBUS_VIRT_RESET_TEMP_HISTORY:
- data->temp_min = 0x7bff;
+ data->temp_min[page] = 0x7bff;
data->temp_max[page] = 0x7c00;
ret = ltc2978_clear_peaks(client, page, data->id);
break;
@@ -278,8 +362,10 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page,
}
static const struct i2c_device_id ltc2978_id[] = {
+ {"ltc2974", ltc2974},
{"ltc2978", ltc2978},
{"ltc3880", ltc3880},
+ {"ltc3883", ltc3883},
{}
};
MODULE_DEVICE_TABLE(i2c, ltc2978_id);
@@ -304,10 +390,14 @@ static int ltc2978_probe(struct i2c_client *client,
if (chip_id < 0)
return chip_id;
- if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2) {
+ if (chip_id == LTC2974_ID) {
+ data->id = ltc2974;
+ } else if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2) {
data->id = ltc2978;
} else if ((chip_id & LTC3880_ID_MASK) == LTC3880_ID) {
data->id = ltc3880;
+ } else if ((chip_id & LTC3883_ID_MASK) == LTC3883_ID) {
+ data->id = ltc3883;
} else {
dev_err(&client->dev, "Unsupported chip ID 0x%x\n", chip_id);
return -ENODEV;
@@ -323,26 +413,45 @@ static int ltc2978_probe(struct i2c_client *client,
data->vin_min = 0x7bff;
data->vin_max = 0x7c00;
- data->temp_min = 0x7bff;
+ for (i = 0; i < ARRAY_SIZE(data->vout_min); i++)
+ data->vout_min[i] = 0xffff;
+ for (i = 0; i < ARRAY_SIZE(data->iout_min); i++)
+ data->iout_min[i] = 0xfbff;
+ for (i = 0; i < ARRAY_SIZE(data->iout_max); i++)
+ data->iout_max[i] = 0x7c00;
+ for (i = 0; i < ARRAY_SIZE(data->temp_min); i++)
+ data->temp_min[i] = 0x7bff;
for (i = 0; i < ARRAY_SIZE(data->temp_max); i++)
data->temp_max[i] = 0x7c00;
data->temp2_max = 0x7c00;
switch (data->id) {
+ case ltc2974:
+ info->read_word_data = ltc2974_read_word_data;
+ info->pages = LTC2974_NUM_PAGES;
+ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
+ | PMBUS_HAVE_TEMP2;
+ for (i = 0; i < info->pages; i++) {
+ info->func[i] |= PMBUS_HAVE_VOUT
+ | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_POUT
+ | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
+ }
+ break;
case ltc2978:
info->read_word_data = ltc2978_read_word_data;
- info->pages = 8;
+ info->pages = LTC2978_NUM_PAGES;
info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
| PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
- for (i = 1; i < 8; i++) {
+ for (i = 1; i < LTC2978_NUM_PAGES; i++) {
info->func[i] = PMBUS_HAVE_VOUT
| PMBUS_HAVE_STATUS_VOUT;
}
break;
case ltc3880:
info->read_word_data = ltc3880_read_word_data;
- info->pages = 2;
+ info->pages = LTC3880_NUM_PAGES;
info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN
| PMBUS_HAVE_STATUS_INPUT
| PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
@@ -353,15 +462,20 @@ static int ltc2978_probe(struct i2c_client *client,
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
| PMBUS_HAVE_POUT
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
- data->iout_max[0] = 0x7c00;
- data->iout_max[1] = 0x7c00;
+ break;
+ case ltc3883:
+ info->read_word_data = ltc3883_read_word_data;
+ info->pages = LTC3883_NUM_PAGES;
+ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN
+ | PMBUS_HAVE_STATUS_INPUT
+ | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
+ | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
+ | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
break;
default:
return -ENODEV;
}
- for (i = 0; i < info->pages; i++)
- data->vout_min[i] = 0xffff;
-
return pmbus_do_probe(client, id, info);
}
@@ -378,5 +492,5 @@ static struct i2c_driver ltc2978_driver = {
module_i2c_driver(ltc2978_driver);
MODULE_AUTHOR("Guenter Roeck");
-MODULE_DESCRIPTION("PMBus driver for LTC2978 and LTC3880");
+MODULE_DESCRIPTION("PMBus driver for LTC2974, LTC2978, LTC3880, and LTC3883");
MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/s3c-hwmon.c b/drivers/hwmon/s3c-hwmon.c
index ff2ae0252a48..a9f7e804f1e4 100644
--- a/drivers/hwmon/s3c-hwmon.c
+++ b/drivers/hwmon/s3c-hwmon.c
@@ -107,17 +107,14 @@ static ssize_t s3c_hwmon_show_raw(struct device *dev,
return (ret < 0) ? ret : snprintf(buf, PAGE_SIZE, "%d\n", ret);
}
-#define DEF_ADC_ATTR(x) \
- static SENSOR_DEVICE_ATTR(adc##x##_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, x)
-
-DEF_ADC_ATTR(0);
-DEF_ADC_ATTR(1);
-DEF_ADC_ATTR(2);
-DEF_ADC_ATTR(3);
-DEF_ADC_ATTR(4);
-DEF_ADC_ATTR(5);
-DEF_ADC_ATTR(6);
-DEF_ADC_ATTR(7);
+static SENSOR_DEVICE_ATTR(adc0_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 0);
+static SENSOR_DEVICE_ATTR(adc1_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 1);
+static SENSOR_DEVICE_ATTR(adc2_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 2);
+static SENSOR_DEVICE_ATTR(adc3_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 3);
+static SENSOR_DEVICE_ATTR(adc4_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 4);
+static SENSOR_DEVICE_ATTR(adc5_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 5);
+static SENSOR_DEVICE_ATTR(adc6_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 6);
+static SENSOR_DEVICE_ATTR(adc7_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 7);
static struct attribute *s3c_hwmon_attrs[9] = {
&sensor_dev_attr_adc0_raw.dev_attr.attr,
diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c
index d00b30adc34b..738681983284 100644
--- a/drivers/hwmon/sch56xx-common.c
+++ b/drivers/hwmon/sch56xx-common.c
@@ -161,8 +161,8 @@ static int sch56xx_send_cmd(u16 addr, u8 cmd, u16 reg, u8 v)
break;
}
if (i == max_busy_polls + max_lazy_polls) {
- pr_err("Max retries exceeded reading virtual "
- "register 0x%04hx (%d)\n", reg, 1);
+ pr_err("Max retries exceeded reading virtual register 0x%04hx (%d)\n",
+ reg, 1);
return -EIO;
}
@@ -178,12 +178,12 @@ static int sch56xx_send_cmd(u16 addr, u8 cmd, u16 reg, u8 v)
break;
if (i == 0)
- pr_warn("EC reports: 0x%02x reading virtual register "
- "0x%04hx\n", (unsigned int)val, reg);
+ pr_warn("EC reports: 0x%02x reading virtual register 0x%04hx\n",
+ (unsigned int)val, reg);
}
if (i == max_busy_polls) {
- pr_err("Max retries exceeded reading virtual "
- "register 0x%04hx (%d)\n", reg, 2);
+ pr_err("Max retries exceeded reading virtual register 0x%04hx (%d)\n",
+ reg, 2);
return -EIO;
}
diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c
index c35847a1a0a3..1404e6319deb 100644
--- a/drivers/hwmon/sis5595.c
+++ b/drivers/hwmon/sis5595.c
@@ -456,8 +456,9 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *da,
data->fan_div[nr] = 3;
break;
default:
- dev_err(dev, "fan_div value %ld not "
- "supported. Choose one of 1, 2, 4 or 8!\n", val);
+ dev_err(dev,
+ "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n",
+ val);
mutex_unlock(&data->update_lock);
return -EINVAL;
}
diff --git a/drivers/hwmon/thmc50.c b/drivers/hwmon/thmc50.c
index 4b59eb53b18a..db288db7d3e9 100644
--- a/drivers/hwmon/thmc50.c
+++ b/drivers/hwmon/thmc50.c
@@ -41,8 +41,8 @@ enum chips { thmc50, adm1022 };
static unsigned short adm1022_temp3[16];
static unsigned int adm1022_temp3_num;
module_param_array(adm1022_temp3, ushort, &adm1022_temp3_num, 0);
-MODULE_PARM_DESC(adm1022_temp3, "List of adapter,address pairs "
- "to enable 3rd temperature (ADM1022 only)");
+MODULE_PARM_DESC(adm1022_temp3,
+ "List of adapter,address pairs to enable 3rd temperature (ADM1022 only)");
/* Many THMC50 constants specified below */
@@ -312,8 +312,7 @@ static int thmc50_detect(struct i2c_client *client,
const char *type_name;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
- pr_debug("thmc50: detect failed, "
- "smbus byte data not supported!\n");
+ pr_debug("thmc50: detect failed, smbus byte data not supported!\n");
return -ENODEV;
}
diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c
index 523dd89ba498..d7b47abf37fe 100644
--- a/drivers/hwmon/tmp102.c
+++ b/drivers/hwmon/tmp102.c
@@ -155,8 +155,8 @@ static int tmp102_probe(struct i2c_client *client,
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WORD_DATA)) {
- dev_err(&client->dev, "adapter doesn't support SMBus word "
- "transactions\n");
+ dev_err(&client->dev,
+ "adapter doesn't support SMBus word transactions\n");
return -ENODEV;
}
diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c
index c85f6967ccc3..a478454f690f 100644
--- a/drivers/hwmon/tmp401.c
+++ b/drivers/hwmon/tmp401.c
@@ -5,6 +5,9 @@
* Gabriel Konat, Sander Leget, Wouter Willems
* Copyright (C) 2009 Andre Prendel <andre.prendel@gmx.de>
*
+ * Cleanup and support for TMP431 and TMP432 by Guenter Roeck
+ * Copyright (c) 2013 Guenter Roeck <linux@roeck-us.net>
+ *
* 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
@@ -30,6 +33,7 @@
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/bitops.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
@@ -40,9 +44,9 @@
#include <linux/sysfs.h>
/* Addresses to scan */
-static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END };
+static const unsigned short normal_i2c[] = { 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
-enum chips { tmp401, tmp411 };
+enum chips { tmp401, tmp411, tmp431, tmp432 };
/*
* The TMP401 registers, note some registers have different addresses for
@@ -54,42 +58,84 @@ enum chips { tmp401, tmp411 };
#define TMP401_CONVERSION_RATE_READ 0x04
#define TMP401_CONVERSION_RATE_WRITE 0x0A
#define TMP401_TEMP_CRIT_HYST 0x21
-#define TMP401_CONSECUTIVE_ALERT 0x22
#define TMP401_MANUFACTURER_ID_REG 0xFE
#define TMP401_DEVICE_ID_REG 0xFF
-#define TMP411_N_FACTOR_REG 0x18
-
-static const u8 TMP401_TEMP_MSB[2] = { 0x00, 0x01 };
-static const u8 TMP401_TEMP_LSB[2] = { 0x15, 0x10 };
-static const u8 TMP401_TEMP_LOW_LIMIT_MSB_READ[2] = { 0x06, 0x08 };
-static const u8 TMP401_TEMP_LOW_LIMIT_MSB_WRITE[2] = { 0x0C, 0x0E };
-static const u8 TMP401_TEMP_LOW_LIMIT_LSB[2] = { 0x17, 0x14 };
-static const u8 TMP401_TEMP_HIGH_LIMIT_MSB_READ[2] = { 0x05, 0x07 };
-static const u8 TMP401_TEMP_HIGH_LIMIT_MSB_WRITE[2] = { 0x0B, 0x0D };
-static const u8 TMP401_TEMP_HIGH_LIMIT_LSB[2] = { 0x16, 0x13 };
-/* These are called the THERM limit / hysteresis / mask in the datasheet */
-static const u8 TMP401_TEMP_CRIT_LIMIT[2] = { 0x20, 0x19 };
-
-static const u8 TMP411_TEMP_LOWEST_MSB[2] = { 0x30, 0x34 };
-static const u8 TMP411_TEMP_LOWEST_LSB[2] = { 0x31, 0x35 };
-static const u8 TMP411_TEMP_HIGHEST_MSB[2] = { 0x32, 0x36 };
-static const u8 TMP411_TEMP_HIGHEST_LSB[2] = { 0x33, 0x37 };
+
+static const u8 TMP401_TEMP_MSB_READ[6][2] = {
+ { 0x00, 0x01 }, /* temp */
+ { 0x06, 0x08 }, /* low limit */
+ { 0x05, 0x07 }, /* high limit */
+ { 0x20, 0x19 }, /* therm (crit) limit */
+ { 0x30, 0x34 }, /* lowest */
+ { 0x32, 0x36 }, /* highest */
+};
+
+static const u8 TMP401_TEMP_MSB_WRITE[6][2] = {
+ { 0, 0 }, /* temp (unused) */
+ { 0x0C, 0x0E }, /* low limit */
+ { 0x0B, 0x0D }, /* high limit */
+ { 0x20, 0x19 }, /* therm (crit) limit */
+ { 0x30, 0x34 }, /* lowest */
+ { 0x32, 0x36 }, /* highest */
+};
+
+static const u8 TMP401_TEMP_LSB[6][2] = {
+ { 0x15, 0x10 }, /* temp */
+ { 0x17, 0x14 }, /* low limit */
+ { 0x16, 0x13 }, /* high limit */
+ { 0, 0 }, /* therm (crit) limit (unused) */
+ { 0x31, 0x35 }, /* lowest */
+ { 0x33, 0x37 }, /* highest */
+};
+
+static const u8 TMP432_TEMP_MSB_READ[4][3] = {
+ { 0x00, 0x01, 0x23 }, /* temp */
+ { 0x06, 0x08, 0x16 }, /* low limit */
+ { 0x05, 0x07, 0x15 }, /* high limit */
+ { 0x20, 0x19, 0x1A }, /* therm (crit) limit */
+};
+
+static const u8 TMP432_TEMP_MSB_WRITE[4][3] = {
+ { 0, 0, 0 }, /* temp - unused */
+ { 0x0C, 0x0E, 0x16 }, /* low limit */
+ { 0x0B, 0x0D, 0x15 }, /* high limit */
+ { 0x20, 0x19, 0x1A }, /* therm (crit) limit */
+};
+
+static const u8 TMP432_TEMP_LSB[3][3] = {
+ { 0x29, 0x10, 0x24 }, /* temp */
+ { 0x3E, 0x14, 0x18 }, /* low limit */
+ { 0x3D, 0x13, 0x17 }, /* high limit */
+};
+
+/* [0] = fault, [1] = low, [2] = high, [3] = therm/crit */
+static const u8 TMP432_STATUS_REG[] = {
+ 0x1b, 0x36, 0x35, 0x37 };
/* Flags */
-#define TMP401_CONFIG_RANGE 0x04
-#define TMP401_CONFIG_SHUTDOWN 0x40
-#define TMP401_STATUS_LOCAL_CRIT 0x01
-#define TMP401_STATUS_REMOTE_CRIT 0x02
-#define TMP401_STATUS_REMOTE_OPEN 0x04
-#define TMP401_STATUS_REMOTE_LOW 0x08
-#define TMP401_STATUS_REMOTE_HIGH 0x10
-#define TMP401_STATUS_LOCAL_LOW 0x20
-#define TMP401_STATUS_LOCAL_HIGH 0x40
+#define TMP401_CONFIG_RANGE BIT(2)
+#define TMP401_CONFIG_SHUTDOWN BIT(6)
+#define TMP401_STATUS_LOCAL_CRIT BIT(0)
+#define TMP401_STATUS_REMOTE_CRIT BIT(1)
+#define TMP401_STATUS_REMOTE_OPEN BIT(2)
+#define TMP401_STATUS_REMOTE_LOW BIT(3)
+#define TMP401_STATUS_REMOTE_HIGH BIT(4)
+#define TMP401_STATUS_LOCAL_LOW BIT(5)
+#define TMP401_STATUS_LOCAL_HIGH BIT(6)
+
+/* On TMP432, each status has its own register */
+#define TMP432_STATUS_LOCAL BIT(0)
+#define TMP432_STATUS_REMOTE1 BIT(1)
+#define TMP432_STATUS_REMOTE2 BIT(2)
/* Manufacturer / Device ID's */
#define TMP401_MANUFACTURER_ID 0x55
#define TMP401_DEVICE_ID 0x11
-#define TMP411_DEVICE_ID 0x12
+#define TMP411A_DEVICE_ID 0x12
+#define TMP411B_DEVICE_ID 0x13
+#define TMP411C_DEVICE_ID 0x10
+#define TMP431_DEVICE_ID 0x31
+#define TMP432_DEVICE_ID 0x32
/*
* Driver data (common to all clients)
@@ -98,6 +144,8 @@ static const u8 TMP411_TEMP_HIGHEST_LSB[2] = { 0x33, 0x37 };
static const struct i2c_device_id tmp401_id[] = {
{ "tmp401", tmp401 },
{ "tmp411", tmp411 },
+ { "tmp431", tmp431 },
+ { "tmp432", tmp432 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tmp401_id);
@@ -113,16 +161,13 @@ struct tmp401_data {
unsigned long last_updated; /* in jiffies */
enum chips kind;
+ unsigned int update_interval; /* in milliseconds */
+
/* register values */
- u8 status;
+ u8 status[4];
u8 config;
- u16 temp[2];
- u16 temp_low[2];
- u16 temp_high[2];
- u8 temp_crit[2];
+ u16 temp[6][3];
u8 temp_crit_hyst;
- u16 temp_lowest[2];
- u16 temp_highest[2];
};
/*
@@ -136,31 +181,10 @@ static int tmp401_register_to_temp(u16 reg, u8 config)
if (config & TMP401_CONFIG_RANGE)
temp -= 64 * 256;
- return (temp * 625 + 80) / 160;
-}
-
-static u16 tmp401_temp_to_register(long temp, u8 config)
-{
- if (config & TMP401_CONFIG_RANGE) {
- temp = clamp_val(temp, -64000, 191000);
- temp += 64000;
- } else
- temp = clamp_val(temp, 0, 127000);
-
- return (temp * 160 + 312) / 625;
-}
-
-static int tmp401_crit_register_to_temp(u8 reg, u8 config)
-{
- int temp = reg;
-
- if (config & TMP401_CONFIG_RANGE)
- temp -= 64;
-
- return temp * 1000;
+ return DIV_ROUND_CLOSEST(temp * 125, 32);
}
-static u8 tmp401_crit_temp_to_register(long temp, u8 config)
+static u16 tmp401_temp_to_register(long temp, u8 config, int zbits)
{
if (config & TMP401_CONFIG_RANGE) {
temp = clamp_val(temp, -64000, 191000);
@@ -168,113 +192,127 @@ static u8 tmp401_crit_temp_to_register(long temp, u8 config)
} else
temp = clamp_val(temp, 0, 127000);
- return (temp + 500) / 1000;
+ return DIV_ROUND_CLOSEST(temp * (1 << (8 - zbits)), 1000) << zbits;
}
-static struct tmp401_data *tmp401_update_device_reg16(
- struct i2c_client *client, struct tmp401_data *data)
+static int tmp401_update_device_reg16(struct i2c_client *client,
+ struct tmp401_data *data)
{
- int i;
-
- for (i = 0; i < 2; i++) {
- /*
- * High byte must be read first immediately followed
- * by the low byte
- */
- data->temp[i] = i2c_smbus_read_byte_data(client,
- TMP401_TEMP_MSB[i]) << 8;
- data->temp[i] |= i2c_smbus_read_byte_data(client,
- TMP401_TEMP_LSB[i]);
- data->temp_low[i] = i2c_smbus_read_byte_data(client,
- TMP401_TEMP_LOW_LIMIT_MSB_READ[i]) << 8;
- data->temp_low[i] |= i2c_smbus_read_byte_data(client,
- TMP401_TEMP_LOW_LIMIT_LSB[i]);
- data->temp_high[i] = i2c_smbus_read_byte_data(client,
- TMP401_TEMP_HIGH_LIMIT_MSB_READ[i]) << 8;
- data->temp_high[i] |= i2c_smbus_read_byte_data(client,
- TMP401_TEMP_HIGH_LIMIT_LSB[i]);
- data->temp_crit[i] = i2c_smbus_read_byte_data(client,
- TMP401_TEMP_CRIT_LIMIT[i]);
-
- if (data->kind == tmp411) {
- data->temp_lowest[i] = i2c_smbus_read_byte_data(client,
- TMP411_TEMP_LOWEST_MSB[i]) << 8;
- data->temp_lowest[i] |= i2c_smbus_read_byte_data(
- client, TMP411_TEMP_LOWEST_LSB[i]);
-
- data->temp_highest[i] = i2c_smbus_read_byte_data(
- client, TMP411_TEMP_HIGHEST_MSB[i]) << 8;
- data->temp_highest[i] |= i2c_smbus_read_byte_data(
- client, TMP411_TEMP_HIGHEST_LSB[i]);
+ int i, j, val;
+ int num_regs = data->kind == tmp411 ? 6 : 4;
+ int num_sensors = data->kind == tmp432 ? 3 : 2;
+
+ for (i = 0; i < num_sensors; i++) { /* local / r1 / r2 */
+ for (j = 0; j < num_regs; j++) { /* temp / low / ... */
+ u8 regaddr;
+ /*
+ * High byte must be read first immediately followed
+ * by the low byte
+ */
+ regaddr = data->kind == tmp432 ?
+ TMP432_TEMP_MSB_READ[j][i] :
+ TMP401_TEMP_MSB_READ[j][i];
+ val = i2c_smbus_read_byte_data(client, regaddr);
+ if (val < 0)
+ return val;
+ data->temp[j][i] = val << 8;
+ if (j == 3) /* crit is msb only */
+ continue;
+ regaddr = data->kind == tmp432 ? TMP432_TEMP_LSB[j][i]
+ : TMP401_TEMP_LSB[j][i];
+ val = i2c_smbus_read_byte_data(client, regaddr);
+ if (val < 0)
+ return val;
+ data->temp[j][i] |= val;
}
}
- return data;
+ return 0;
}
static struct tmp401_data *tmp401_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct tmp401_data *data = i2c_get_clientdata(client);
+ struct tmp401_data *ret = data;
+ int i, val;
+ unsigned long next_update;
mutex_lock(&data->update_lock);
- if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
- data->status = i2c_smbus_read_byte_data(client, TMP401_STATUS);
- data->config = i2c_smbus_read_byte_data(client,
- TMP401_CONFIG_READ);
- tmp401_update_device_reg16(client, data);
+ next_update = data->last_updated +
+ msecs_to_jiffies(data->update_interval) + 1;
+ if (time_after(jiffies, next_update) || !data->valid) {
+ if (data->kind != tmp432) {
+ /*
+ * The driver uses the TMP432 status format internally.
+ * Convert status to TMP432 format for other chips.
+ */
+ val = i2c_smbus_read_byte_data(client, TMP401_STATUS);
+ if (val < 0) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->status[0] =
+ (val & TMP401_STATUS_REMOTE_OPEN) >> 1;
+ data->status[1] =
+ ((val & TMP401_STATUS_REMOTE_LOW) >> 2) |
+ ((val & TMP401_STATUS_LOCAL_LOW) >> 5);
+ data->status[2] =
+ ((val & TMP401_STATUS_REMOTE_HIGH) >> 3) |
+ ((val & TMP401_STATUS_LOCAL_HIGH) >> 6);
+ data->status[3] = val & (TMP401_STATUS_LOCAL_CRIT
+ | TMP401_STATUS_REMOTE_CRIT);
+ } else {
+ for (i = 0; i < ARRAY_SIZE(data->status); i++) {
+ val = i2c_smbus_read_byte_data(client,
+ TMP432_STATUS_REG[i]);
+ if (val < 0) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->status[i] = val;
+ }
+ }
- data->temp_crit_hyst = i2c_smbus_read_byte_data(client,
- TMP401_TEMP_CRIT_HYST);
+ val = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
+ if (val < 0) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->config = val;
+ val = tmp401_update_device_reg16(client, data);
+ if (val < 0) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ val = i2c_smbus_read_byte_data(client, TMP401_TEMP_CRIT_HYST);
+ if (val < 0) {
+ ret = ERR_PTR(val);
+ goto abort;
+ }
+ data->temp_crit_hyst = val;
data->last_updated = jiffies;
data->valid = 1;
}
+abort:
mutex_unlock(&data->update_lock);
-
- return data;
-}
-
-static ssize_t show_temp_value(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- int index = to_sensor_dev_attr(devattr)->index;
- struct tmp401_data *data = tmp401_update_device(dev);
-
- return sprintf(buf, "%d\n",
- tmp401_register_to_temp(data->temp[index], data->config));
-}
-
-static ssize_t show_temp_min(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- int index = to_sensor_dev_attr(devattr)->index;
- struct tmp401_data *data = tmp401_update_device(dev);
-
- return sprintf(buf, "%d\n",
- tmp401_register_to_temp(data->temp_low[index], data->config));
+ return ret;
}
-static ssize_t show_temp_max(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static ssize_t show_temp(struct device *dev,
+ struct device_attribute *devattr, char *buf)
{
- int index = to_sensor_dev_attr(devattr)->index;
+ int nr = to_sensor_dev_attr_2(devattr)->nr;
+ int index = to_sensor_dev_attr_2(devattr)->index;
struct tmp401_data *data = tmp401_update_device(dev);
- return sprintf(buf, "%d\n",
- tmp401_register_to_temp(data->temp_high[index], data->config));
-}
-
-static ssize_t show_temp_crit(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- int index = to_sensor_dev_attr(devattr)->index;
- struct tmp401_data *data = tmp401_update_device(dev);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
return sprintf(buf, "%d\n",
- tmp401_crit_register_to_temp(data->temp_crit[index],
- data->config));
+ tmp401_register_to_temp(data->temp[nr][index], data->config));
}
static ssize_t show_temp_crit_hyst(struct device *dev,
@@ -283,122 +321,60 @@ static ssize_t show_temp_crit_hyst(struct device *dev,
int temp, index = to_sensor_dev_attr(devattr)->index;
struct tmp401_data *data = tmp401_update_device(dev);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
mutex_lock(&data->update_lock);
- temp = tmp401_crit_register_to_temp(data->temp_crit[index],
- data->config);
+ temp = tmp401_register_to_temp(data->temp[3][index], data->config);
temp -= data->temp_crit_hyst * 1000;
mutex_unlock(&data->update_lock);
return sprintf(buf, "%d\n", temp);
}
-static ssize_t show_temp_lowest(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- int index = to_sensor_dev_attr(devattr)->index;
- struct tmp401_data *data = tmp401_update_device(dev);
-
- return sprintf(buf, "%d\n",
- tmp401_register_to_temp(data->temp_lowest[index],
- data->config));
-}
-
-static ssize_t show_temp_highest(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- int index = to_sensor_dev_attr(devattr)->index;
- struct tmp401_data *data = tmp401_update_device(dev);
-
- return sprintf(buf, "%d\n",
- tmp401_register_to_temp(data->temp_highest[index],
- data->config));
-}
-
static ssize_t show_status(struct device *dev,
struct device_attribute *devattr, char *buf)
{
- int mask = to_sensor_dev_attr(devattr)->index;
- struct tmp401_data *data = tmp401_update_device(dev);
-
- if (data->status & mask)
- return sprintf(buf, "1\n");
- else
- return sprintf(buf, "0\n");
-}
-
-static ssize_t store_temp_min(struct device *dev, struct device_attribute
- *devattr, const char *buf, size_t count)
-{
- int index = to_sensor_dev_attr(devattr)->index;
+ int nr = to_sensor_dev_attr_2(devattr)->nr;
+ int mask = to_sensor_dev_attr_2(devattr)->index;
struct tmp401_data *data = tmp401_update_device(dev);
- long val;
- u16 reg;
-
- if (kstrtol(buf, 10, &val))
- return -EINVAL;
-
- reg = tmp401_temp_to_register(val, data->config);
-
- mutex_lock(&data->update_lock);
-
- i2c_smbus_write_byte_data(to_i2c_client(dev),
- TMP401_TEMP_LOW_LIMIT_MSB_WRITE[index], reg >> 8);
- i2c_smbus_write_byte_data(to_i2c_client(dev),
- TMP401_TEMP_LOW_LIMIT_LSB[index], reg & 0xFF);
-
- data->temp_low[index] = reg;
- mutex_unlock(&data->update_lock);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
- return count;
+ return sprintf(buf, "%d\n", !!(data->status[nr] & mask));
}
-static ssize_t store_temp_max(struct device *dev, struct device_attribute
- *devattr, const char *buf, size_t count)
+static ssize_t store_temp(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
{
- int index = to_sensor_dev_attr(devattr)->index;
+ int nr = to_sensor_dev_attr_2(devattr)->nr;
+ int index = to_sensor_dev_attr_2(devattr)->index;
+ struct i2c_client *client = to_i2c_client(dev);
struct tmp401_data *data = tmp401_update_device(dev);
long val;
u16 reg;
+ u8 regaddr;
- if (kstrtol(buf, 10, &val))
- return -EINVAL;
-
- reg = tmp401_temp_to_register(val, data->config);
-
- mutex_lock(&data->update_lock);
-
- i2c_smbus_write_byte_data(to_i2c_client(dev),
- TMP401_TEMP_HIGH_LIMIT_MSB_WRITE[index], reg >> 8);
- i2c_smbus_write_byte_data(to_i2c_client(dev),
- TMP401_TEMP_HIGH_LIMIT_LSB[index], reg & 0xFF);
-
- data->temp_high[index] = reg;
-
- mutex_unlock(&data->update_lock);
-
- return count;
-}
-
-static ssize_t store_temp_crit(struct device *dev, struct device_attribute
- *devattr, const char *buf, size_t count)
-{
- int index = to_sensor_dev_attr(devattr)->index;
- struct tmp401_data *data = tmp401_update_device(dev);
- long val;
- u8 reg;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
if (kstrtol(buf, 10, &val))
return -EINVAL;
- reg = tmp401_crit_temp_to_register(val, data->config);
+ reg = tmp401_temp_to_register(val, data->config, nr == 3 ? 8 : 4);
mutex_lock(&data->update_lock);
- i2c_smbus_write_byte_data(to_i2c_client(dev),
- TMP401_TEMP_CRIT_LIMIT[index], reg);
-
- data->temp_crit[index] = reg;
+ regaddr = data->kind == tmp432 ? TMP432_TEMP_MSB_WRITE[nr][index]
+ : TMP401_TEMP_MSB_WRITE[nr][index];
+ i2c_smbus_write_byte_data(client, regaddr, reg >> 8);
+ if (nr != 3) {
+ regaddr = data->kind == tmp432 ? TMP432_TEMP_LSB[nr][index]
+ : TMP401_TEMP_LSB[nr][index];
+ i2c_smbus_write_byte_data(client, regaddr, reg & 0xFF);
+ }
+ data->temp[nr][index] = reg;
mutex_unlock(&data->update_lock);
@@ -413,6 +389,9 @@ static ssize_t store_temp_crit_hyst(struct device *dev, struct device_attribute
long val;
u8 reg;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
if (kstrtol(buf, 10, &val))
return -EINVAL;
@@ -422,13 +401,12 @@ static ssize_t store_temp_crit_hyst(struct device *dev, struct device_attribute
val = clamp_val(val, 0, 127000);
mutex_lock(&data->update_lock);
- temp = tmp401_crit_register_to_temp(data->temp_crit[index],
- data->config);
+ temp = tmp401_register_to_temp(data->temp[3][index], data->config);
val = clamp_val(val, temp - 255000, temp);
reg = ((temp - val) + 500) / 1000;
- i2c_smbus_write_byte_data(to_i2c_client(dev),
- TMP401_TEMP_CRIT_HYST, reg);
+ i2c_smbus_write_byte_data(to_i2c_client(dev), TMP401_TEMP_CRIT_HYST,
+ reg);
data->temp_crit_hyst = reg;
@@ -445,54 +423,130 @@ static ssize_t store_temp_crit_hyst(struct device *dev, struct device_attribute
static ssize_t reset_temp_history(struct device *dev,
struct device_attribute *devattr, const char *buf, size_t count)
{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct tmp401_data *data = i2c_get_clientdata(client);
long val;
if (kstrtol(buf, 10, &val))
return -EINVAL;
if (val != 1) {
- dev_err(dev, "temp_reset_history value %ld not"
- " supported. Use 1 to reset the history!\n", val);
+ dev_err(dev,
+ "temp_reset_history value %ld not supported. Use 1 to reset the history!\n",
+ val);
return -EINVAL;
}
- i2c_smbus_write_byte_data(to_i2c_client(dev),
- TMP411_TEMP_LOWEST_MSB[0], val);
+ mutex_lock(&data->update_lock);
+ i2c_smbus_write_byte_data(client, TMP401_TEMP_MSB_WRITE[5][0], val);
+ data->valid = 0;
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static ssize_t show_update_interval(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct tmp401_data *data = i2c_get_clientdata(client);
+
+ return sprintf(buf, "%u\n", data->update_interval);
+}
+
+static ssize_t set_update_interval(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct tmp401_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err, rate;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+
+ /*
+ * For valid rates, interval can be calculated as
+ * interval = (1 << (7 - rate)) * 125;
+ * Rounded rate is therefore
+ * rate = 7 - __fls(interval * 4 / (125 * 3));
+ * Use clamp_val() to avoid overflows, and to ensure valid input
+ * for __fls.
+ */
+ val = clamp_val(val, 125, 16000);
+ rate = 7 - __fls(val * 4 / (125 * 3));
+ mutex_lock(&data->update_lock);
+ i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, rate);
+ data->update_interval = (1 << (7 - rate)) * 125;
+ mutex_unlock(&data->update_lock);
return count;
}
-static struct sensor_device_attribute tmp401_attr[] = {
- SENSOR_ATTR(temp1_input, S_IRUGO, show_temp_value, NULL, 0),
- SENSOR_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_min,
- store_temp_min, 0),
- SENSOR_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
- store_temp_max, 0),
- SENSOR_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp_crit,
- store_temp_crit, 0),
- SENSOR_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp_crit_hyst,
- store_temp_crit_hyst, 0),
- SENSOR_ATTR(temp1_min_alarm, S_IRUGO, show_status, NULL,
- TMP401_STATUS_LOCAL_LOW),
- SENSOR_ATTR(temp1_max_alarm, S_IRUGO, show_status, NULL,
- TMP401_STATUS_LOCAL_HIGH),
- SENSOR_ATTR(temp1_crit_alarm, S_IRUGO, show_status, NULL,
- TMP401_STATUS_LOCAL_CRIT),
- SENSOR_ATTR(temp2_input, S_IRUGO, show_temp_value, NULL, 1),
- SENSOR_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_min,
- store_temp_min, 1),
- SENSOR_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_max,
- store_temp_max, 1),
- SENSOR_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp_crit,
- store_temp_crit, 1),
- SENSOR_ATTR(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 1),
- SENSOR_ATTR(temp2_fault, S_IRUGO, show_status, NULL,
- TMP401_STATUS_REMOTE_OPEN),
- SENSOR_ATTR(temp2_min_alarm, S_IRUGO, show_status, NULL,
- TMP401_STATUS_REMOTE_LOW),
- SENSOR_ATTR(temp2_max_alarm, S_IRUGO, show_status, NULL,
- TMP401_STATUS_REMOTE_HIGH),
- SENSOR_ATTR(temp2_crit_alarm, S_IRUGO, show_status, NULL,
- TMP401_STATUS_REMOTE_CRIT),
+static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_min, S_IWUSR | S_IRUGO, show_temp,
+ store_temp, 1, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_max, S_IWUSR | S_IRUGO, show_temp,
+ store_temp, 2, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_crit, S_IWUSR | S_IRUGO, show_temp,
+ store_temp, 3, 0);
+static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO,
+ show_temp_crit_hyst, store_temp_crit_hyst, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_min_alarm, S_IRUGO, show_status, NULL,
+ 1, TMP432_STATUS_LOCAL);
+static SENSOR_DEVICE_ATTR_2(temp1_max_alarm, S_IRUGO, show_status, NULL,
+ 2, TMP432_STATUS_LOCAL);
+static SENSOR_DEVICE_ATTR_2(temp1_crit_alarm, S_IRUGO, show_status, NULL,
+ 3, TMP432_STATUS_LOCAL);
+static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1);
+static SENSOR_DEVICE_ATTR_2(temp2_min, S_IWUSR | S_IRUGO, show_temp,
+ store_temp, 1, 1);
+static SENSOR_DEVICE_ATTR_2(temp2_max, S_IWUSR | S_IRUGO, show_temp,
+ store_temp, 2, 1);
+static SENSOR_DEVICE_ATTR_2(temp2_crit, S_IWUSR | S_IRUGO, show_temp,
+ store_temp, 3, 1);
+static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst,
+ NULL, 1);
+static SENSOR_DEVICE_ATTR_2(temp2_fault, S_IRUGO, show_status, NULL,
+ 0, TMP432_STATUS_REMOTE1);
+static SENSOR_DEVICE_ATTR_2(temp2_min_alarm, S_IRUGO, show_status, NULL,
+ 1, TMP432_STATUS_REMOTE1);
+static SENSOR_DEVICE_ATTR_2(temp2_max_alarm, S_IRUGO, show_status, NULL,
+ 2, TMP432_STATUS_REMOTE1);
+static SENSOR_DEVICE_ATTR_2(temp2_crit_alarm, S_IRUGO, show_status, NULL,
+ 3, TMP432_STATUS_REMOTE1);
+
+static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval,
+ set_update_interval);
+
+static struct attribute *tmp401_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+
+ &dev_attr_update_interval.attr,
+
+ NULL
+};
+
+static const struct attribute_group tmp401_group = {
+ .attrs = tmp401_attributes,
};
/*
@@ -502,12 +556,60 @@ static struct sensor_device_attribute tmp401_attr[] = {
* minimum and maximum register reset for both the local
* and remote channels.
*/
-static struct sensor_device_attribute tmp411_attr[] = {
- SENSOR_ATTR(temp1_highest, S_IRUGO, show_temp_highest, NULL, 0),
- SENSOR_ATTR(temp1_lowest, S_IRUGO, show_temp_lowest, NULL, 0),
- SENSOR_ATTR(temp2_highest, S_IRUGO, show_temp_highest, NULL, 1),
- SENSOR_ATTR(temp2_lowest, S_IRUGO, show_temp_lowest, NULL, 1),
- SENSOR_ATTR(temp_reset_history, S_IWUSR, NULL, reset_temp_history, 0),
+static SENSOR_DEVICE_ATTR_2(temp1_lowest, S_IRUGO, show_temp, NULL, 4, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_highest, S_IRUGO, show_temp, NULL, 5, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_lowest, S_IRUGO, show_temp, NULL, 4, 1);
+static SENSOR_DEVICE_ATTR_2(temp2_highest, S_IRUGO, show_temp, NULL, 5, 1);
+static SENSOR_DEVICE_ATTR(temp_reset_history, S_IWUSR, NULL, reset_temp_history,
+ 0);
+
+static struct attribute *tmp411_attributes[] = {
+ &sensor_dev_attr_temp1_highest.dev_attr.attr,
+ &sensor_dev_attr_temp1_lowest.dev_attr.attr,
+ &sensor_dev_attr_temp2_highest.dev_attr.attr,
+ &sensor_dev_attr_temp2_lowest.dev_attr.attr,
+ &sensor_dev_attr_temp_reset_history.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group tmp411_group = {
+ .attrs = tmp411_attributes,
+};
+
+static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2);
+static SENSOR_DEVICE_ATTR_2(temp3_min, S_IWUSR | S_IRUGO, show_temp,
+ store_temp, 1, 2);
+static SENSOR_DEVICE_ATTR_2(temp3_max, S_IWUSR | S_IRUGO, show_temp,
+ store_temp, 2, 2);
+static SENSOR_DEVICE_ATTR_2(temp3_crit, S_IWUSR | S_IRUGO, show_temp,
+ store_temp, 3, 2);
+static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst,
+ NULL, 2);
+static SENSOR_DEVICE_ATTR_2(temp3_fault, S_IRUGO, show_status, NULL,
+ 0, TMP432_STATUS_REMOTE2);
+static SENSOR_DEVICE_ATTR_2(temp3_min_alarm, S_IRUGO, show_status, NULL,
+ 1, TMP432_STATUS_REMOTE2);
+static SENSOR_DEVICE_ATTR_2(temp3_max_alarm, S_IRUGO, show_status, NULL,
+ 2, TMP432_STATUS_REMOTE2);
+static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO, show_status, NULL,
+ 3, TMP432_STATUS_REMOTE2);
+
+static struct attribute *tmp432_attributes[] = {
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
+
+ NULL
+};
+
+static const struct attribute_group tmp432_group = {
+ .attrs = tmp432_attributes,
};
/*
@@ -517,9 +619,11 @@ static struct sensor_device_attribute tmp411_attr[] = {
static void tmp401_init_client(struct i2c_client *client)
{
int config, config_orig;
+ struct tmp401_data *data = i2c_get_clientdata(client);
/* Set the conversion rate to 2 Hz */
i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, 5);
+ data->update_interval = 500;
/* Start conversions (disable shutdown if necessary) */
config = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
@@ -554,11 +658,35 @@ static int tmp401_detect(struct i2c_client *client,
switch (reg) {
case TMP401_DEVICE_ID:
+ if (client->addr != 0x4c)
+ return -ENODEV;
kind = tmp401;
break;
- case TMP411_DEVICE_ID:
+ case TMP411A_DEVICE_ID:
+ if (client->addr != 0x4c)
+ return -ENODEV;
+ kind = tmp411;
+ break;
+ case TMP411B_DEVICE_ID:
+ if (client->addr != 0x4d)
+ return -ENODEV;
kind = tmp411;
break;
+ case TMP411C_DEVICE_ID:
+ if (client->addr != 0x4e)
+ return -ENODEV;
+ kind = tmp411;
+ break;
+ case TMP431_DEVICE_ID:
+ if (client->addr == 0x4e)
+ return -ENODEV;
+ kind = tmp431;
+ break;
+ case TMP432_DEVICE_ID:
+ if (client->addr == 0x4e)
+ return -ENODEV;
+ kind = tmp432;
+ break;
default:
return -ENODEV;
}
@@ -579,20 +707,19 @@ static int tmp401_detect(struct i2c_client *client,
static int tmp401_remove(struct i2c_client *client)
{
+ struct device *dev = &client->dev;
struct tmp401_data *data = i2c_get_clientdata(client);
- int i;
if (data->hwmon_dev)
hwmon_device_unregister(data->hwmon_dev);
- for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++)
- device_remove_file(&client->dev, &tmp401_attr[i].dev_attr);
+ sysfs_remove_group(&dev->kobj, &tmp401_group);
- if (data->kind == tmp411) {
- for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++)
- device_remove_file(&client->dev,
- &tmp411_attr[i].dev_attr);
- }
+ if (data->kind == tmp411)
+ sysfs_remove_group(&dev->kobj, &tmp411_group);
+
+ if (data->kind == tmp432)
+ sysfs_remove_group(&dev->kobj, &tmp432_group);
return 0;
}
@@ -600,12 +727,12 @@ static int tmp401_remove(struct i2c_client *client)
static int tmp401_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- int i, err = 0;
+ struct device *dev = &client->dev;
+ int err;
struct tmp401_data *data;
- const char *names[] = { "TMP401", "TMP411" };
+ const char *names[] = { "TMP401", "TMP411", "TMP431", "TMP432" };
- data = devm_kzalloc(&client->dev, sizeof(struct tmp401_data),
- GFP_KERNEL);
+ data = devm_kzalloc(dev, sizeof(struct tmp401_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -617,31 +744,32 @@ static int tmp401_probe(struct i2c_client *client,
tmp401_init_client(client);
/* Register sysfs hooks */
- for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++) {
- err = device_create_file(&client->dev,
- &tmp401_attr[i].dev_attr);
+ err = sysfs_create_group(&dev->kobj, &tmp401_group);
+ if (err)
+ return err;
+
+ /* Register additional tmp411 sysfs hooks */
+ if (data->kind == tmp411) {
+ err = sysfs_create_group(&dev->kobj, &tmp411_group);
if (err)
goto exit_remove;
}
- /* Register additional tmp411 sysfs hooks */
- if (data->kind == tmp411) {
- for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++) {
- err = device_create_file(&client->dev,
- &tmp411_attr[i].dev_attr);
- if (err)
- goto exit_remove;
- }
+ /* Register additional tmp432 sysfs hooks */
+ if (data->kind == tmp432) {
+ err = sysfs_create_group(&dev->kobj, &tmp432_group);
+ if (err)
+ goto exit_remove;
}
- data->hwmon_dev = hwmon_device_register(&client->dev);
+ data->hwmon_dev = hwmon_device_register(dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
data->hwmon_dev = NULL;
goto exit_remove;
}
- dev_info(&client->dev, "Detected TI %s chip\n", names[data->kind]);
+ dev_info(dev, "Detected TI %s chip\n", names[data->kind]);
return 0;
diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c
index 6a8ded29f1ed..964c1d688274 100644
--- a/drivers/hwmon/tmp421.c
+++ b/drivers/hwmon/tmp421.c
@@ -208,8 +208,8 @@ static int tmp421_init_client(struct i2c_client *client)
/* Start conversions (disable shutdown if necessary) */
config = i2c_smbus_read_byte_data(client, TMP421_CONFIG_REG_1);
if (config < 0) {
- dev_err(&client->dev, "Could not read configuration"
- " register (%d)\n", config);
+ dev_err(&client->dev,
+ "Could not read configuration register (%d)\n", config);
return -ENODEV;
}
@@ -322,6 +322,5 @@ static struct i2c_driver tmp421_driver = {
module_i2c_driver(tmp421_driver);
MODULE_AUTHOR("Andre Prendel <andre.prendel@gmx.de>");
-MODULE_DESCRIPTION("Texas Instruments TMP421/422/423 temperature sensor"
- " driver");
+MODULE_DESCRIPTION("Texas Instruments TMP421/422/423 temperature sensor driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/via686a.c b/drivers/hwmon/via686a.c
index 3123b30208c5..c9dcce8c3dc3 100644
--- a/drivers/hwmon/via686a.c
+++ b/drivers/hwmon/via686a.c
@@ -125,7 +125,7 @@ static const u8 VIA686A_REG_TEMP_HYST[] = { 0x3a, 0x3e, 0x1e };
* (These conversions were contributed by Jonathan Teh Soon Yew
* <j.teh@iname.com>)
*/
-static inline u8 IN_TO_REG(long val, int inNum)
+static inline u8 IN_TO_REG(long val, int in_num)
{
/*
* To avoid floating point, we multiply constants by 10 (100 for +12V).
@@ -134,29 +134,29 @@ static inline u8 IN_TO_REG(long val, int inNum)
* by an additional 10000 (100000 for +12V): 1000 for val and 10 (100)
* for the constants.
*/
- if (inNum <= 1)
+ if (in_num <= 1)
return (u8) clamp_val((val * 21024 - 1205000) / 250000, 0, 255);
- else if (inNum == 2)
+ else if (in_num == 2)
return (u8) clamp_val((val * 15737 - 1205000) / 250000, 0, 255);
- else if (inNum == 3)
+ else if (in_num == 3)
return (u8) clamp_val((val * 10108 - 1205000) / 250000, 0, 255);
else
return (u8) clamp_val((val * 41714 - 12050000) / 2500000, 0,
255);
}
-static inline long IN_FROM_REG(u8 val, int inNum)
+static inline long IN_FROM_REG(u8 val, int in_num)
{
/*
* To avoid floating point, we multiply constants by 10 (100 for +12V).
* We also multiply them by 1000 because we want 0.001V/bit for the
* output value. Rounding is done.
*/
- if (inNum <= 1)
+ if (in_num <= 1)
return (long) ((250000 * val + 1330000 + 21024 / 2) / 21024);
- else if (inNum == 2)
+ else if (in_num == 2)
return (long) ((250000 * val + 1330000 + 15737 / 2) / 15737);
- else if (inNum == 3)
+ else if (in_num == 3)
return (long) ((250000 * val + 1330000 + 10108 / 2) / 10108);
else
return (long) ((2500000 * val + 13300000 + 41714 / 2) / 41714);
@@ -210,10 +210,10 @@ static inline u8 FAN_TO_REG(long rpm, int div)
* VIA register values 0-255. I *10 before rounding, so we get tenth-degree
* precision. (I could have done all 1024 values for our 10-bit readings,
* but the function is very linear in the useful range (0-80 deg C), so
- * we'll just use linear interpolation for 10-bit readings.) So, tempLUT
+ * we'll just use linear interpolation for 10-bit readings.) So, temp_lut
* is the temp at via register values 0-255:
*/
-static const s16 tempLUT[] = {
+static const s16 temp_lut[] = {
-709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519,
-503, -487, -471, -456, -442, -428, -414, -400, -387, -375,
-362, -350, -339, -327, -316, -305, -295, -285, -275, -265,
@@ -261,7 +261,7 @@ static const s16 tempLUT[] = {
* - 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01)
* Note that n=161:
*/
-static const u8 viaLUT[] = {
+static const u8 via_lut[] = {
12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23,
23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 39, 40,
41, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 60, 62, 64, 66,
@@ -284,26 +284,26 @@ static const u8 viaLUT[] = {
*/
static inline u8 TEMP_TO_REG(long val)
{
- return viaLUT[val <= -50000 ? 0 : val >= 110000 ? 160 :
+ return via_lut[val <= -50000 ? 0 : val >= 110000 ? 160 :
(val < 0 ? val - 500 : val + 500) / 1000 + 50];
}
/* for 8-bit temperature hyst and over registers */
-#define TEMP_FROM_REG(val) ((long)tempLUT[val] * 100)
+#define TEMP_FROM_REG(val) ((long)temp_lut[val] * 100)
/* for 10-bit temperature readings */
static inline long TEMP_FROM_REG10(u16 val)
{
- u16 eightBits = val >> 2;
- u16 twoBits = val & 3;
+ u16 eight_bits = val >> 2;
+ u16 two_bits = val & 3;
/* no interpolation for these */
- if (twoBits == 0 || eightBits == 255)
- return TEMP_FROM_REG(eightBits);
+ if (two_bits == 0 || eight_bits == 255)
+ return TEMP_FROM_REG(eight_bits);
/* do some linear interpolation */
- return (tempLUT[eightBits] * (4 - twoBits) +
- tempLUT[eightBits + 1] * twoBits) * 25;
+ return (temp_lut[eight_bits] * (4 - two_bits) +
+ temp_lut[eight_bits + 1] * two_bits) * 25;
}
#define DIV_FROM_REG(val) (1 << (val))
@@ -889,8 +889,8 @@ static int via686a_pci_probe(struct pci_dev *dev,
address = val & ~(VIA686A_EXTENT - 1);
if (address == 0) {
- dev_err(&dev->dev, "base address not set - upgrade BIOS "
- "or use force_addr=0xaddr\n");
+ dev_err(&dev->dev,
+ "base address not set - upgrade BIOS or use force_addr=0xaddr\n");
return -ENODEV;
}
@@ -899,8 +899,9 @@ static int via686a_pci_probe(struct pci_dev *dev,
return -ENODEV;
if (!(val & 0x0001)) {
if (!force_addr) {
- dev_warn(&dev->dev, "Sensors disabled, enable "
- "with force_addr=0x%x\n", address);
+ dev_warn(&dev->dev,
+ "Sensors disabled, enable with force_addr=0x%x\n",
+ address);
return -ENODEV;
}
diff --git a/drivers/hwmon/vt1211.c b/drivers/hwmon/vt1211.c
index dcc62f80f67b..6b2f1a42b3ff 100644
--- a/drivers/hwmon/vt1211.c
+++ b/drivers/hwmon/vt1211.c
@@ -571,8 +571,9 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
break;
default:
count = -EINVAL;
- dev_warn(dev, "fan div value %ld not supported. "
- "Choose one of 1, 2, 4, or 8.\n", val);
+ dev_warn(dev,
+ "fan div value %ld not supported. Choose one of 1, 2, 4, or 8.\n",
+ val);
goto EXIT;
}
vt1211_write8(data, VT1211_REG_FAN_DIV,
@@ -674,8 +675,9 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
break;
default:
count = -EINVAL;
- dev_warn(dev, "pwm mode %ld not supported. "
- "Choose one of 0 or 2.\n", val);
+ dev_warn(dev,
+ "pwm mode %ld not supported. Choose one of 0 or 2.\n",
+ val);
goto EXIT;
}
vt1211_write8(data, VT1211_REG_PWM_CTL,
@@ -700,8 +702,9 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
case SHOW_SET_PWM_AUTO_CHANNELS_TEMP:
if (val < 1 || val > 7) {
count = -EINVAL;
- dev_warn(dev, "temp channel %ld not supported. "
- "Choose a value between 1 and 7.\n", val);
+ dev_warn(dev,
+ "temp channel %ld not supported. Choose a value between 1 and 7.\n",
+ val);
goto EXIT;
}
if (!ISTEMP(val - 1, data->uch_config)) {
@@ -1325,15 +1328,15 @@ static int __init vt1211_init(void)
if ((uch_config < -1) || (uch_config > 31)) {
err = -EINVAL;
- pr_warn("Invalid UCH configuration %d. "
- "Choose a value between 0 and 31.\n", uch_config);
+ pr_warn("Invalid UCH configuration %d. Choose a value between 0 and 31.\n",
+ uch_config);
goto EXIT;
}
if ((int_mode < -1) || (int_mode > 0)) {
err = -EINVAL;
- pr_warn("Invalid interrupt mode %d. "
- "Only mode 0 is supported.\n", int_mode);
+ pr_warn("Invalid interrupt mode %d. Only mode 0 is supported.\n",
+ int_mode);
goto EXIT;
}
diff --git a/drivers/hwmon/vt8231.c b/drivers/hwmon/vt8231.c
index 988a2a796764..0e7017841f7d 100644
--- a/drivers/hwmon/vt8231.c
+++ b/drivers/hwmon/vt8231.c
@@ -573,8 +573,9 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
data->fan_div[nr] = 3;
break;
default:
- dev_err(dev, "fan_div value %ld not supported. "
- "Choose one of 1, 2, 4 or 8!\n", val);
+ dev_err(dev,
+ "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n",
+ val);
mutex_unlock(&data->update_lock);
return -EINVAL;
}
diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c
index 0a89211c25f6..016027229e81 100644
--- a/drivers/hwmon/w83627ehf.c
+++ b/drivers/hwmon/w83627ehf.c
@@ -840,8 +840,8 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
&& (reg >= 0xff || (sio_data->kind == nct6775
&& reg == 0x00))
&& data->fan_div[i] < 0x07) {
- dev_dbg(dev, "Increasing fan%d "
- "clock divider from %u to %u\n",
+ dev_dbg(dev,
+ "Increasing fan%d clock divider from %u to %u\n",
i + 1, div_from_reg(data->fan_div[i]),
div_from_reg(data->fan_div[i] + 1));
data->fan_div[i]++;
@@ -1110,9 +1110,9 @@ store_fan_min(struct device *dev, struct device_attribute *attr,
*/
data->fan_min[nr] = 254;
new_div = 7; /* 128 == (1 << 7) */
- dev_warn(dev, "fan%u low limit %lu below minimum %u, set to "
- "minimum\n", nr + 1, val,
- data->fan_from_reg_min(254, 7));
+ dev_warn(dev,
+ "fan%u low limit %lu below minimum %u, set to minimum\n",
+ nr + 1, val, data->fan_from_reg_min(254, 7));
} else if (!reg) {
/*
* Speed above this value cannot possibly be represented,
@@ -1120,9 +1120,9 @@ store_fan_min(struct device *dev, struct device_attribute *attr,
*/
data->fan_min[nr] = 1;
new_div = 0; /* 1 == (1 << 0) */
- dev_warn(dev, "fan%u low limit %lu above maximum %u, set to "
- "maximum\n", nr + 1, val,
- data->fan_from_reg_min(1, 0));
+ dev_warn(dev,
+ "fan%u low limit %lu above maximum %u, set to maximum\n",
+ nr + 1, val, data->fan_from_reg_min(1, 0));
} else {
/*
* Automatically pick the best divider, i.e. the one such
@@ -2396,15 +2396,15 @@ static int w83627ehf_probe(struct platform_device *pdev)
en_vrm10 = superio_inb(sio_data->sioreg,
SIO_REG_EN_VRM10);
if ((en_vrm10 & 0x08) && data->vrm == 90) {
- dev_warn(dev, "Setting VID input "
- "voltage to TTL\n");
+ dev_warn(dev,
+ "Setting VID input voltage to TTL\n");
superio_outb(sio_data->sioreg,
SIO_REG_EN_VRM10,
en_vrm10 & ~0x08);
} else if (!(en_vrm10 & 0x08)
&& data->vrm == 100) {
- dev_warn(dev, "Setting VID input "
- "voltage to VRM10\n");
+ dev_warn(dev,
+ "Setting VID input voltage to VRM10\n");
superio_outb(sio_data->sioreg,
SIO_REG_EN_VRM10,
en_vrm10 | 0x08);
@@ -2420,8 +2420,8 @@ static int w83627ehf_probe(struct platform_device *pdev)
if (err)
goto exit_release;
} else {
- dev_info(dev, "VID pins in output mode, CPU VID not "
- "available\n");
+ dev_info(dev,
+ "VID pins in output mode, CPU VID not available\n");
}
}
@@ -2795,8 +2795,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
/* Activate logical device if needed */
val = superio_inb(sioaddr, SIO_REG_ENABLE);
if (!(val & 0x01)) {
- pr_warn("Forcibly enabling Super-I/O. "
- "Sensor is probably unusable.\n");
+ pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n");
superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01);
}
diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c
index aeec5b1d81c9..f9d513949a38 100644
--- a/drivers/hwmon/w83781d.c
+++ b/drivers/hwmon/w83781d.c
@@ -64,8 +64,8 @@ enum chips { w83781d, w83782d, w83783s, as99127f };
/* Insmod parameters */
static unsigned short force_subclients[4];
module_param_array(force_subclients, short, NULL, 0);
-MODULE_PARM_DESC(force_subclients, "List of subclient addresses: "
- "{bus, clientaddr, subclientaddr1, subclientaddr2}");
+MODULE_PARM_DESC(force_subclients,
+ "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}");
static bool reset;
module_param(reset, bool, 0);
@@ -826,8 +826,9 @@ store_sensor(struct device *dev, struct device_attribute *da,
data->sens[nr] = val;
break;
case W83781D_DEFAULT_BETA:
- dev_warn(dev, "Sensor type %d is deprecated, please use 4 "
- "instead\n", W83781D_DEFAULT_BETA);
+ dev_warn(dev,
+ "Sensor type %d is deprecated, please use 4 instead\n",
+ W83781D_DEFAULT_BETA);
/* fall through */
case 4: /* thermistor */
tmp = w83781d_read_value(data, W83781D_REG_SCFG1);
@@ -874,8 +875,8 @@ w83781d_detect_subclients(struct i2c_client *new_client)
for (i = 2; i <= 3; i++) {
if (force_subclients[i] < 0x48 ||
force_subclients[i] > 0x4f) {
- dev_err(&new_client->dev, "Invalid subclient "
- "address %d; must be 0x48-0x4f\n",
+ dev_err(&new_client->dev,
+ "Invalid subclient address %d; must be 0x48-0x4f\n",
force_subclients[i]);
err = -EINVAL;
goto ERROR_SC_1;
@@ -910,9 +911,9 @@ w83781d_detect_subclients(struct i2c_client *new_client)
for (i = 0; i < num_sc; i++) {
data->lm75[i] = i2c_new_dummy(adapter, sc_addr[i]);
if (!data->lm75[i]) {
- dev_err(&new_client->dev, "Subclient %d "
- "registration at address 0x%x "
- "failed.\n", i, sc_addr[i]);
+ dev_err(&new_client->dev,
+ "Subclient %d registration at address 0x%x failed.\n",
+ i, sc_addr[i]);
err = -ENOMEM;
if (i == 1)
goto ERROR_SC_3;
@@ -1176,8 +1177,9 @@ w83781d_detect(struct i2c_client *client, struct i2c_board_info *info)
goto err_nodev;
if (val1 <= 0x30 && w83781d_alias_detect(client, val1)) {
- dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
- "be the same as ISA device\n", address);
+ dev_dbg(&adapter->dev,
+ "Device at 0x%02x appears to be the same as ISA device\n",
+ address);
goto err_nodev;
}
@@ -1367,8 +1369,8 @@ w83781d_init_device(struct device *dev)
* as I see very little reason why this would be needed at
* all.
*/
- dev_info(dev, "If reset=1 solved a problem you were "
- "having, please report!\n");
+ dev_info(dev,
+ "If reset=1 solved a problem you were having, please report!\n");
/* save these registers */
i = w83781d_read_value(data, W83781D_REG_BEEP_CONFIG);
@@ -1425,8 +1427,8 @@ w83781d_init_device(struct device *dev)
/* Enable temp2 */
tmp = w83781d_read_value(data, W83781D_REG_TEMP2_CONFIG);
if (tmp & 0x01) {
- dev_warn(dev, "Enabling temp2, readings "
- "might not make sense\n");
+ dev_warn(dev,
+ "Enabling temp2, readings might not make sense\n");
w83781d_write_value(data, W83781D_REG_TEMP2_CONFIG,
tmp & 0xfe);
}
@@ -1436,8 +1438,8 @@ w83781d_init_device(struct device *dev)
tmp = w83781d_read_value(data,
W83781D_REG_TEMP3_CONFIG);
if (tmp & 0x01) {
- dev_warn(dev, "Enabling temp3, "
- "readings might not make sense\n");
+ dev_warn(dev,
+ "Enabling temp3, readings might not make sense\n");
w83781d_write_value(data,
W83781D_REG_TEMP3_CONFIG, tmp & 0xfe);
}
diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c
index 38dddddf8875..a3feee332e20 100644
--- a/drivers/hwmon/w83791d.c
+++ b/drivers/hwmon/w83791d.c
@@ -56,8 +56,8 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
static unsigned short force_subclients[4];
module_param_array(force_subclients, short, NULL, 0);
-MODULE_PARM_DESC(force_subclients, "List of subclient addresses: "
- "{bus, clientaddr, subclientaddr1, subclientaddr2}");
+MODULE_PARM_DESC(force_subclients,
+ "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}");
static bool reset;
module_param(reset, bool, 0);
diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c
index 5cb83ddf2cc6..0b804895be43 100644
--- a/drivers/hwmon/w83792d.c
+++ b/drivers/hwmon/w83792d.c
@@ -54,8 +54,8 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
static unsigned short force_subclients[4];
module_param_array(force_subclients, short, NULL, 0);
-MODULE_PARM_DESC(force_subclients, "List of subclient addresses: "
- "{bus, clientaddr, subclientaddr1, subclientaddr2}");
+MODULE_PARM_DESC(force_subclients,
+ "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}");
static bool init;
module_param(init, bool, 0);
@@ -951,8 +951,8 @@ w83792d_detect_subclients(struct i2c_client *new_client)
for (i = 2; i <= 3; i++) {
if (force_subclients[i] < 0x48 ||
force_subclients[i] > 0x4f) {
- dev_err(&new_client->dev, "invalid subclient "
- "address %d; must be 0x48-0x4f\n",
+ dev_err(&new_client->dev,
+ "invalid subclient address %d; must be 0x48-0x4f\n",
force_subclients[i]);
err = -ENODEV;
goto ERROR_SC_0;
@@ -969,8 +969,9 @@ w83792d_detect_subclients(struct i2c_client *new_client)
if (!(val & 0x80)) {
if ((data->lm75[0] != NULL) &&
((val & 0x7) == ((val >> 4) & 0x7))) {
- dev_err(&new_client->dev, "duplicate addresses 0x%x, "
- "use force_subclient\n", data->lm75[0]->addr);
+ dev_err(&new_client->dev,
+ "duplicate addresses 0x%x, use force_subclient\n",
+ data->lm75[0]->addr);
err = -ENODEV;
goto ERROR_SC_1;
}
diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c
index 660427520c53..b0c30a546ff2 100644
--- a/drivers/hwmon/w83793.c
+++ b/drivers/hwmon/w83793.c
@@ -59,8 +59,8 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
static unsigned short force_subclients[4];
module_param_array(force_subclients, short, NULL, 0);
-MODULE_PARM_DESC(force_subclients, "List of subclient addresses: "
- "{bus, clientaddr, subclientaddr1, subclientaddr2}");
+MODULE_PARM_DESC(force_subclients,
+ "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}");
static bool reset;
module_param(reset, bool, 0);
@@ -1921,8 +1921,8 @@ static int w83793_probe(struct i2c_client *client,
}
if (i == ARRAY_SIZE(watchdog_minors)) {
data->watchdog_miscdev.minor = 0;
- dev_warn(&client->dev, "Couldn't register watchdog chardev "
- "(due to no free minor)\n");
+ dev_warn(&client->dev,
+ "Couldn't register watchdog chardev (due to no free minor)\n");
}
mutex_unlock(&watchdog_data_mutex);
diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c
index e226096148eb..908209d24664 100644
--- a/drivers/hwmon/w83795.c
+++ b/drivers/hwmon/w83795.c
@@ -2120,11 +2120,12 @@ static void w83795_check_dynamic_in_limits(struct i2c_client *client)
&w83795_in[i][3].dev_attr.attr,
S_IRUGO);
if (err_max || err_min)
- dev_warn(&client->dev, "Failed to set in%d limits "
- "read-only (%d, %d)\n", i, err_max, err_min);
+ dev_warn(&client->dev,
+ "Failed to set in%d limits read-only (%d, %d)\n",
+ i, err_max, err_min);
else
- dev_info(&client->dev, "in%d limits set dynamically "
- "from VID\n", i);
+ dev_info(&client->dev,
+ "in%d limits set dynamically from VID\n", i);
}
}