diff options
Diffstat (limited to 'drivers/hwmon')
-rw-r--r-- | drivers/hwmon/Kconfig | 100 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 2 | ||||
-rw-r--r-- | drivers/hwmon/adt7475.c | 2 | ||||
-rw-r--r-- | drivers/hwmon/ams/Makefile | 8 | ||||
-rw-r--r-- | drivers/hwmon/ams/ams-core.c | 250 | ||||
-rw-r--r-- | drivers/hwmon/ams/ams-i2c.c | 277 | ||||
-rw-r--r-- | drivers/hwmon/ams/ams-input.c | 157 | ||||
-rw-r--r-- | drivers/hwmon/ams/ams-pmu.c | 201 | ||||
-rw-r--r-- | drivers/hwmon/ams/ams.h | 70 | ||||
-rw-r--r-- | drivers/hwmon/asc7621.c | 4 | ||||
-rw-r--r-- | drivers/hwmon/it87.c | 210 | ||||
-rw-r--r-- | drivers/hwmon/k8temp.c | 51 | ||||
-rw-r--r-- | drivers/hwmon/lm75.c | 51 | ||||
-rw-r--r-- | drivers/hwmon/lm85.c | 36 | ||||
-rw-r--r-- | drivers/hwmon/lm90.c | 1014 | ||||
-rw-r--r-- | drivers/hwmon/pcf8591.c | 38 | ||||
-rw-r--r-- | drivers/hwmon/s3c-hwmon.c | 8 | ||||
-rw-r--r-- | drivers/hwmon/tmp421.c | 4 | ||||
-rw-r--r-- | drivers/hwmon/w83795.c | 2121 |
19 files changed, 3137 insertions, 1467 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index c357c835eb1..a56f6adf3b7 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -129,7 +129,7 @@ config SENSORS_ADM1025 config SENSORS_ADM1026 tristate "Analog Devices ADM1026 and compatibles" - depends on I2C && EXPERIMENTAL + depends on I2C select HWMON_VID help If you say yes here you get support for Analog Devices ADM1026 @@ -140,7 +140,7 @@ config SENSORS_ADM1026 config SENSORS_ADM1029 tristate "Analog Devices ADM1029" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for Analog Devices ADM1029 sensor chip. @@ -151,7 +151,7 @@ config SENSORS_ADM1029 config SENSORS_ADM1031 tristate "Analog Devices ADM1031 and compatibles" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for Analog Devices ADM1031 and ADM1030 sensor chips. @@ -202,7 +202,7 @@ config SENSORS_ADT7470 config SENSORS_ADT7475 tristate "Analog Devices ADT7473, ADT7475, ADT7476 and ADT7490" - depends on I2C && EXPERIMENTAL + depends on I2C select HWMON_VID help If you say yes here you get support for the Analog Devices @@ -249,32 +249,6 @@ config SENSORS_K10TEMP This driver can also be built as a module. If so, the module will be called k10temp. -config SENSORS_AMS - tristate "Apple Motion Sensor driver" - depends on PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL - select INPUT_POLLDEV - help - Support for the motion sensor included in PowerBooks. Includes - implementations for PMU and I2C. - - This driver can also be built as a module. If so, the module - will be called ams. - -config SENSORS_AMS_PMU - bool "PMU variant" - depends on SENSORS_AMS && ADB_PMU - default y - help - PMU variant of motion sensor, found in late 2005 PowerBooks. - -config SENSORS_AMS_I2C - bool "I2C variant" - depends on SENSORS_AMS && I2C - default y - help - I2C variant of motion sensor, found in early 2005 PowerBooks and - iBooks. - config SENSORS_ASB100 tristate "Asus ASB100 Bach" depends on X86 && I2C && EXPERIMENTAL @@ -322,7 +296,6 @@ config SENSORS_I5K_AMB config SENSORS_F71805F tristate "Fintek F71805F/FG, F71806F/FG and F71872F/FG" - depends on EXPERIMENTAL help If you say yes here you get support for hardware monitoring features of the Fintek F71805F/FG, F71806F/FG and F71872F/FG @@ -333,7 +306,6 @@ config SENSORS_F71805F config SENSORS_F71882FG tristate "Fintek F71858FG, F71862FG, F71882FG, F71889FG and F8000" - depends on EXPERIMENTAL help If you say yes here you get support for hardware monitoring features of the Fintek F71858FG, F71862FG/71863FG, F71882FG/F71883FG, @@ -343,8 +315,8 @@ config SENSORS_F71882FG will be called f71882fg. config SENSORS_F75375S - tristate "Fintek F75375S/SP and F75373"; - depends on I2C && EXPERIMENTAL + tristate "Fintek F75375S/SP and F75373" + depends on I2C help If you say yes here you get support for hardware monitoring features of the Fintek F75375S/SP and F75373 @@ -456,8 +428,8 @@ config SENSORS_IT87 select HWMON_VID help If you say yes here you get support for ITE IT8705F, IT8712F, - IT8716F, IT8718F, IT8720F and IT8726F sensor chips, and the - SiS960 clone. + IT8716F, IT8718F, IT8720F, IT8721F, IT8726F and IT8758E sensor + chips, and the SiS960 clone. This driver can also be built as a module. If so, the module will be called it87. @@ -499,7 +471,7 @@ config SENSORS_LM63 config SENSORS_LM70 tristate "National Semiconductor LM70 / Texas Instruments TMP121" - depends on SPI_MASTER && EXPERIMENTAL + depends on SPI_MASTER help If you say yes here you get support for the National Semiconductor LM70 and Texas Instruments TMP121/TMP123 digital temperature @@ -567,7 +539,7 @@ config SENSORS_LM78 config SENSORS_LM80 tristate "National Semiconductor LM80" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for National Semiconductor LM80 sensor chips. @@ -587,11 +559,12 @@ config SENSORS_LM83 config SENSORS_LM85 tristate "National Semiconductor LM85 and compatibles" - depends on I2C && EXPERIMENTAL + depends on I2C select HWMON_VID help If you say yes here you get support for National Semiconductor LM85 - sensor chips and clones: ADT7463, EMC6D100, EMC6D102 and ADM1027. + sensor chips and clones: ADM1027, ADT7463, ADT7468, EMC6D100, + EMC6D101 and EMC6D102. This driver can also be built as a module. If so, the module will be called lm85. @@ -614,8 +587,8 @@ config SENSORS_LM90 If you say yes here you get support for National Semiconductor LM90, LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659, - MAX6680, MAX6681 and MAX6692, and Winbond/Nuvoton W83L771AWG/ASG - sensor chips. + MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, and Winbond/Nuvoton + W83L771W/G/AWG/ASG sensor chips. This driver can also be built as a module. If so, the module will be called lm90. @@ -726,7 +699,6 @@ config SENSORS_PC87360 config SENSORS_PC87427 tristate "National Semiconductor PC87427" - depends on EXPERIMENTAL help If you say yes here you get access to the hardware monitoring functions of the National Semiconductor PC87427 Super-I/O chip. @@ -763,14 +735,14 @@ config SENSORS_SHT15 will be called sht15. config SENSORS_S3C - tristate "S3C24XX/S3C64XX Inbuilt ADC" - depends on ARCH_S3C2410 + tristate "Samsung built-in ADC" + depends on S3C_ADC help If you say yes here you get support for the on-board ADCs of - the Samsung S3C24XX or S3C64XX series of SoC + the Samsung S3C24XX, S3C64XX and other series of SoC This driver can also be built as a module. If so, the module - will be called s3c-hwmo. + will be called s3c-hwmon. config SENSORS_S3C_RAW bool "Include raw channel attributes in sysfs" @@ -854,7 +826,7 @@ config SENSORS_SMSC47M1 config SENSORS_SMSC47M192 tristate "SMSC LPC47M192 and compatibles" - depends on I2C && EXPERIMENTAL + depends on I2C select HWMON_VID help If you say yes here you get support for the temperature and @@ -910,7 +882,7 @@ config SENSORS_AMC6821 config SENSORS_THMC50 tristate "Texas Instruments THMC50 / Analog Devices ADM1022" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for Texas Instruments THMC50 sensor chips and clones: the Analog Devices ADM1022. @@ -968,7 +940,6 @@ config SENSORS_VIA686A config SENSORS_VT1211 tristate "VIA VT1211" - depends on EXPERIMENTAL select HWMON_VID help If you say yes here then you get support for hardware monitoring @@ -1012,7 +983,7 @@ config SENSORS_W83791D config SENSORS_W83792D tristate "Winbond W83792D" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for the Winbond W83792D chip. @@ -1031,6 +1002,33 @@ config SENSORS_W83793 This driver can also be built as a module. If so, the module will be called w83793. +config SENSORS_W83795 + tristate "Winbond/Nuvoton W83795G/ADG" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for the Winbond W83795G and + W83795ADG hardware monitoring chip. + + This driver can also be built as a module. If so, the module + will be called w83795. + +config SENSORS_W83795_FANCTRL + boolean "Include fan control support (DANGEROUS)" + depends on SENSORS_W83795 && EXPERIMENTAL + default n + help + If you say yes here, support for the both manual and automatic + fan control features will be included in the driver. + + This part of the code wasn't carefully reviewed and tested yet, + so enabling this option is strongly discouraged on production + servers. Only developers and testers should enable it for the + time being. + + Please also note that this option will create sysfs attribute + files which may change in the future, so you shouldn't rely + on them being stable. + config SENSORS_W83L785TS tristate "Winbond W83L785TS-S" depends on I2C && EXPERIMENTAL diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index d30f0f6870e..2479b3da272 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_SENSORS_ASB100) += asb100.o obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o obj-$(CONFIG_SENSORS_W83792D) += w83792d.o obj-$(CONFIG_SENSORS_W83793) += w83793.o +obj-$(CONFIG_SENSORS_W83795) += w83795.o obj-$(CONFIG_SENSORS_W83781D) += w83781d.o obj-$(CONFIG_SENSORS_W83791D) += w83791d.o @@ -35,7 +36,6 @@ obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o -obj-$(CONFIG_SENSORS_AMS) += ams/ obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index a0c38514568..b5fcd87931c 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -146,7 +146,7 @@ #define TEMP_OFFSET_REG(idx) (REG_TEMP_OFFSET_BASE + (idx)) #define TEMP_TRANGE_REG(idx) (REG_TEMP_TRANGE_BASE + (idx)) -static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; +static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; enum chips { adt7473, adt7475, adt7476, adt7490 }; diff --git a/drivers/hwmon/ams/Makefile b/drivers/hwmon/ams/Makefile deleted file mode 100644 index 41c95b2089d..00000000000 --- a/drivers/hwmon/ams/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# Makefile for Apple Motion Sensor driver -# - -ams-y := ams-core.o ams-input.o -ams-$(CONFIG_SENSORS_AMS_PMU) += ams-pmu.o -ams-$(CONFIG_SENSORS_AMS_I2C) += ams-i2c.o -obj-$(CONFIG_SENSORS_AMS) += ams.o diff --git a/drivers/hwmon/ams/ams-core.c b/drivers/hwmon/ams/ams-core.c deleted file mode 100644 index 2ad62c339cd..00000000000 --- a/drivers/hwmon/ams/ams-core.c +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Apple Motion Sensor driver - * - * Copyright (C) 2005 Stelian Pop (stelian@popies.net) - * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/of_platform.h> -#include <asm/pmac_pfunc.h> - -#include "ams.h" - -/* There is only one motion sensor per machine */ -struct ams ams_info; - -static unsigned int verbose; -module_param(verbose, bool, 0644); -MODULE_PARM_DESC(verbose, "Show free falls and shocks in kernel output"); - -/* Call with ams_info.lock held! */ -void ams_sensors(s8 *x, s8 *y, s8 *z) -{ - u32 orient = ams_info.vflag? ams_info.orient1 : ams_info.orient2; - - if (orient & 0x80) - /* X and Y swapped */ - ams_info.get_xyz(y, x, z); - else - ams_info.get_xyz(x, y, z); - - if (orient & 0x04) - *z = ~(*z); - if (orient & 0x02) - *y = ~(*y); - if (orient & 0x01) - *x = ~(*x); -} - -static ssize_t ams_show_current(struct device *dev, - struct device_attribute *attr, char *buf) -{ - s8 x, y, z; - - mutex_lock(&ams_info.lock); - ams_sensors(&x, &y, &z); - mutex_unlock(&ams_info.lock); - - return snprintf(buf, PAGE_SIZE, "%d %d %d\n", x, y, z); -} - -static DEVICE_ATTR(current, S_IRUGO, ams_show_current, NULL); - -static void ams_handle_irq(void *data) -{ - enum ams_irq irq = *((enum ams_irq *)data); - - spin_lock(&ams_info.irq_lock); - - ams_info.worker_irqs |= irq; - schedule_work(&ams_info.worker); - - spin_unlock(&ams_info.irq_lock); -} - -static enum ams_irq ams_freefall_irq_data = AMS_IRQ_FREEFALL; -static struct pmf_irq_client ams_freefall_client = { - .owner = THIS_MODULE, - .handler = ams_handle_irq, - .data = &ams_freefall_irq_data, -}; - -static enum ams_irq ams_shock_irq_data = AMS_IRQ_SHOCK; -static struct pmf_irq_client ams_shock_client = { - .owner = THIS_MODULE, - .handler = ams_handle_irq, - .data = &ams_shock_irq_data, -}; - -/* Once hard disk parking is implemented in the kernel, this function can - * trigger it. - */ -static void ams_worker(struct work_struct *work) -{ - unsigned long flags; - u8 irqs_to_clear; - - mutex_lock(&ams_info.lock); - - spin_lock_irqsave(&ams_info.irq_lock, flags); - irqs_to_clear = ams_info.worker_irqs; - - if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) { - if (verbose) - printk(KERN_INFO "ams: freefall detected!\n"); - - ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL; - } - - if (ams_info.worker_irqs & AMS_IRQ_SHOCK) { - if (verbose) - printk(KERN_INFO "ams: shock detected!\n"); - - ams_info.worker_irqs &= ~AMS_IRQ_SHOCK; - } - - spin_unlock_irqrestore(&ams_info.irq_lock, flags); - - ams_info.clear_irq(irqs_to_clear); - - mutex_unlock(&ams_info.lock); -} - -/* Call with ams_info.lock held! */ -int ams_sensor_attach(void) -{ - int result; - const u32 *prop; - - /* Get orientation */ - prop = of_get_property(ams_info.of_node, "orientation", NULL); - if (!prop) - return -ENODEV; - ams_info.orient1 = *prop; - ams_info.orient2 = *(prop + 1); - - /* Register freefall interrupt handler */ - result = pmf_register_irq_client(ams_info.of_node, - "accel-int-1", - &ams_freefall_client); - if (result < 0) - return -ENODEV; - - /* Reset saved irqs */ - ams_info.worker_irqs = 0; - - /* Register shock interrupt handler */ - result = pmf_register_irq_client(ams_info.of_node, - "accel-int-2", - &ams_shock_client); - if (result < 0) - goto release_freefall; - - /* Create device */ - ams_info.of_dev = of_platform_device_create(ams_info.of_node, "ams", NULL); - if (!ams_info.of_dev) { - result = -ENODEV; - goto release_shock; - } - - /* Create attributes */ - result = device_create_file(&ams_info.of_dev->dev, &dev_attr_current); - if (result) - goto release_of; - - ams_info.vflag = !!(ams_info.get_vendor() & 0x10); - - /* Init input device */ - result = ams_input_init(); - if (result) - goto release_device_file; - - return result; -release_device_file: - device_remove_file(&ams_info.of_dev->dev, &dev_attr_current); -release_of: - of_device_unregister(ams_info.of_dev); -release_shock: - pmf_unregister_irq_client(&ams_shock_client); -release_freefall: - pmf_unregister_irq_client(&ams_freefall_client); - return result; -} - -int __init ams_init(void) -{ - struct device_node *np; - - spin_lock_init(&ams_info.irq_lock); - mutex_init(&ams_info.lock); - INIT_WORK(&ams_info.worker, ams_worker); - -#ifdef CONFIG_SENSORS_AMS_I2C - np = of_find_node_by_name(NULL, "accelerometer"); - if (np && of_device_is_compatible(np, "AAPL,accelerometer_1")) - /* Found I2C motion sensor */ - return ams_i2c_init(np); -#endif - -#ifdef CONFIG_SENSORS_AMS_PMU - np = of_find_node_by_name(NULL, "sms"); - if (np && of_device_is_compatible(np, "sms")) - /* Found PMU motion sensor */ - return ams_pmu_init(np); -#endif - return -ENODEV; -} - -void ams_sensor_detach(void) -{ - /* Remove input device */ - ams_input_exit(); - - /* Remove attributes */ - device_remove_file(&ams_info.of_dev->dev, &dev_attr_current); - - /* Flush interrupt worker - * - * We do this after ams_info.exit(), because an interrupt might - * have arrived before disabling them. - */ - flush_scheduled_work(); - - /* Remove device */ - of_device_unregister(ams_info.of_dev); - - /* Remove handler */ - pmf_unregister_irq_client(&ams_shock_client); - pmf_unregister_irq_client(&ams_freefall_client); -} - -static void __exit ams_exit(void) -{ - /* Shut down implementation */ - ams_info.exit(); -} - -MODULE_AUTHOR("Stelian Pop, Michael Hanselmann"); -MODULE_DESCRIPTION("Apple Motion Sensor driver"); -MODULE_LICENSE("GPL"); - -module_init(ams_init); -module_exit(ams_exit); diff --git a/drivers/hwmon/ams/ams-i2c.c b/drivers/hwmon/ams/ams-i2c.c deleted file mode 100644 index abeecd27b48..00000000000 --- a/drivers/hwmon/ams/ams-i2c.c +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Apple Motion Sensor driver (I2C variant) - * - * Copyright (C) 2005 Stelian Pop (stelian@popies.net) - * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) - * - * Clean room implementation based on the reverse engineered Mac OS X driver by - * Johannes Berg <johannes@sipsolutions.net>, documentation available at - * http://johannes.sipsolutions.net/PowerBook/Apple_Motion_Sensor_Specification - * - * 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. - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/delay.h> - -#include "ams.h" - -/* AMS registers */ -#define AMS_COMMAND 0x00 /* command register */ -#define AMS_STATUS 0x01 /* status register */ -#define AMS_CTRL1 0x02 /* read control 1 (number of values) */ -#define AMS_CTRL2 0x03 /* read control 2 (offset?) */ -#define AMS_CTRL3 0x04 /* read control 3 (size of each value?) */ -#define AMS_DATA1 0x05 /* read data 1 */ -#define AMS_DATA2 0x06 /* read data 2 */ -#define AMS_DATA3 0x07 /* read data 3 */ -#define AMS_DATA4 0x08 /* read data 4 */ -#define AMS_DATAX 0x20 /* data X */ -#define AMS_DATAY 0x21 /* data Y */ -#define AMS_DATAZ 0x22 /* data Z */ -#define AMS_FREEFALL 0x24 /* freefall int control */ -#define AMS_SHOCK 0x25 /* shock int control */ -#define AMS_SENSLOW 0x26 /* sensitivity low limit */ -#define AMS_SENSHIGH 0x27 /* sensitivity high limit */ -#define AMS_CTRLX 0x28 /* control X */ -#define AMS_CTRLY 0x29 /* control Y */ -#define AMS_CTRLZ 0x2A /* control Z */ -#define AMS_UNKNOWN1 0x2B /* unknown 1 */ -#define AMS_UNKNOWN2 0x2C /* unknown 2 */ -#define AMS_UNKNOWN3 0x2D /* unknown 3 */ -#define AMS_VENDOR 0x2E /* vendor */ - -/* AMS commands - use with the AMS_COMMAND register */ -enum ams_i2c_cmd { - AMS_CMD_NOOP = 0, - AMS_CMD_VERSION, - AMS_CMD_READMEM, - AMS_CMD_WRITEMEM, - AMS_CMD_ERASEMEM, - AMS_CMD_READEE, - AMS_CMD_WRITEEE, - AMS_CMD_RESET, - AMS_CMD_START, -}; - -static int ams_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int ams_i2c_remove(struct i2c_client *client); - -static const struct i2c_device_id ams_id[] = { - { "ams", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ams_id); - -static struct i2c_driver ams_i2c_driver = { - .driver = { - .name = "ams", - .owner = THIS_MODULE, - }, - .probe = ams_i2c_probe, - .remove = ams_i2c_remove, - .id_table = ams_id, -}; - -static s32 ams_i2c_read(u8 reg) -{ - return i2c_smbus_read_byte_data(ams_info.i2c_client, reg); -} - -static int ams_i2c_write(u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(ams_info.i2c_client, reg, value); -} - -static int ams_i2c_cmd(enum ams_i2c_cmd cmd) -{ - s32 result; - int count = 3; - - ams_i2c_write(AMS_COMMAND, cmd); - msleep(5); - - while (count--) { - result = ams_i2c_read(AMS_COMMAND); - if (result == 0 || result & 0x80) - return 0; - - schedule_timeout_uninterruptible(HZ / 20); - } - - return -1; -} - -static void ams_i2c_set_irq(enum ams_irq reg, char enable) -{ - if (reg & AMS_IRQ_FREEFALL) { - u8 val = ams_i2c_read(AMS_CTRLX); - if (enable) - val |= 0x80; - else - val &= ~0x80; - ams_i2c_write(AMS_CTRLX, val); - } - - if (reg & AMS_IRQ_SHOCK) { - u8 val = ams_i2c_read(AMS_CTRLY); - if (enable) - val |= 0x80; - else - val &= ~0x80; - ams_i2c_write(AMS_CTRLY, val); - } - - if (reg & AMS_IRQ_GLOBAL) { - u8 val = ams_i2c_read(AMS_CTRLZ); - if (enable) - val |= 0x80; - else - val &= ~0x80; - ams_i2c_write(AMS_CTRLZ, val); - } -} - -static void ams_i2c_clear_irq(enum ams_irq reg) -{ - if (reg & AMS_IRQ_FREEFALL) - ams_i2c_write(AMS_FREEFALL, 0); - - if (reg & AMS_IRQ_SHOCK) - ams_i2c_write(AMS_SHOCK, 0); -} - -static u8 ams_i2c_get_vendor(void) -{ - return ams_i2c_read(AMS_VENDOR); -} - -static void ams_i2c_get_xyz(s8 *x, s8 *y, s8 *z) -{ - *x = ams_i2c_read(AMS_DATAX); - *y = ams_i2c_read(AMS_DATAY); - *z = ams_i2c_read(AMS_DATAZ); -} - -static int ams_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int vmaj, vmin; - int result; - - /* There can be only one */ - if (unlikely(ams_info.has_device)) - return -ENODEV; - - ams_info.i2c_client = client; - - if (ams_i2c_cmd(AMS_CMD_RESET)) { - printk(KERN_INFO "ams: Failed to reset the device\n"); - return -ENODEV; - } - - if (ams_i2c_cmd(AMS_CMD_START)) { - printk(KERN_INFO "ams: Failed to start the device\n"); - return -ENODEV; - } - - /* get version/vendor information */ - ams_i2c_write(AMS_CTRL1, 0x02); - ams_i2c_write(AMS_CTRL2, 0x85); - ams_i2c_write(AMS_CTRL3, 0x01); - - ams_i2c_cmd(AMS_CMD_READMEM); - - vmaj = ams_i2c_read(AMS_DATA1); - vmin = ams_i2c_read(AMS_DATA2); - if (vmaj != 1 || vmin != 52) { - printk(KERN_INFO "ams: Incorrect device version (%d.%d)\n", - vmaj, vmin); - return -ENODEV; - } - - ams_i2c_cmd(AMS_CMD_VERSION); - - vmaj = ams_i2c_read(AMS_DATA1); - vmin = ams_i2c_read(AMS_DATA2); - if (vmaj != 0 || vmin != 1) { - printk(KERN_INFO "ams: Incorrect firmware version (%d.%d)\n", - vmaj, vmin); - return -ENODEV; - } - - /* Disable interrupts */ - ams_i2c_set_irq(AMS_IRQ_ALL, 0); - - result = ams_sensor_attach(); - if (result < 0) - return result; - - /* Set default values */ - ams_i2c_write(AMS_SENSLOW, 0x15); - ams_i2c_write(AMS_SENSHIGH, 0x60); - ams_i2c_write(AMS_CTRLX, 0x08); - ams_i2c_write(AMS_CTRLY, 0x0F); - ams_i2c_write(AMS_CTRLZ, 0x4F); - ams_i2c_write(AMS_UNKNOWN1, 0x14); - - /* Clear interrupts */ - ams_i2c_clear_irq(AMS_IRQ_ALL); - - ams_info.has_device = 1; - - /* Enable interrupts */ - ams_i2c_set_irq(AMS_IRQ_ALL, 1); - - printk(KERN_INFO "ams: Found I2C based motion sensor\n"); - - return 0; -} - -static int ams_i2c_remove(struct i2c_client *client) -{ - if (ams_info.has_device) { - ams_sensor_detach(); - - /* Disable interrupts */ - ams_i2c_set_irq(AMS_IRQ_ALL, 0); - - /* Clear interrupts */ - ams_i2c_clear_irq(AMS_IRQ_ALL); - - printk(KERN_INFO "ams: Unloading\n"); - - ams_info.has_device = 0; - } - - return 0; -} - -static void ams_i2c_exit(void) -{ - i2c_del_driver(&ams_i2c_driver); -} - -int __init ams_i2c_init(struct device_node *np) -{ - int result; - - /* Set implementation stuff */ - ams_info.of_node = np; - ams_info.exit = ams_i2c_exit; - ams_info.get_vendor = ams_i2c_get_vendor; - ams_info.get_xyz = ams_i2c_get_xyz; - ams_info.clear_irq = ams_i2c_clear_irq; - ams_info.bustype = BUS_I2C; - - result = i2c_add_driver(&ams_i2c_driver); - - return result; -} diff --git a/drivers/hwmon/ams/ams-input.c b/drivers/hwmon/ams/ams-input.c deleted file mode 100644 index 8a712392cd3..00000000000 --- a/drivers/hwmon/ams/ams-input.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Apple Motion Sensor driver (joystick emulation) - * - * Copyright (C) 2005 Stelian Pop (stelian@popies.net) - * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) - * - * 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. - */ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/delay.h> - -#include "ams.h" - -static unsigned int joystick; -module_param(joystick, bool, S_IRUGO); -MODULE_PARM_DESC(joystick, "Enable the input class device on module load"); - -static unsigned int invert; -module_param(invert, bool, S_IWUSR | S_IRUGO); -MODULE_PARM_DESC(invert, "Invert input data on X and Y axis"); - -static DEFINE_MUTEX(ams_input_mutex); - -static void ams_idev_poll(struct input_polled_dev *dev) -{ - struct input_dev *idev = dev->input; - s8 x, y, z; - - mutex_lock(&ams_info.lock); - - ams_sensors(&x, &y, &z); - - x -= ams_info.xcalib; - y -= ams_info.ycalib; - z -= ams_info.zcalib; - - input_report_abs(idev, ABS_X, invert ? -x : x); - input_report_abs(idev, ABS_Y, invert ? -y : y); - input_report_abs(idev, ABS_Z, z); - - input_sync(idev); - - mutex_unlock(&ams_info.lock); -} - -/* Call with ams_info.lock held! */ -static int ams_input_enable(void) -{ - struct input_dev *input; - s8 x, y, z; - int error; - - ams_sensors(&x, &y, &z); - ams_info.xcalib = x; - ams_info.ycalib = y; - ams_info.zcalib = z; - - ams_info.idev = input_allocate_polled_device(); - if (!ams_info.idev) - return -ENOMEM; - - ams_info.idev->poll = ams_idev_poll; - ams_info.idev->poll_interval = 25; - - input = ams_info.idev->input; - input->name = "Apple Motion Sensor"; - input->id.bustype = ams_info.bustype; - input->id.vendor = 0; - input->dev.parent = &ams_info.of_dev->dev; - - input_set_abs_params(input, ABS_X, -50, 50, 3, 0); - input_set_abs_params(input, ABS_Y, -50, 50, 3, 0); - input_set_abs_params(input, ABS_Z, -50, 50, 3, 0); - - set_bit(EV_ABS, input->evbit); - set_bit(EV_KEY, input->evbit); - set_bit(BTN_TOUCH, input->keybit); - - error = input_register_polled_device(ams_info.idev); - if (error) { - input_free_polled_device(ams_info.idev); - ams_info.idev = NULL; - return error; - } - - joystick = 1; - - return 0; -} - -static void ams_input_disable(void) -{ - if (ams_info.idev) { - input_unregister_polled_device(ams_info.idev); - input_free_polled_device(ams_info.idev); - ams_info.idev = NULL; - } - - joystick = 0; -} - -static ssize_t ams_input_show_joystick(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%d\n", joystick); -} - -static ssize_t ams_input_store_joystick(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - unsigned long enable; - int error = 0; - - if (strict_strtoul(buf, 0, &enable) || enable > 1) - return -EINVAL; - - mutex_lock(&ams_input_mutex); - - if (enable != joystick) { - if (enable) - error = ams_input_enable(); - else - ams_input_disable(); - } - - mutex_unlock(&ams_input_mutex); - - return error ? error : count; -} - -static DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR, - ams_input_show_joystick, ams_input_store_joystick); - -int ams_input_init(void) -{ - if (joystick) - ams_input_enable(); - - return device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick); -} - -void ams_input_exit(void) -{ - device_remove_file(&ams_info.of_dev->dev, &dev_attr_joystick); - - mutex_lock(&ams_input_mutex); - ams_input_disable(); - mutex_unlock(&ams_input_mutex); -} diff --git a/drivers/hwmon/ams/ams-pmu.c b/drivers/hwmon/ams/ams-pmu.c deleted file mode 100644 index 4f61b3ee1b0..00000000000 --- a/drivers/hwmon/ams/ams-pmu.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Apple Motion Sensor driver (PMU variant) - * - * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) - * - * 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. - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/adb.h> -#include <linux/pmu.h> - -#include "ams.h" - -/* Attitude */ -#define AMS_X 0x00 -#define AMS_Y 0x01 -#define AMS_Z 0x02 - -/* Not exactly known, maybe chip vendor */ -#define AMS_VENDOR 0x03 - -/* Freefall registers */ -#define AMS_FF_CLEAR 0x04 -#define AMS_FF_ENABLE 0x05 -#define AMS_FF_LOW_LIMIT 0x06 -#define AMS_FF_DEBOUNCE 0x07 - -/* Shock registers */ -#define AMS_SHOCK_CLEAR 0x08 -#define AMS_SHOCK_ENABLE 0x09 -#define AMS_SHOCK_HIGH_LIMIT 0x0a -#define AMS_SHOCK_DEBOUNCE 0x0b - -/* Global interrupt and power control register */ -#define AMS_CONTROL 0x0c - -static u8 ams_pmu_cmd; - -static void ams_pmu_req_complete(struct adb_request *req) -{ - complete((struct completion *)req->arg); -} - -/* Only call this function from task context */ -static void ams_pmu_set_register(u8 reg, u8 value) -{ - static struct adb_request req; - DECLARE_COMPLETION(req_complete); - - req.arg = &req_complete; - if (pmu_request(&req, ams_pmu_req_complete, 4, ams_pmu_cmd, 0x00, reg, value)) - return; - - wait_for_completion(&req_complete); -} - -/* Only call this function from task context */ -static u8 ams_pmu_get_register(u8 reg) -{ - static struct adb_request req; - DECLARE_COMPLETION(req_complete); - - req.arg = &req_complete; - if (pmu_request(&req, ams_pmu_req_complete, 3, ams_pmu_cmd, 0x01, reg)) - return 0; - - wait_for_completion(&req_complete); - - if (req.reply_len > 0) - return req.reply[0]; - else - return 0; -} - -/* Enables or disables the specified interrupts */ -static void ams_pmu_set_irq(enum ams_irq reg, char enable) -{ - if (reg & AMS_IRQ_FREEFALL) { - u8 val = ams_pmu_get_register(AMS_FF_ENABLE); - if (enable) - val |= 0x80; - else - val &= ~0x80; - ams_pmu_set_register(AMS_FF_ENABLE, val); - } - - if (reg & AMS_IRQ_SHOCK) { - u8 val = ams_pmu_get_register(AMS_SHOCK_ENABLE); - if (enable) - val |= 0x80; - else - val &= ~0x80; - ams_pmu_set_register(AMS_SHOCK_ENABLE, val); - } - - if (reg & AMS_IRQ_GLOBAL) { - u8 val = ams_pmu_get_register(AMS_CONTROL); - if (enable) - val |= 0x80; - else - val &= ~0x80; - ams_pmu_set_register(AMS_CONTROL, val); - } -} - -static void ams_pmu_clear_irq(enum ams_irq reg) -{ - if (reg & AMS_IRQ_FREEFALL) - ams_pmu_set_register(AMS_FF_CLEAR, 0x00); - - if (reg & AMS_IRQ_SHOCK) - ams_pmu_set_register(AMS_SHOCK_CLEAR, 0x00); -} - -static u8 ams_pmu_get_vendor(void) -{ - return ams_pmu_get_register(AMS_VENDOR); -} - -static void ams_pmu_get_xyz(s8 *x, s8 *y, s8 *z) -{ - *x = ams_pmu_get_register(AMS_X); - *y = ams_pmu_get_register(AMS_Y); - *z = ams_pmu_get_register(AMS_Z); -} - -static void ams_pmu_exit(void) -{ - ams_sensor_detach(); - - /* Disable interrupts */ - ams_pmu_set_irq(AMS_IRQ_ALL, 0); - - /* Clear interrupts */ - ams_pmu_clear_irq(AMS_IRQ_ALL); - - ams_info.has_device = 0; - - printk(KERN_INFO "ams: Unloading\n"); -} - -int __init ams_pmu_init(struct device_node *np) -{ - const u32 *prop; - int result; - - /* Set implementation stuff */ - ams_info.of_node = np; - ams_info.exit = ams_pmu_exit; - ams_info.get_vendor = ams_pmu_get_vendor; - ams_info.get_xyz = ams_pmu_get_xyz; - ams_info.clear_irq = ams_pmu_clear_irq; - ams_info.bustype = BUS_HOST; - - /* Get PMU command, should be 0x4e, but we can never know */ - prop = of_get_property(ams_info.of_node, "reg", NULL); - if (!prop) - return -ENODEV; - - ams_pmu_cmd = ((*prop) >> 8) & 0xff; - - /* Disable interrupts */ - ams_pmu_set_irq(AMS_IRQ_ALL, 0); - - /* Clear interrupts */ - ams_pmu_clear_irq(AMS_IRQ_ALL); - - result = ams_sensor_attach(); - if (result < 0) - return result; - - /* Set default values */ - ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15); - ams_pmu_set_register(AMS_FF_ENABLE, 0x08); - ams_pmu_set_register(AMS_FF_DEBOUNCE, 0x14); - - ams_pmu_set_register(AMS_SHOCK_HIGH_LIMIT, 0x60); - ams_pmu_set_register(AMS_SHOCK_ENABLE, 0x0f); - ams_pmu_set_register(AMS_SHOCK_DEBOUNCE, 0x14); - - ams_pmu_set_register(AMS_CONTROL, 0x4f); - - /* Clear interrupts */ - ams_pmu_clear_irq(AMS_IRQ_ALL); - - ams_info.has_device = 1; - - /* Enable interrupts */ - ams_pmu_set_irq(AMS_IRQ_ALL, 1); - - printk(KERN_INFO "ams: Found PMU based motion sensor\n"); - - return 0; -} diff --git a/drivers/hwmon/ams/ams.h b/drivers/hwmon/ams/ams.h deleted file mode 100644 index 90f094d4545..00000000000 --- a/drivers/hwmon/ams/ams.h +++ /dev/null @@ -1,70 +0,0 @@ -#include <linux/i2c.h> -#include <linux/input-polldev.h> -#include <linux/kthread.h> -#include <linux/mutex.h> -#include <linux/spinlock.h> -#include <linux/types.h> -#include <linux/of_device.h> - -enum ams_irq { - AMS_IRQ_FREEFALL = 0x01, - AMS_IRQ_SHOCK = 0x02, - AMS_IRQ_GLOBAL = 0x04, - AMS_IRQ_ALL = - AMS_IRQ_FREEFALL | - AMS_IRQ_SHOCK | - AMS_IRQ_GLOBAL, -}; - -struct ams { - /* Locks */ - spinlock_t irq_lock; - struct mutex lock; - - /* General properties */ - struct device_node *of_node; - struct platform_device *of_dev; - char has_device; - char vflag; - u32 orient1; - u32 orient2; - - /* Interrupt worker */ - struct work_struct worker; - u8 worker_irqs; - - /* Implementation - * - * Only call these functions with the main lock held. - */ - void (*exit)(void); - - void (*get_xyz)(s8 *x, s8 *y, s8 *z); - u8 (*get_vendor)(void); - - void (*clear_irq)(enum ams_irq reg); - -#ifdef CONFIG_SENSORS_AMS_I2C - /* I2C properties */ - struct i2c_client *i2c_client; -#endif - - /* Joystick emulation */ - struct input_polled_dev *idev; - __u16 bustype; - - /* calibrated null values */ - int xcalib, ycalib, zcalib; -}; - -extern struct ams ams_info; - -extern void ams_sensors(s8 *x, s8 *y, s8 *z); -extern int ams_sensor_attach(void); -extern void ams_sensor_detach(void); - -extern int ams_pmu_init(struct device_node *np); -extern int ams_i2c_init(struct device_node *np); - -extern int ams_input_init(void); -extern void ams_input_exit(void); diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c index 89b4f3babe8..d2596cec18b 100644 --- a/drivers/hwmon/asc7621.c +++ b/drivers/hwmon/asc7621.c @@ -28,7 +28,7 @@ #include <linux/mutex.h> /* Addresses to scan */ -static unsigned short normal_i2c[] = { +static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; @@ -52,7 +52,7 @@ struct asc7621_chip { u8 company_id; u8 verstep_reg; u8 verstep_id; - unsigned short *addresses; + const unsigned short *addresses; }; static struct asc7621_chip asc7621_chips[] = { diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index f7701295937..14a5d981be7 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -15,7 +15,9 @@ * IT8716F Super I/O chip w/LPC interface * IT8718F Super I/O chip w/LPC interface * IT8720F Super I/O chip w/LPC interface + * IT8721F Super I/O chip w/LPC interface * IT8726F Super I/O chip w/LPC interface + * IT8758E Super I/O chip w/LPC interface * Sis950 A clone of the IT8705F * * Copyright (C) 2001 Chris Gauthron @@ -54,7 +56,7 @@ #define DRVNAME "it87" -enum chips { it87, it8712, it8716, it8718, it8720 }; +enum chips { it87, it8712, it8716, it8718, it8720, it8721 }; static unsigned short force_id; module_param(force_id, ushort, 0); @@ -126,6 +128,7 @@ superio_exit(void) #define IT8716F_DEVID 0x8716 #define IT8718F_DEVID 0x8718 #define IT8720F_DEVID 0x8720 +#define IT8721F_DEVID 0x8721 #define IT8726F_DEVID 0x8726 #define IT87_ACT_REG 0x30 #define IT87_BASE_REG 0x60 @@ -202,56 +205,6 @@ static const u8 IT87_REG_FANX_MIN[] = { 0x1b, 0x1c, 0x1d, 0x85, 0x87 }; #define IT87_REG_AUTO_TEMP(nr, i) (0x60 + (nr) * 8 + (i)) #define IT87_REG_AUTO_PWM(nr, i) (0x65 + (nr) * 8 + (i)) -#define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8)/16),0,255)) -#define IN_FROM_REG(val) ((val) * 16) - -static inline u8 FAN_TO_REG(long rpm, int div) -{ - if (rpm == 0) - return 255; - rpm = SENSORS_LIMIT(rpm, 1, 1000000); - return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, - 254); -} - -static inline u16 FAN16_TO_REG(long rpm) -{ - if (rpm == 0) - return 0xffff; - return SENSORS_LIMIT((1350000 + rpm) / (rpm * 2), 1, 0xfffe); -} - -#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) -/* The divider is fixed to 2 in 16-bit mode */ -#define FAN16_FROM_REG(val) ((val)==0?-1:(val)==0xffff?0:1350000/((val)*2)) - -#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-500)/1000):\ - ((val)+500)/1000),-128,127)) -#define TEMP_FROM_REG(val) ((val) * 1000) - -#define PWM_TO_REG(val) ((val) >> 1) -#define PWM_FROM_REG(val) (((val)&0x7f) << 1) - -static int DIV_TO_REG(int val) -{ - int answer = 0; - while (answer < 7 && (val >>= 1)) - answer++; - return answer; -} -#define DIV_FROM_REG(val) (1 << (val)) - -static const unsigned int pwm_freq[8] = { - 48000000 / 128, - 24000000 / 128, - 12000000 / 128, - 8000000 / 128, - 6000000 / 128, - 3000000 / 128, - 1500000 / 128, - 750000 / 128, -}; - struct it87_sio_data { enum chips type; @@ -279,6 +232,7 @@ struct it87_data { char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ + u16 in_scaled; /* Internal voltage sensors are scaled */ u8 in[9]; /* Register value */ u8 in_max[8]; /* Register value */ u8 in_min[8]; /* Register value */ @@ -310,6 +264,96 @@ struct it87_data { s8 auto_temp[3][5]; /* [nr][0] is point1_temp_hyst */ }; +static u8 in_to_reg(const struct it87_data *data, int nr, long val) +{ + long lsb; + + if (data->type == it8721) { + if (data->in_scaled & (1 << nr)) + lsb = 24; + else + lsb = 12; + } else + lsb = 16; + + val = DIV_ROUND_CLOSEST(val, lsb); + return SENSORS_LIMIT(val, 0, 255); +} + +static int in_from_reg(const struct it87_data *data, int nr, int val) +{ + if (data->type == it8721) { + if (data->in_scaled & (1 << nr)) + return val * 24; + else + return val * 12; + } else + return val * 16; +} + +static inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +static inline u16 FAN16_TO_REG(long rpm) +{ + if (rpm == 0) + return 0xffff; + return SENSORS_LIMIT((1350000 + rpm) / (rpm * 2), 1, 0xfffe); +} + +#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : (val) == 255 ? 0 : \ + 1350000 / ((val) * (div))) +/* The divider is fixed to 2 in 16-bit mode */ +#define FAN16_FROM_REG(val) ((val) == 0 ? -1 : (val) == 0xffff ? 0 : \ + 1350000 / ((val) * 2)) + +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val) < 0 ? (((val) - 500) / 1000) : \ + ((val) + 500) / 1000), -128, 127)) +#define TEMP_FROM_REG(val) ((val) * 1000) + +static u8 pwm_to_reg(const struct it87_data *data, long val) +{ + if (data->type == it8721) + return val; + else + return val >> 1; +} + +static int pwm_from_reg(const struct it87_data *data, u8 reg) +{ + if (data->type == it8721) + return reg; + else + return (reg & 0x7f) << 1; +} + + +static int DIV_TO_REG(int val) +{ + int answer = 0; + while (answer < 7 && (val >>= 1)) + answer++; + return answer; +} +#define DIV_FROM_REG(val) (1 << (val)) + +static const unsigned int pwm_freq[8] = { + 48000000 / 128, + 24000000 / 128, + 12000000 / 128, + 8000000 / 128, + 6000000 / 128, + 3000000 / 128, + 1500000 / 128, + 750000 / 128, +}; + static inline int has_16bit_fans(const struct it87_data *data) { /* IT8705F Datasheet 0.4.1, 3h == Version G. @@ -319,7 +363,8 @@ static inline int has_16bit_fans(const struct it87_data *data) || (data->type == it8712 && data->revision >= 0x08) || data->type == it8716 || data->type == it8718 - || data->type == it8720; + || data->type == it8720 + || data->type == it8721; } static inline int has_old_autopwm(const struct it87_data *data) @@ -357,7 +402,7 @@ static ssize_t show_in(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr])); + return sprintf(buf, "%d\n", in_from_reg(data, nr, data->in[nr])); } static ssize_t show_in_min(struct device *dev, struct device_attribute *attr, @@ -367,7 +412,7 @@ static ssize_t show_in_min(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr])); + return sprintf(buf, "%d\n", in_from_reg(data, nr, data->in_min[nr])); } static ssize_t show_in_max(struct device *dev, struct device_attribute *attr, @@ -377,7 +422,7 @@ static ssize_t show_in_max(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr])); + return sprintf(buf, "%d\n", in_from_reg(data, nr, data->in_max[nr])); } static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, @@ -393,7 +438,7 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&data->update_lock); - data->in_min[nr] = IN_TO_REG(val); + data->in_min[nr] = in_to_reg(data, nr, val); it87_write_value(data, IT87_REG_VIN_MIN(nr), data->in_min[nr]); mutex_unlock(&data->update_lock); @@ -412,7 +457,7 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&data->update_lock); - data->in_max[nr] = IN_TO_REG(val); + data->in_max[nr] = in_to_reg(data, nr, val); it87_write_value(data, IT87_REG_VIN_MAX(nr), data->in_max[nr]); mutex_unlock(&data->update_lock); @@ -642,7 +687,8 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm_duty[nr])); + return sprintf(buf, "%d\n", + pwm_from_reg(data, data->pwm_duty[nr])); } static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr, char *buf) @@ -812,7 +858,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&data->update_lock); - data->pwm_duty[nr] = PWM_TO_REG(val); + data->pwm_duty[nr] = pwm_to_reg(data, val); /* If we are in manual mode, write the duty cycle immediately; * otherwise, just store it for later use. */ if (!(data->pwm_ctrl[nr] & 0x80)) { @@ -916,7 +962,8 @@ static ssize_t show_auto_pwm(struct device *dev, int nr = sensor_attr->nr; int point = sensor_attr->index; - return sprintf(buf, "%d\n", PWM_FROM_REG(data->auto_pwm[nr][point])); + return sprintf(buf, "%d\n", + pwm_from_reg(data, data->auto_pwm[nr][point])); } static ssize_t set_auto_pwm(struct device *dev, @@ -933,7 +980,7 @@ static ssize_t set_auto_pwm(struct device *dev, return -EINVAL; mutex_lock(&data->update_lock); - data->auto_pwm[nr][point] = PWM_TO_REG(val); + data->auto_pwm[nr][point] = pwm_to_reg(data, val); it87_write_value(data, IT87_REG_AUTO_PWM(nr, point), data->auto_pwm[nr][point]); mutex_unlock(&data->update_lock); @@ -1203,9 +1250,16 @@ static ssize_t show_label(struct device *dev, struct device_attribute *attr, "5VSB", "Vbat", }; + static const char *labels_it8721[] = { + "+3.3V", + "3VSB", + "Vbat", + }; + struct it87_data *data = dev_get_drvdata(dev); int nr = to_sensor_dev_attr(attr)->index; - return sprintf(buf, "%s\n", labels[nr]); + return sprintf(buf, "%s\n", data->type == it8721 ? labels_it8721[nr] + : labels[nr]); } static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, 0); static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_label, NULL, 1); @@ -1490,6 +1544,9 @@ static int __init it87_find(unsigned short *address, case IT8720F_DEVID: sio_data->type = it8720; break; + case IT8721F_DEVID: + sio_data->type = it8721; + break; case 0xffff: /* No device at all */ goto exit; default: @@ -1530,11 +1587,17 @@ static int __init it87_find(unsigned short *address, int reg; superio_select(GPIO); - /* We need at least 4 VID pins */ + reg = superio_inb(IT87_SIO_GPIO3_REG); - if (reg & 0x0f) { - pr_info("it87: VID is disabled (pins used for GPIO)\n"); + if (sio_data->type == it8721) { + /* The IT8721F/IT8758E doesn't have VID pins at all */ sio_data->skip_vid = 1; + } else { + /* We need at least 4 VID pins */ + if (reg & 0x0f) { + pr_info("it87: VID is disabled (pins used for GPIO)\n"); + sio_data->skip_vid = 1; + } } /* Check if fan3 is there or not */ @@ -1572,7 +1635,7 @@ static int __init it87_find(unsigned short *address, } if (reg & (1 << 0)) sio_data->internal |= (1 << 0); - if (reg & (1 << 1)) + if ((reg & (1 << 1)) || sio_data->type == it8721) sio_data->internal |= (1 << 1); sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; @@ -1650,6 +1713,7 @@ static int __devinit it87_probe(struct platform_device *pdev) "it8716", "it8718", "it8720", + "it8721", }; res = platform_get_resource(pdev, IORESOURCE_IO, 0); @@ -1686,6 +1750,16 @@ static int __devinit it87_probe(struct platform_device *pdev) /* Check PWM configuration */ enable_pwm_interface = it87_check_pwm(dev); + /* Starting with IT8721F, we handle scaling of internal voltages */ + if (data->type == it8721) { + if (sio_data->internal & (1 << 0)) + data->in_scaled |= (1 << 3); /* in3 is AVCC */ + if (sio_data->internal & (1 << 1)) + data->in_scaled |= (1 << 7); /* in7 is VSB */ + if (sio_data->internal & (1 << 2)) + data->in_scaled |= (1 << 8); /* in8 is Vbat */ + } + /* Initialize the IT87 chip */ it87_init_device(pdev); @@ -2051,7 +2125,7 @@ static struct it87_data *it87_update_device(struct device *dev) data->sensor = it87_read_value(data, IT87_REG_TEMP_ENABLE); /* The 8705 does not have VID capability. - The 8718 and the 8720 don't use IT87_REG_VID for the + The 8718 and later don't use IT87_REG_VID for the same purpose. */ if (data->type == it8712 || data->type == it8716) { data->vid = it87_read_value(data, IT87_REG_VID); @@ -2151,7 +2225,7 @@ static void __exit sm_it87_exit(void) MODULE_AUTHOR("Chris Gauthron, " "Jean Delvare <khali@linux-fr.org>"); -MODULE_DESCRIPTION("IT8705F/8712F/8716F/8718F/8720F/8726F, SiS950 driver"); +MODULE_DESCRIPTION("IT8705F/IT871xF/IT872xF hardware monitoring driver"); module_param(update_vbat, bool, 0); MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value"); module_param(fix_pwm_polarity, bool, 0); diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c index 39ead2a4d3c..418496f1302 100644 --- a/drivers/hwmon/k8temp.c +++ b/drivers/hwmon/k8temp.c @@ -191,38 +191,31 @@ static int __devinit k8temp_probe(struct pci_dev *pdev, model = boot_cpu_data.x86_model; stepping = boot_cpu_data.x86_mask; - switch (boot_cpu_data.x86) { - case 0xf: - /* feature available since SH-C0, exclude older revisions */ - if (((model == 4) && (stepping == 0)) || - ((model == 5) && (stepping <= 1))) { - err = -ENODEV; - goto exit_free; - } - - /* - * AMD NPT family 0fh, i.e. RevF and RevG: - * meaning of SEL_CORE bit is inverted - */ - if (model >= 0x40) { - data->swap_core_select = 1; - dev_warn(&pdev->dev, "Temperature readouts might be " - "wrong - check erratum #141\n"); - } - - if (is_rev_g_desktop(model)) { - /* - * RevG desktop CPUs (i.e. no socket S1G1 or - * ASB1 parts) need additional offset, - * otherwise reported temperature is below - * ambient temperature - */ - data->temp_offset = 21000; - } + /* feature available since SH-C0, exclude older revisions */ + if (((model == 4) && (stepping == 0)) || + ((model == 5) && (stepping <= 1))) { + err = -ENODEV; + goto exit_free; + } - break; + /* + * AMD NPT family 0fh, i.e. RevF and RevG: + * meaning of SEL_CORE bit is inverted + */ + if (model >= 0x40) { + data->swap_core_select = 1; + dev_warn(&pdev->dev, "Temperature readouts might be wrong - " + "check erratum #141\n"); } + /* + * RevG desktop CPUs (i.e. no socket S1G1 or ASB1 parts) need + * additional offset, otherwise reported temperature is below + * ambient temperature + */ + if (is_rev_g_desktop(model)) + data->temp_offset = 21000; + pci_read_config_byte(pdev, REG_TEMP, &scfg); scfg &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */ pci_write_config_byte(pdev, REG_TEMP, scfg); diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index ab5b87a8167..f36eb80d227 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -1,22 +1,22 @@ /* - lm75.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> - - 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. -*/ + * lm75.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> + * + * 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> @@ -103,7 +103,12 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da, struct i2c_client *client = to_i2c_client(dev); struct lm75_data *data = i2c_get_clientdata(client); int nr = attr->index; - long temp = simple_strtol(buf, NULL, 10); + long temp; + int error; + + error = strict_strtol(buf, 10, &temp); + if (error) + return error; mutex_lock(&data->update_lock); data->temp[nr] = LM75_TEMP_TO_REG(temp); @@ -335,9 +340,11 @@ static struct i2c_driver lm75_driver = { /* register access */ -/* All registers are word-sized, except for the configuration register. - LM75 uses a high-byte first convention, which is exactly opposite to - the SMBus standard. */ +/* + * All registers are word-sized, except for the configuration register. + * LM75 uses a high-byte first convention, which is exactly opposite to + * the SMBus standard. + */ static int lm75_read_value(struct i2c_client *client, u8 reg) { int value; diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index b3841a61559..1e229847f37 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -64,9 +64,12 @@ enum chips { #define LM85_REG_VERSTEP 0x3f #define ADT7468_REG_CFG5 0x7c -#define ADT7468_OFF64 0x01 +#define ADT7468_OFF64 (1 << 0) +#define ADT7468_HFPWM (1 << 1) #define IS_ADT7468_OFF64(data) \ ((data)->type == adt7468 && !((data)->cfg5 & ADT7468_OFF64)) +#define IS_ADT7468_HFPWM(data) \ + ((data)->type == adt7468 && !((data)->cfg5 & ADT7468_HFPWM)) /* These are the recognized values for the above regs */ #define LM85_COMPANY_NATIONAL 0x01 @@ -567,8 +570,14 @@ static ssize_t show_pwm_freq(struct device *dev, { int nr = to_sensor_dev_attr(attr)->index; struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf, "%d\n", FREQ_FROM_REG(data->freq_map, - data->pwm_freq[nr])); + int freq; + + if (IS_ADT7468_HFPWM(data)) + freq = 22500; + else + freq = FREQ_FROM_REG(data->freq_map, data->pwm_freq[nr]); + + return sprintf(buf, "%d\n", freq); } static ssize_t set_pwm_freq(struct device *dev, @@ -580,10 +589,22 @@ static ssize_t set_pwm_freq(struct device *dev, long val = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); - data->pwm_freq[nr] = FREQ_TO_REG(data->freq_map, val); - lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), - (data->zone[nr].range << 4) - | data->pwm_freq[nr]); + /* The ADT7468 has a special high-frequency PWM output mode, + * where all PWM outputs are driven by a 22.5 kHz clock. + * This might confuse the user, but there's not much we can do. */ + if (data->type == adt7468 && val >= 11300) { /* High freq. mode */ + data->cfg5 &= ~ADT7468_HFPWM; + lm85_write_value(client, ADT7468_REG_CFG5, data->cfg5); + } else { /* Low freq. mode */ + data->pwm_freq[nr] = FREQ_TO_REG(data->freq_map, val); + lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), + (data->zone[nr].range << 4) + | data->pwm_freq[nr]); + if (data->type == adt7468) { + data->cfg5 |= ADT7468_HFPWM; + lm85_write_value(client, ADT7468_REG_CFG5, data->cfg5); + } + } mutex_unlock(&data->update_lock); return count; } @@ -1259,6 +1280,7 @@ static int lm85_probe(struct i2c_client *client, switch (data->type) { case adm1027: case adt7463: + case adt7468: case emc6d100: case emc6d102: data->freq_map = adm1027_freq_map; diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 760ef72eea5..812781c655a 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -28,9 +28,11 @@ * This driver also supports the MAX6657, MAX6658 and MAX6659 sensor * chips made by Maxim. These chips are similar to the LM86. * Note that there is no easy way to differentiate between the three - * variants. The extra address and features of the MAX6659 are not - * supported by this driver. These chips lack the remote temperature - * offset feature. + * variants. We use the device address to detect MAX6659, which will result + * in a detection as max6657 if it is on address 0x4c. The extra address + * and features of the MAX6659 are only supported if the chip is configured + * explicitly as max6659, or if its address is not 0x4c. + * These chips lack the remote temperature offset feature. * * This driver also supports the MAX6646, MAX6647, MAX6648, MAX6649 and * MAX6692 chips made by Maxim. These are again similar to the LM86, @@ -42,6 +44,11 @@ * chips. The MAX6680 and MAX6681 only differ in the pinout so they can * be treated identically. * + * This driver also supports the MAX6695 and MAX6696, two other sensor + * chips made by Maxim. These are also quite similar to other Maxim + * chips, but support three temperature sensors instead of two. MAX6695 + * and MAX6696 only differ in the pinout so they can be treated identically. + * * This driver also supports the ADT7461 chip from Analog Devices. * It's supported in both compatibility and extended mode. It is mostly * compatible with LM90 except for a data format difference for the @@ -81,11 +88,11 @@ * Addresses to scan * Address is fully defined internally and cannot be changed except for * MAX6659, MAX6680 and MAX6681. - * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6649, MAX6657 - * and MAX6658 have address 0x4c. + * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6649, MAX6657, + * MAX6658 and W83L771 have address 0x4c. * ADM1032-2, ADT7461-2, LM89-1, LM99-1 and MAX6646 have address 0x4d. * MAX6647 has address 0x4e. - * MAX6659 can have address 0x4c, 0x4d or 0x4e (unsupported). + * MAX6659 can have address 0x4c, 0x4d or 0x4e. * MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, * 0x4c, 0x4d or 0x4e. */ @@ -93,8 +100,8 @@ static const unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; -enum chips { lm90, adm1032, lm99, lm86, max6657, adt7461, max6680, max6646, - w83l771 }; +enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, + max6646, w83l771, max6696 }; /* * The LM90 registers @@ -135,26 +142,30 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, adt7461, max6680, max6646, #define LM90_REG_R_TCRIT_HYST 0x21 #define LM90_REG_W_TCRIT_HYST 0x21 -/* MAX6646/6647/6649/6657/6658/6659 registers */ +/* MAX6646/6647/6649/6657/6658/6659/6695/6696 registers */ #define MAX6657_REG_R_LOCAL_TEMPL 0x11 +#define MAX6696_REG_R_STATUS2 0x12 +#define MAX6659_REG_R_REMOTE_EMERG 0x16 +#define MAX6659_REG_W_REMOTE_EMERG 0x16 +#define MAX6659_REG_R_LOCAL_EMERG 0x17 +#define MAX6659_REG_W_LOCAL_EMERG 0x17 -/* - * Device flags - */ -#define LM90_FLAG_ADT7461_EXT 0x01 /* ADT7461 extended mode */ +#define LM90_DEF_CONVRATE_RVAL 6 /* Def conversion rate register value */ +#define LM90_MAX_CONVRATE_MS 16000 /* Maximum conversion rate in ms */ /* - * Functions declaration + * Device flags */ - -static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info); -static int lm90_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static void lm90_init_client(struct i2c_client *client); -static void lm90_alert(struct i2c_client *client, unsigned int flag); -static int lm90_remove(struct i2c_client *client); -static struct lm90_data *lm90_update_device(struct device *dev); +#define LM90_FLAG_ADT7461_EXT (1 << 0) /* ADT7461 extended mode */ +/* Device features */ +#define LM90_HAVE_OFFSET (1 << 1) /* temperature offset register */ +#define LM90_HAVE_LOCAL_EXT (1 << 2) /* extended local temperature */ +#define LM90_HAVE_REM_LIMIT_EXT (1 << 3) /* extended remote limit */ +#define LM90_HAVE_EMERGENCY (1 << 4) /* 3rd upper (emergency) limit */ +#define LM90_HAVE_EMERGENCY_ALARM (1 << 5)/* emergency alarm */ +#define LM90_HAVE_TEMP3 (1 << 6) /* 3rd temperature sensor */ +#define LM90_HAVE_BROKEN_ALERT (1 << 7) /* Broken alert */ /* * Driver data (common to all clients) @@ -172,25 +183,85 @@ static const struct i2c_device_id lm90_id[] = { { "max6649", max6646 }, { "max6657", max6657 }, { "max6658", max6657 }, - { "max6659", max6657 }, + { "max6659", max6659 }, { "max6680", max6680 }, { "max6681", max6680 }, + { "max6695", max6696 }, + { "max6696", max6696 }, { "w83l771", w83l771 }, { } }; MODULE_DEVICE_TABLE(i2c, lm90_id); -static struct i2c_driver lm90_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "lm90", +/* + * chip type specific parameters + */ +struct lm90_params { + u32 flags; /* Capabilities */ + u16 alert_alarms; /* Which alarm bits trigger ALERT# */ + /* Upper 8 bits for max6695/96 */ + u8 max_convrate; /* Maximum conversion rate register value */ +}; + +static const struct lm90_params lm90_params[] = { + [adm1032] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_BROKEN_ALERT, + .alert_alarms = 0x7c, + .max_convrate = 10, + }, + [adt7461] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_BROKEN_ALERT, + .alert_alarms = 0x7c, + .max_convrate = 10, + }, + [lm86] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, + .alert_alarms = 0x7b, + .max_convrate = 9, + }, + [lm90] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, + .alert_alarms = 0x7b, + .max_convrate = 9, + }, + [lm99] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, + .alert_alarms = 0x7b, + .max_convrate = 9, + }, + [max6646] = { + .flags = LM90_HAVE_LOCAL_EXT, + .alert_alarms = 0x7c, + .max_convrate = 6, + }, + [max6657] = { + .flags = LM90_HAVE_LOCAL_EXT, + .alert_alarms = 0x7c, + .max_convrate = 8, + }, + [max6659] = { + .flags = LM90_HAVE_LOCAL_EXT | LM90_HAVE_EMERGENCY, + .alert_alarms = 0x7c, + .max_convrate = 8, + }, + [max6680] = { + .flags = LM90_HAVE_OFFSET, + .alert_alarms = 0x7c, + .max_convrate = 7, + }, + [max6696] = { + .flags = LM90_HAVE_LOCAL_EXT | LM90_HAVE_EMERGENCY + | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3, + .alert_alarms = 0x187c, + .max_convrate = 6, + }, + [w83l771] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, + .alert_alarms = 0x7c, + .max_convrate = 8, }, - .probe = lm90_probe, - .remove = lm90_remove, - .alert = lm90_alert, - .id_table = lm90_id, - .detect = lm90_detect, - .address_list = normal_i2c, }; /* @@ -203,26 +274,268 @@ struct lm90_data { char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ int kind; - int flags; + u32 flags; + + int update_interval; /* in milliseconds */ u8 config_orig; /* Original configuration register value */ - u8 alert_alarms; /* Which alarm bits trigger ALERT# */ + u8 convrate_orig; /* Original conversion rate register value */ + u16 alert_alarms; /* Which alarm bits trigger ALERT# */ + /* Upper 8 bits for max6695/96 */ + u8 max_convrate; /* Maximum conversion rate */ /* registers values */ - s8 temp8[4]; /* 0: local low limit + s8 temp8[8]; /* 0: local low limit 1: local high limit 2: local critical limit - 3: remote critical limit */ - s16 temp11[5]; /* 0: remote input + 3: remote critical limit + 4: local emergency limit (max6659 and max6695/96) + 5: remote emergency limit (max6659 and max6695/96) + 6: remote 2 critical limit (max6695/96 only) + 7: remote 2 emergency limit (max6695/96 only) */ + s16 temp11[8]; /* 0: remote input 1: remote low limit 2: remote high limit - 3: remote offset (except max6646 and max6657) - 4: local input */ + 3: remote offset (except max6646, max6657/58/59, + and max6695/96) + 4: local input + 5: remote 2 input (max6695/96 only) + 6: remote 2 low limit (max6695/96 only) + 7: remote 2 high limit (ma6695/96 only) */ u8 temp_hyst; - u8 alarms; /* bitvector */ + u16 alarms; /* bitvector (upper 8 bits for max6695/96) */ }; /* + * Support functions + */ + +/* + * The ADM1032 supports PEC but not on write byte transactions, so we need + * to explicitly ask for a transaction without PEC. + */ +static inline s32 adm1032_write_byte(struct i2c_client *client, u8 value) +{ + return i2c_smbus_xfer(client->adapter, client->addr, + client->flags & ~I2C_CLIENT_PEC, + I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL); +} + +/* + * It is assumed that client->update_lock is held (unless we are in + * detection or initialization steps). This matters when PEC is enabled, + * because we don't want the address pointer to change between the write + * byte and the read byte transactions. + */ +static int lm90_read_reg(struct i2c_client *client, u8 reg, u8 *value) +{ + int err; + + if (client->flags & I2C_CLIENT_PEC) { + err = adm1032_write_byte(client, reg); + if (err >= 0) + err = i2c_smbus_read_byte(client); + } else + err = i2c_smbus_read_byte_data(client, reg); + + if (err < 0) { + dev_warn(&client->dev, "Register %#02x read failed (%d)\n", + reg, err); + return err; + } + *value = err; + + return 0; +} + +static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value) +{ + int err; + u8 oldh, newh, l; + + /* + * There is a trick here. We have to read two registers to have the + * sensor temperature, but we have to beware a conversion could occur + * inbetween the readings. The datasheet says we should either use + * the one-shot conversion register, which we don't want to do + * (disables hardware monitoring) or monitor the busy bit, which is + * impossible (we can't read the values and monitor that bit at the + * exact same time). So the solution used here is to read the high + * byte once, then the low byte, then the high byte again. If the new + * high byte matches the old one, then we have a valid reading. Else + * we have to read the low byte again, and now we believe we have a + * correct reading. + */ + if ((err = lm90_read_reg(client, regh, &oldh)) + || (err = lm90_read_reg(client, regl, &l)) + || (err = lm90_read_reg(client, regh, &newh))) + return err; + if (oldh != newh) { + err = lm90_read_reg(client, regl, &l); + if (err) + return err; + } + *value = (newh << 8) | l; + + return 0; +} + +/* + * client->update_lock must be held when calling this function (unless we are + * in detection or initialization steps), and while a remote channel other + * than channel 0 is selected. Also, calling code must make sure to re-select + * external channel 0 before releasing the lock. This is necessary because + * various registers have different meanings as a result of selecting a + * non-default remote channel. + */ +static inline void lm90_select_remote_channel(struct i2c_client *client, + struct lm90_data *data, + int channel) +{ + u8 config; + + if (data->kind == max6696) { + lm90_read_reg(client, LM90_REG_R_CONFIG1, &config); + config &= ~0x08; + if (channel) + config |= 0x08; + i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, + config); + } +} + +/* + * Set conversion rate. + * client->update_lock must be held when calling this function (unless we are + * in detection or initialization steps). + */ +static void lm90_set_convrate(struct i2c_client *client, struct lm90_data *data, + unsigned int interval) +{ + int i; + unsigned int update_interval; + + /* Shift calculations to avoid rounding errors */ + interval <<= 6; + + /* find the nearest update rate */ + for (i = 0, update_interval = LM90_MAX_CONVRATE_MS << 6; + i < data->max_convrate; i++, update_interval >>= 1) + if (interval >= update_interval * 3 / 4) + break; + + i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, i); + data->update_interval = DIV_ROUND_CLOSEST(update_interval, 64); +} + +static struct lm90_data *lm90_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm90_data *data = i2c_get_clientdata(client); + unsigned long next_update; + + mutex_lock(&data->update_lock); + + next_update = data->last_updated + + msecs_to_jiffies(data->update_interval) + 1; + if (time_after(jiffies, next_update) || !data->valid) { + u8 h, l; + u8 alarms; + + dev_dbg(&client->dev, "Updating lm90 data.\n"); + lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[0]); + lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[1]); + lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[2]); + lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]); + lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst); + + if (data->flags & LM90_HAVE_LOCAL_EXT) { + lm90_read16(client, LM90_REG_R_LOCAL_TEMP, + MAX6657_REG_R_LOCAL_TEMPL, + &data->temp11[4]); + } else { + if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, + &h) == 0) + data->temp11[4] = h << 8; + } + lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, + LM90_REG_R_REMOTE_TEMPL, &data->temp11[0]); + + if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0) { + data->temp11[1] = h << 8; + if ((data->flags & LM90_HAVE_REM_LIMIT_EXT) + && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, + &l) == 0) + data->temp11[1] |= l; + } + if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0) { + data->temp11[2] = h << 8; + if ((data->flags & LM90_HAVE_REM_LIMIT_EXT) + && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, + &l) == 0) + data->temp11[2] |= l; + } + + if (data->flags & LM90_HAVE_OFFSET) { + if (lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSH, + &h) == 0 + && lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSL, + &l) == 0) + data->temp11[3] = (h << 8) | l; + } + if (data->flags & LM90_HAVE_EMERGENCY) { + lm90_read_reg(client, MAX6659_REG_R_LOCAL_EMERG, + &data->temp8[4]); + lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG, + &data->temp8[5]); + } + lm90_read_reg(client, LM90_REG_R_STATUS, &alarms); + data->alarms = alarms; /* save as 16 bit value */ + + if (data->kind == max6696) { + lm90_select_remote_channel(client, data, 1); + lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, + &data->temp8[6]); + lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG, + &data->temp8[7]); + lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, + LM90_REG_R_REMOTE_TEMPL, &data->temp11[5]); + if (!lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h)) + data->temp11[6] = h << 8; + if (!lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h)) + data->temp11[7] = h << 8; + lm90_select_remote_channel(client, data, 0); + + if (!lm90_read_reg(client, MAX6696_REG_R_STATUS2, + &alarms)) + data->alarms |= alarms << 8; + } + + /* Re-enable ALERT# output if it was originally enabled and + * relevant alarms are all clear */ + if ((data->config_orig & 0x80) == 0 + && (data->alarms & data->alert_alarms) == 0) { + u8 config; + + lm90_read_reg(client, LM90_REG_R_CONFIG1, &config); + if (config & 0x80) { + dev_dbg(&client->dev, "Re-enabling ALERT#\n"); + i2c_smbus_write_byte_data(client, + LM90_REG_W_CONFIG1, + config & ~0x80); + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +/* * Conversions * For local temperatures and limits, critical limits and the hysteresis * value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius. @@ -377,18 +690,27 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - static const u8 reg[4] = { + static const u8 reg[8] = { LM90_REG_W_LOCAL_LOW, LM90_REG_W_LOCAL_HIGH, LM90_REG_W_LOCAL_CRIT, LM90_REG_W_REMOTE_CRIT, + MAX6659_REG_W_LOCAL_EMERG, + MAX6659_REG_W_REMOTE_EMERG, + LM90_REG_W_REMOTE_CRIT, + MAX6659_REG_W_REMOTE_EMERG, }; struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct lm90_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); int nr = attr->index; + long val; + int err; + + err = strict_strtol(buf, 10, &val); + if (err < 0) + return err; /* +16 degrees offset for temp2 for the LM99 */ if (data->kind == lm99 && attr->index == 3) @@ -401,7 +723,11 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, data->temp8[nr] = temp_to_u8(val); else data->temp8[nr] = temp_to_s8(val); + + lm90_select_remote_channel(client, data, nr >= 6); i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); + lm90_select_remote_channel(client, data, 0); + mutex_unlock(&data->update_lock); return count; } @@ -409,7 +735,7 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, char *buf) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); struct lm90_data *data = lm90_update_device(dev); int temp; @@ -430,46 +756,58 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - static const u8 reg[6] = { - LM90_REG_W_REMOTE_LOWH, - LM90_REG_W_REMOTE_LOWL, - LM90_REG_W_REMOTE_HIGHH, - LM90_REG_W_REMOTE_HIGHL, - LM90_REG_W_REMOTE_OFFSH, - LM90_REG_W_REMOTE_OFFSL, + struct { + u8 high; + u8 low; + int channel; + } reg[5] = { + { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL, 0 }, + { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL, 0 }, + { LM90_REG_W_REMOTE_OFFSH, LM90_REG_W_REMOTE_OFFSL, 0 }, + { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL, 1 }, + { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL, 1 } }; - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); struct i2c_client *client = to_i2c_client(dev); struct lm90_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - int nr = attr->index; + int nr = attr->nr; + int index = attr->index; + long val; + int err; + + err = strict_strtol(buf, 10, &val); + if (err < 0) + return err; /* +16 degrees offset for temp2 for the LM99 */ - if (data->kind == lm99 && attr->index <= 2) + if (data->kind == lm99 && index <= 2) val -= 16000; mutex_lock(&data->update_lock); if (data->kind == adt7461) - data->temp11[nr] = temp_to_u16_adt7461(data, val); - else if (data->kind == max6657 || data->kind == max6680) - data->temp11[nr] = temp_to_s8(val) << 8; + data->temp11[index] = temp_to_u16_adt7461(data, val); else if (data->kind == max6646) - data->temp11[nr] = temp_to_u8(val) << 8; + data->temp11[index] = temp_to_u8(val) << 8; + else if (data->flags & LM90_HAVE_REM_LIMIT_EXT) + data->temp11[index] = temp_to_s16(val); else - data->temp11[nr] = temp_to_s16(val); - - i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], - data->temp11[nr] >> 8); - if (data->kind != max6657 && data->kind != max6680 - && data->kind != max6646) - i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1], - data->temp11[nr] & 0xff); + data->temp11[index] = temp_to_s8(val) << 8; + + lm90_select_remote_channel(client, data, reg[nr].channel); + i2c_smbus_write_byte_data(client, reg[nr].high, + data->temp11[index] >> 8); + if (data->flags & LM90_HAVE_REM_LIMIT_EXT) + i2c_smbus_write_byte_data(client, reg[nr].low, + data->temp11[index] & 0xff); + lm90_select_remote_channel(client, data, 0); + mutex_unlock(&data->update_lock); return count; } -static ssize_t show_temphyst(struct device *dev, struct device_attribute *devattr, +static ssize_t show_temphyst(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); @@ -495,9 +833,14 @@ static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, { struct i2c_client *client = to_i2c_client(dev); struct lm90_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; int temp; + err = strict_strtol(buf, 10, &val); + if (err < 0) + return err; + mutex_lock(&data->update_lock); if (data->kind == adt7461) temp = temp_from_u8_adt7461(data, data->temp8[2]); @@ -530,16 +873,44 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); } -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp11, NULL, 4); -static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0); +static ssize_t show_update_interval(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lm90_data *data = dev_get_drvdata(dev); + + 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 lm90_data *data = i2c_get_clientdata(client); + unsigned long val; + int err; + + err = strict_strtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + lm90_set_convrate(client, data, val); + mutex_unlock(&data->update_lock); + + return count; +} + +static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp11, NULL, 0, 4); +static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp11, NULL, 0, 0); static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8, set_temp8, 0); -static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 1); +static SENSOR_DEVICE_ATTR_2(temp2_min, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 0, 1); static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8, set_temp8, 1); -static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 2); +static SENSOR_DEVICE_ATTR_2(temp2_max, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 1, 2); static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8, set_temp8, 2); static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8, @@ -547,8 +918,8 @@ static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8, static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst, set_temphyst, 2); static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 3); -static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 3); +static SENSOR_DEVICE_ATTR_2(temp2_offset, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 2, 3); /* Individual alarm files */ static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0); @@ -561,6 +932,9 @@ static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6); /* Raw alarm file for compatibility */ static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); +static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval, + set_update_interval); + static struct attribute *lm90_attributes[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, @@ -581,6 +955,7 @@ static struct attribute *lm90_attributes[] = { &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, &dev_attr_alarms.attr, + &dev_attr_update_interval.attr, NULL }; @@ -588,6 +963,86 @@ static const struct attribute_group lm90_group = { .attrs = lm90_attributes, }; +/* + * Additional attributes for devices with emergency sensors + */ +static SENSOR_DEVICE_ATTR(temp1_emergency, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 4); +static SENSOR_DEVICE_ATTR(temp2_emergency, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 5); +static SENSOR_DEVICE_ATTR(temp1_emergency_hyst, S_IRUGO, show_temphyst, + NULL, 4); +static SENSOR_DEVICE_ATTR(temp2_emergency_hyst, S_IRUGO, show_temphyst, + NULL, 5); + +static struct attribute *lm90_emergency_attributes[] = { + &sensor_dev_attr_temp1_emergency.dev_attr.attr, + &sensor_dev_attr_temp2_emergency.dev_attr.attr, + &sensor_dev_attr_temp1_emergency_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_emergency_hyst.dev_attr.attr, + NULL +}; + +static const struct attribute_group lm90_emergency_group = { + .attrs = lm90_emergency_attributes, +}; + +static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO, show_alarm, NULL, 15); +static SENSOR_DEVICE_ATTR(temp2_emergency_alarm, S_IRUGO, show_alarm, NULL, 13); + +static struct attribute *lm90_emergency_alarm_attributes[] = { + &sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_emergency_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group lm90_emergency_alarm_group = { + .attrs = lm90_emergency_alarm_attributes, +}; + +/* + * Additional attributes for devices with 3 temperature sensors + */ +static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp11, NULL, 0, 5); +static SENSOR_DEVICE_ATTR_2(temp3_min, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 3, 6); +static SENSOR_DEVICE_ATTR_2(temp3_max, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 4, 7); +static SENSOR_DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 6); +static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_temphyst, NULL, 6); +static SENSOR_DEVICE_ATTR(temp3_emergency, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 7); +static SENSOR_DEVICE_ATTR(temp3_emergency_hyst, S_IRUGO, show_temphyst, + NULL, 7); + +static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, 9); +static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 10); +static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_alarm, NULL, 11); +static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 12); +static SENSOR_DEVICE_ATTR(temp3_emergency_alarm, S_IRUGO, show_alarm, NULL, 14); + +static struct attribute *lm90_temp3_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_emergency.dev_attr.attr, + &sensor_dev_attr_temp3_emergency_hyst.dev_attr.attr, + + &sensor_dev_attr_temp3_fault.dev_attr.attr, + &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_emergency_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group lm90_temp3_group = { + .attrs = lm90_temp3_attributes, +}; + /* pec used for ADM1032 only */ static ssize_t show_pec(struct device *dev, struct device_attribute *dummy, char *buf) @@ -600,7 +1055,12 @@ static ssize_t set_pec(struct device *dev, struct device_attribute *dummy, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = strict_strtol(buf, 10, &val); + if (err < 0) + return err; switch (val) { case 0: @@ -622,40 +1082,6 @@ static DEVICE_ATTR(pec, S_IWUSR | S_IRUGO, show_pec, set_pec); * Real code */ -/* The ADM1032 supports PEC but not on write byte transactions, so we need - to explicitly ask for a transaction without PEC. */ -static inline s32 adm1032_write_byte(struct i2c_client *client, u8 value) -{ - return i2c_smbus_xfer(client->adapter, client->addr, - client->flags & ~I2C_CLIENT_PEC, - I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL); -} - -/* It is assumed that client->update_lock is held (unless we are in - detection or initialization steps). This matters when PEC is enabled, - because we don't want the address pointer to change between the write - byte and the read byte transactions. */ -static int lm90_read_reg(struct i2c_client* client, u8 reg, u8 *value) -{ - int err; - - if (client->flags & I2C_CLIENT_PEC) { - err = adm1032_write_byte(client, reg); - if (err >= 0) - err = i2c_smbus_read_byte(client); - } else - err = i2c_smbus_read_byte_data(client, reg); - - if (err < 0) { - dev_warn(&client->dev, "Register %#02x read failed (%d)\n", - reg, err); - return err; - } - *value = err; - - return 0; -} - /* Return 0 if detection is successful, -ENODEV otherwise */ static int lm90_detect(struct i2c_client *new_client, struct i2c_board_info *info) @@ -730,6 +1156,23 @@ static int lm90_detect(struct i2c_client *new_client, } } else if (man_id == 0x4D) { /* Maxim */ + int reg_emerg, reg_emerg2, reg_status2; + + /* + * We read MAX6659_REG_R_REMOTE_EMERG twice, and re-read + * LM90_REG_R_MAN_ID in between. If MAX6659_REG_R_REMOTE_EMERG + * exists, both readings will reflect the same value. Otherwise, + * the readings will be different. + */ + if ((reg_emerg = i2c_smbus_read_byte_data(new_client, + MAX6659_REG_R_REMOTE_EMERG)) < 0 + || i2c_smbus_read_byte_data(new_client, LM90_REG_R_MAN_ID) < 0 + || (reg_emerg2 = i2c_smbus_read_byte_data(new_client, + MAX6659_REG_R_REMOTE_EMERG)) < 0 + || (reg_status2 = i2c_smbus_read_byte_data(new_client, + MAX6696_REG_R_STATUS2)) < 0) + return -ENODEV; + /* * The MAX6657, MAX6658 and MAX6659 do NOT have a chip_id * register. Reading from that address will return the last @@ -737,12 +1180,38 @@ static int lm90_detect(struct i2c_client *new_client, * register. Likewise, the config1 register seems to lack a * low nibble, so the value will be those of the previous * read, so in our case those of the man_id register. + * MAX6659 has a third set of upper temperature limit registers. + * Those registers also return values on MAX6657 and MAX6658, + * thus the only way to detect MAX6659 is by its address. + * For this reason it will be mis-detected as MAX6657 if its + * address is 0x4C. */ if (chip_id == man_id - && (address == 0x4C || address == 0x4D) + && (address == 0x4C || address == 0x4D || address == 0x4E) && (reg_config1 & 0x1F) == (man_id & 0x0F) && reg_convrate <= 0x09) { - name = "max6657"; + if (address == 0x4C) + name = "max6657"; + else + name = "max6659"; + } else + /* + * Even though MAX6695 and MAX6696 do not have a chip ID + * register, reading it returns 0x01. Bit 4 of the config1 + * register is unused and should return zero when read. Bit 0 of + * the status2 register is unused and should return zero when + * read. + * + * MAX6695 and MAX6696 have an additional set of temperature + * limit registers. We can detect those chips by checking if + * one of those registers exists. + */ + if (chip_id == 0x01 + && (reg_config1 & 0x10) == 0x00 + && (reg_status2 & 0x01) == 0x00 + && reg_emerg == reg_emerg2 + && reg_convrate <= 0x07) { + name = "max6696"; } else /* * The chip_id register of the MAX6680 and MAX6681 holds the @@ -768,10 +1237,23 @@ static int lm90_detect(struct i2c_client *new_client, } else if (address == 0x4C && man_id == 0x5C) { /* Winbond/Nuvoton */ - if ((chip_id & 0xFE) == 0x10 /* W83L771AWG/ASG */ - && (reg_config1 & 0x2A) == 0x00 - && reg_convrate <= 0x08) { - name = "w83l771"; + int reg_config2; + + reg_config2 = i2c_smbus_read_byte_data(new_client, + LM90_REG_R_CONFIG2); + if (reg_config2 < 0) + return -ENODEV; + + if ((reg_config1 & 0x2A) == 0x00 + && (reg_config2 & 0xF8) == 0x00) { + if (chip_id == 0x01 /* W83L771W/G */ + && reg_convrate <= 0x09) { + name = "w83l771"; + } else + if ((chip_id & 0xFE) == 0x10 /* W83L771AWG/ASG */ + && reg_convrate <= 0x08) { + name = "w83l771"; + } } } @@ -787,6 +1269,69 @@ static int lm90_detect(struct i2c_client *new_client, return 0; } +static void lm90_remove_files(struct i2c_client *client, struct lm90_data *data) +{ + if (data->flags & LM90_HAVE_TEMP3) + sysfs_remove_group(&client->dev.kobj, &lm90_temp3_group); + if (data->flags & LM90_HAVE_EMERGENCY_ALARM) + sysfs_remove_group(&client->dev.kobj, + &lm90_emergency_alarm_group); + if (data->flags & LM90_HAVE_EMERGENCY) + sysfs_remove_group(&client->dev.kobj, + &lm90_emergency_group); + if (data->flags & LM90_HAVE_OFFSET) + device_remove_file(&client->dev, + &sensor_dev_attr_temp2_offset.dev_attr); + device_remove_file(&client->dev, &dev_attr_pec); + sysfs_remove_group(&client->dev.kobj, &lm90_group); +} + +static void lm90_init_client(struct i2c_client *client) +{ + u8 config, convrate; + struct lm90_data *data = i2c_get_clientdata(client); + + if (lm90_read_reg(client, LM90_REG_R_CONVRATE, &convrate) < 0) { + dev_warn(&client->dev, "Failed to read convrate register!\n"); + convrate = LM90_DEF_CONVRATE_RVAL; + } + data->convrate_orig = convrate; + + /* + * Start the conversions. + */ + lm90_set_convrate(client, data, 500); /* 500ms; 2Hz conversion rate */ + if (lm90_read_reg(client, LM90_REG_R_CONFIG1, &config) < 0) { + dev_warn(&client->dev, "Initialization failed!\n"); + return; + } + data->config_orig = config; + + /* Check Temperature Range Select */ + if (data->kind == adt7461) { + if (config & 0x04) + data->flags |= LM90_FLAG_ADT7461_EXT; + } + + /* + * Put MAX6680/MAX8881 into extended resolution (bit 0x10, + * 0.125 degree resolution) and range (0x08, extend range + * to -64 degree) mode for the remote temperature sensor. + */ + if (data->kind == max6680) + config |= 0x18; + + /* + * Select external channel 0 for max6695/96 + */ + if (data->kind == max6696) + config &= ~0x08; + + config &= 0xBF; /* run */ + if (config != data->config_orig) /* Only write if changed */ + i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config); +} + static int lm90_probe(struct i2c_client *new_client, const struct i2c_device_id *id) { @@ -811,31 +1356,48 @@ static int lm90_probe(struct i2c_client *new_client, /* Different devices have different alarm bits triggering the * ALERT# output */ - switch (data->kind) { - case lm90: - case lm99: - case lm86: - data->alert_alarms = 0x7b; - break; - default: - data->alert_alarms = 0x7c; - break; - } + data->alert_alarms = lm90_params[data->kind].alert_alarms; + + /* Set chip capabilities */ + data->flags = lm90_params[data->kind].flags; + + /* Set maximum conversion rate */ + data->max_convrate = lm90_params[data->kind].max_convrate; /* Initialize the LM90 chip */ lm90_init_client(new_client); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&new_client->dev.kobj, &lm90_group))) + err = sysfs_create_group(&new_client->dev.kobj, &lm90_group); + if (err) goto exit_free; if (new_client->flags & I2C_CLIENT_PEC) { - if ((err = device_create_file(&new_client->dev, - &dev_attr_pec))) + err = device_create_file(&new_client->dev, &dev_attr_pec); + if (err) + goto exit_remove_files; + } + if (data->flags & LM90_HAVE_OFFSET) { + err = device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_offset.dev_attr); + if (err) goto exit_remove_files; } - if (data->kind != max6657 && data->kind != max6646) { - if ((err = device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_offset.dev_attr))) + if (data->flags & LM90_HAVE_EMERGENCY) { + err = sysfs_create_group(&new_client->dev.kobj, + &lm90_emergency_group); + if (err) + goto exit_remove_files; + } + if (data->flags & LM90_HAVE_EMERGENCY_ALARM) { + err = sysfs_create_group(&new_client->dev.kobj, + &lm90_emergency_alarm_group); + if (err) + goto exit_remove_files; + } + if (data->flags & LM90_HAVE_TEMP3) { + err = sysfs_create_group(&new_client->dev.kobj, + &lm90_temp3_group); + if (err) goto exit_remove_files; } @@ -848,62 +1410,23 @@ static int lm90_probe(struct i2c_client *new_client, return 0; exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &lm90_group); - device_remove_file(&new_client->dev, &dev_attr_pec); + lm90_remove_files(new_client, data); exit_free: kfree(data); exit: return err; } -static void lm90_init_client(struct i2c_client *client) -{ - u8 config; - struct lm90_data *data = i2c_get_clientdata(client); - - /* - * Start the conversions. - */ - i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, - 5); /* 2 Hz */ - if (lm90_read_reg(client, LM90_REG_R_CONFIG1, &config) < 0) { - dev_warn(&client->dev, "Initialization failed!\n"); - return; - } - data->config_orig = config; - - /* Check Temperature Range Select */ - if (data->kind == adt7461) { - if (config & 0x04) - data->flags |= LM90_FLAG_ADT7461_EXT; - } - - /* - * Put MAX6680/MAX8881 into extended resolution (bit 0x10, - * 0.125 degree resolution) and range (0x08, extend range - * to -64 degree) mode for the remote temperature sensor. - */ - if (data->kind == max6680) { - config |= 0x18; - } - - config &= 0xBF; /* run */ - if (config != data->config_orig) /* Only write if changed */ - i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config); -} - static int lm90_remove(struct i2c_client *client) { struct lm90_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &lm90_group); - device_remove_file(&client->dev, &dev_attr_pec); - if (data->kind != max6657 && data->kind != max6646) - device_remove_file(&client->dev, - &sensor_dev_attr_temp2_offset.dev_attr); + lm90_remove_files(client, data); /* Restore initial configuration */ + i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, + data->convrate_orig); i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, data->config_orig); @@ -914,10 +1437,14 @@ static int lm90_remove(struct i2c_client *client) static void lm90_alert(struct i2c_client *client, unsigned int flag) { struct lm90_data *data = i2c_get_clientdata(client); - u8 config, alarms; + u8 config, alarms, alarms2 = 0; lm90_read_reg(client, LM90_REG_R_STATUS, &alarms); - if ((alarms & 0x7f) == 0) { + + if (data->kind == max6696) + lm90_read_reg(client, MAX6696_REG_R_STATUS2, &alarms2); + + if ((alarms & 0x7f) == 0 && (alarms2 & 0xfe) == 0) { dev_info(&client->dev, "Everything OK\n"); } else { if (alarms & 0x61) @@ -930,10 +1457,14 @@ static void lm90_alert(struct i2c_client *client, unsigned int flag) dev_warn(&client->dev, "temp%d diode open, please check!\n", 2); + if (alarms2 & 0x18) + dev_warn(&client->dev, + "temp%d out of range, please check!\n", 3); + /* Disable ALERT# output, because these chips don't implement SMBus alert correctly; they should only hold the alert line low briefly. */ - if ((data->kind == adm1032 || data->kind == adt7461) + if ((data->flags & LM90_HAVE_BROKEN_ALERT) && (alarms & data->alert_alarms)) { dev_dbg(&client->dev, "Disabling ALERT#\n"); lm90_read_reg(client, LM90_REG_R_CONFIG1, &config); @@ -943,117 +1474,18 @@ static void lm90_alert(struct i2c_client *client, unsigned int flag) } } -static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value) -{ - int err; - u8 oldh, newh, l; - - /* - * There is a trick here. We have to read two registers to have the - * sensor temperature, but we have to beware a conversion could occur - * inbetween the readings. The datasheet says we should either use - * the one-shot conversion register, which we don't want to do - * (disables hardware monitoring) or monitor the busy bit, which is - * impossible (we can't read the values and monitor that bit at the - * exact same time). So the solution used here is to read the high - * byte once, then the low byte, then the high byte again. If the new - * high byte matches the old one, then we have a valid reading. Else - * we have to read the low byte again, and now we believe we have a - * correct reading. - */ - if ((err = lm90_read_reg(client, regh, &oldh)) - || (err = lm90_read_reg(client, regl, &l)) - || (err = lm90_read_reg(client, regh, &newh))) - return err; - if (oldh != newh) { - err = lm90_read_reg(client, regl, &l); - if (err) - return err; - } - *value = (newh << 8) | l; - - return 0; -} - -static struct lm90_data *lm90_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm90_data *data = i2c_get_clientdata(client); - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ / 2 + HZ / 10) - || !data->valid) { - u8 h, l; - - dev_dbg(&client->dev, "Updating lm90 data.\n"); - lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[0]); - lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[1]); - lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[2]); - lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]); - lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst); - - if (data->kind == max6657 || data->kind == max6646) { - lm90_read16(client, LM90_REG_R_LOCAL_TEMP, - MAX6657_REG_R_LOCAL_TEMPL, - &data->temp11[4]); - } else { - if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, - &h) == 0) - data->temp11[4] = h << 8; - } - lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, - LM90_REG_R_REMOTE_TEMPL, &data->temp11[0]); - - if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0) { - data->temp11[1] = h << 8; - if (data->kind != max6657 && data->kind != max6680 - && data->kind != max6646 - && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, - &l) == 0) - data->temp11[1] |= l; - } - if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0) { - data->temp11[2] = h << 8; - if (data->kind != max6657 && data->kind != max6680 - && data->kind != max6646 - && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, - &l) == 0) - data->temp11[2] |= l; - } - - if (data->kind != max6657 && data->kind != max6646) { - if (lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSH, - &h) == 0 - && lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSL, - &l) == 0) - data->temp11[3] = (h << 8) | l; - } - lm90_read_reg(client, LM90_REG_R_STATUS, &data->alarms); - - /* Re-enable ALERT# output if it was originally enabled and - * relevant alarms are all clear */ - if ((data->config_orig & 0x80) == 0 - && (data->alarms & data->alert_alarms) == 0) { - u8 config; - - lm90_read_reg(client, LM90_REG_R_CONFIG1, &config); - if (config & 0x80) { - dev_dbg(&client->dev, "Re-enabling ALERT#\n"); - i2c_smbus_write_byte_data(client, - LM90_REG_W_CONFIG1, - config & ~0x80); - } - } - - data->last_updated = jiffies; - data->valid = 1; - } - - mutex_unlock(&data->update_lock); - - return data; -} +static struct i2c_driver lm90_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "lm90", + }, + .probe = lm90_probe, + .remove = lm90_remove, + .alert = lm90_alert, + .id_table = lm90_id, + .detect = lm90_detect, + .address_list = normal_i2c, +}; static int __init sensors_lm90_init(void) { diff --git a/drivers/hwmon/pcf8591.c b/drivers/hwmon/pcf8591.c index d4478794985..dc7259d6981 100644 --- a/drivers/hwmon/pcf8591.c +++ b/drivers/hwmon/pcf8591.c @@ -23,10 +23,8 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/mutex.h> - -/* Addresses to scan */ -static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, - 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; +#include <linux/err.h> +#include <linux/hwmon.h> /* Insmod parameters */ @@ -71,6 +69,7 @@ MODULE_PARM_DESC(input_mode, #define REG_TO_SIGNED(reg) (((reg) & 0x80)?((reg) - 256):(reg)) struct pcf8591_data { + struct device *hwmon_dev; struct mutex update_lock; u8 control; @@ -167,24 +166,6 @@ static const struct attribute_group pcf8591_attr_group_opt = { * Real code */ -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int pcf8591_detect(struct i2c_client *client, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = client->adapter; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE - | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return -ENODEV; - - /* Now, we would do the remaining detection. But the PCF8591 is plainly - impossible to detect! Stupid chip. */ - - strlcpy(info->type, "pcf8591", I2C_NAME_SIZE); - - return 0; -} - static int pcf8591_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -221,6 +202,12 @@ static int pcf8591_probe(struct i2c_client *client, goto exit_sysfs_remove; } + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_sysfs_remove; + } + return 0; exit_sysfs_remove: @@ -234,6 +221,9 @@ exit: static int pcf8591_remove(struct i2c_client *client) { + struct pcf8591_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt); sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group); kfree(i2c_get_clientdata(client)); @@ -295,10 +285,6 @@ static struct i2c_driver pcf8591_driver = { .probe = pcf8591_probe, .remove = pcf8591_remove, .id_table = pcf8591_id, - - .class = I2C_CLASS_HWMON, /* Nearest choice */ - .detect = pcf8591_detect, - .address_list = normal_i2c, }; static int __init pcf8591_init(void) diff --git a/drivers/hwmon/s3c-hwmon.c b/drivers/hwmon/s3c-hwmon.c index 3f3f9a47acf..05248f2d758 100644 --- a/drivers/hwmon/s3c-hwmon.c +++ b/drivers/hwmon/s3c-hwmon.c @@ -51,7 +51,7 @@ struct s3c_hwmon_attr { * @attr: The holders for the channel attributes. */ struct s3c_hwmon { - struct semaphore lock; + struct mutex lock; struct s3c_adc_client *client; struct device *hwmon_dev; @@ -73,14 +73,14 @@ static int s3c_hwmon_read_ch(struct device *dev, { int ret; - ret = down_interruptible(&hwmon->lock); + ret = mutex_lock_interruptible(&hwmon->lock); if (ret < 0) return ret; dev_dbg(dev, "reading channel %d\n", channel); ret = s3c_adc_read(hwmon->client, channel); - up(&hwmon->lock); + mutex_unlock(&hwmon->lock); return ret; } @@ -296,7 +296,7 @@ static int __devinit s3c_hwmon_probe(struct platform_device *dev) platform_set_drvdata(dev, hwmon); - init_MUTEX(&hwmon->lock); + mutex_init(&hwmon->lock); /* Register with the core ADC driver. */ diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c index 6b4165c1209..0517a8f09d3 100644 --- a/drivers/hwmon/tmp421.c +++ b/drivers/hwmon/tmp421.c @@ -36,8 +36,8 @@ #include <linux/sysfs.h> /* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x2a, 0x4c, 0x4d, 0x4e, 0x4f, - I2C_CLIENT_END }; +static const unsigned short normal_i2c[] = { 0x2a, 0x4c, 0x4d, 0x4e, 0x4f, + I2C_CLIENT_END }; enum chips { tmp421, tmp422, tmp423 }; diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c new file mode 100644 index 00000000000..1d840aa8378 --- /dev/null +++ b/drivers/hwmon/w83795.c @@ -0,0 +1,2121 @@ +/* + * w83795.c - Linux kernel driver for hardware monitoring + * Copyright (C) 2008 Nuvoton Technology Corp. + * Wei Song + * Copyright (C) 2010 Jean Delvare <khali@linux-fr.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation - version 2. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + * + * Supports following chips: + * + * Chip #vin #fanin #pwm #temp #dts wchipid vendid i2c ISA + * w83795g 21 14 8 6 8 0x79 0x5ca3 yes no + * w83795adg 18 14 2 6 8 0x79 0x5ca3 yes no + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.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> + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { + 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END +}; + + +static int reset; +module_param(reset, bool, 0); +MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended"); + + +#define W83795_REG_BANKSEL 0x00 +#define W83795_REG_VENDORID 0xfd +#define W83795_REG_CHIPID 0xfe +#define W83795_REG_DEVICEID 0xfb +#define W83795_REG_DEVICEID_A 0xff + +#define W83795_REG_I2C_ADDR 0xfc +#define W83795_REG_CONFIG 0x01 +#define W83795_REG_CONFIG_CONFIG48 0x04 +#define W83795_REG_CONFIG_START 0x01 + +/* Multi-Function Pin Ctrl Registers */ +#define W83795_REG_VOLT_CTRL1 0x02 +#define W83795_REG_VOLT_CTRL2 0x03 +#define W83795_REG_TEMP_CTRL1 0x04 +#define W83795_REG_TEMP_CTRL2 0x05 +#define W83795_REG_FANIN_CTRL1 0x06 +#define W83795_REG_FANIN_CTRL2 0x07 +#define W83795_REG_VMIGB_CTRL 0x08 + +#define TEMP_READ 0 +#define TEMP_CRIT 1 +#define TEMP_CRIT_HYST 2 +#define TEMP_WARN 3 +#define TEMP_WARN_HYST 4 +/* only crit and crit_hyst affect real-time alarm status + * current crit crit_hyst warn warn_hyst */ +static const u16 W83795_REG_TEMP[][5] = { + {0x21, 0x96, 0x97, 0x98, 0x99}, /* TD1/TR1 */ + {0x22, 0x9a, 0x9b, 0x9c, 0x9d}, /* TD2/TR2 */ + {0x23, 0x9e, 0x9f, 0xa0, 0xa1}, /* TD3/TR3 */ + {0x24, 0xa2, 0xa3, 0xa4, 0xa5}, /* TD4/TR4 */ + {0x1f, 0xa6, 0xa7, 0xa8, 0xa9}, /* TR5 */ + {0x20, 0xaa, 0xab, 0xac, 0xad}, /* TR6 */ +}; + +#define IN_READ 0 +#define IN_MAX 1 +#define IN_LOW 2 +static const u16 W83795_REG_IN[][3] = { + /* Current, HL, LL */ + {0x10, 0x70, 0x71}, /* VSEN1 */ + {0x11, 0x72, 0x73}, /* VSEN2 */ + {0x12, 0x74, 0x75}, /* VSEN3 */ + {0x13, 0x76, 0x77}, /* VSEN4 */ + {0x14, 0x78, 0x79}, /* VSEN5 */ + {0x15, 0x7a, 0x7b}, /* VSEN6 */ + {0x16, 0x7c, 0x7d}, /* VSEN7 */ + {0x17, 0x7e, 0x7f}, /* VSEN8 */ + {0x18, 0x80, 0x81}, /* VSEN9 */ + {0x19, 0x82, 0x83}, /* VSEN10 */ + {0x1A, 0x84, 0x85}, /* VSEN11 */ + {0x1B, 0x86, 0x87}, /* VTT */ + {0x1C, 0x88, 0x89}, /* 3VDD */ + {0x1D, 0x8a, 0x8b}, /* 3VSB */ + {0x1E, 0x8c, 0x8d}, /* VBAT */ + {0x1F, 0xa6, 0xa7}, /* VSEN12 */ + {0x20, 0xaa, 0xab}, /* VSEN13 */ + {0x21, 0x96, 0x97}, /* VSEN14 */ + {0x22, 0x9a, 0x9b}, /* VSEN15 */ + {0x23, 0x9e, 0x9f}, /* VSEN16 */ + {0x24, 0xa2, 0xa3}, /* VSEN17 */ +}; +#define W83795_REG_VRLSB 0x3C + +static const u8 W83795_REG_IN_HL_LSB[] = { + 0x8e, /* VSEN1-4 */ + 0x90, /* VSEN5-8 */ + 0x92, /* VSEN9-11 */ + 0x94, /* VTT, 3VDD, 3VSB, 3VBAT */ + 0xa8, /* VSEN12 */ + 0xac, /* VSEN13 */ + 0x98, /* VSEN14 */ + 0x9c, /* VSEN15 */ + 0xa0, /* VSEN16 */ + 0xa4, /* VSEN17 */ +}; + +#define IN_LSB_REG(index, type) \ + (((type) == 1) ? W83795_REG_IN_HL_LSB[(index)] \ + : (W83795_REG_IN_HL_LSB[(index)] + 1)) + +#define IN_LSB_SHIFT 0 +#define IN_LSB_IDX 1 +static const u8 IN_LSB_SHIFT_IDX[][2] = { + /* High/Low LSB shift, LSB No. */ + {0x00, 0x00}, /* VSEN1 */ + {0x02, 0x00}, /* VSEN2 */ + {0x04, 0x00}, /* VSEN3 */ + {0x06, 0x00}, /* VSEN4 */ + {0x00, 0x01}, /* VSEN5 */ + {0x02, 0x01}, /* VSEN6 */ + {0x04, 0x01}, /* VSEN7 */ + {0x06, 0x01}, /* VSEN8 */ + {0x00, 0x02}, /* VSEN9 */ + {0x02, 0x02}, /* VSEN10 */ + {0x04, 0x02}, /* VSEN11 */ + {0x00, 0x03}, /* VTT */ + {0x02, 0x03}, /* 3VDD */ + {0x04, 0x03}, /* 3VSB */ + {0x06, 0x03}, /* VBAT */ + {0x06, 0x04}, /* VSEN12 */ + {0x06, 0x05}, /* VSEN13 */ + {0x06, 0x06}, /* VSEN14 */ + {0x06, 0x07}, /* VSEN15 */ + {0x06, 0x08}, /* VSEN16 */ + {0x06, 0x09}, /* VSEN17 */ +}; + + +#define W83795_REG_FAN(index) (0x2E + (index)) +#define W83795_REG_FAN_MIN_HL(index) (0xB6 + (index)) +#define W83795_REG_FAN_MIN_LSB(index) (0xC4 + (index) / 2) +#define W83795_REG_FAN_MIN_LSB_SHIFT(index) \ + (((index) & 1) ? 4 : 0) + +#define W83795_REG_VID_CTRL 0x6A + +#define W83795_REG_ALARM(index) (0x41 + (index)) +#define W83795_REG_BEEP(index) (0x50 + (index)) + +#define W83795_REG_CLR_CHASSIS 0x4D + + +#define W83795_REG_FCMS1 0x201 +#define W83795_REG_FCMS2 0x208 +#define W83795_REG_TFMR(index) (0x202 + (index)) +#define W83795_REG_FOMC 0x20F + +#define W83795_REG_TSS(index) (0x209 + (index)) + +#define PWM_OUTPUT 0 +#define PWM_FREQ 1 +#define PWM_START 2 +#define PWM_NONSTOP 3 +#define PWM_STOP_TIME 4 +#define W83795_REG_PWM(index, nr) (0x210 + (nr) * 8 + (index)) + +#define W83795_REG_FTSH(index) (0x240 + (index) * 2) +#define W83795_REG_FTSL(index) (0x241 + (index) * 2) +#define W83795_REG_TFTS 0x250 + +#define TEMP_PWM_TTTI 0 +#define TEMP_PWM_CTFS 1 +#define TEMP_PWM_HCT 2 +#define TEMP_PWM_HOT 3 +#define W83795_REG_TTTI(index) (0x260 + (index)) +#define W83795_REG_CTFS(index) (0x268 + (index)) +#define W83795_REG_HT(index) (0x270 + (index)) + +#define SF4_TEMP 0 +#define SF4_PWM 1 +#define W83795_REG_SF4_TEMP(temp_num, index) \ + (0x280 + 0x10 * (temp_num) + (index)) +#define W83795_REG_SF4_PWM(temp_num, index) \ + (0x288 + 0x10 * (temp_num) + (index)) + +#define W83795_REG_DTSC 0x301 +#define W83795_REG_DTSE 0x302 +#define W83795_REG_DTS(index) (0x26 + (index)) +#define W83795_REG_PECI_TBASE(index) (0x320 + (index)) + +#define DTS_CRIT 0 +#define DTS_CRIT_HYST 1 +#define DTS_WARN 2 +#define DTS_WARN_HYST 3 +#define W83795_REG_DTS_EXT(index) (0xB2 + (index)) + +#define SETUP_PWM_DEFAULT 0 +#define SETUP_PWM_UPTIME 1 +#define SETUP_PWM_DOWNTIME 2 +#define W83795_REG_SETUP_PWM(index) (0x20C + (index)) + +static inline u16 in_from_reg(u8 index, u16 val) +{ + /* 3VDD, 3VSB and VBAT: 6 mV/bit; other inputs: 2 mV/bit */ + if (index >= 12 && index <= 14) + return val * 6; + else + return val * 2; +} + +static inline u16 in_to_reg(u8 index, u16 val) +{ + if (index >= 12 && index <= 14) + return val / 6; + else + return val / 2; +} + +static inline unsigned long fan_from_reg(u16 val) +{ + if ((val == 0xfff) || (val == 0)) + return 0; + return 1350000UL / val; +} + +static inline u16 fan_to_reg(long rpm) +{ + if (rpm <= 0) + return 0x0fff; + return SENSORS_LIMIT((1350000 + (rpm >> 1)) / rpm, 1, 0xffe); +} + +static inline unsigned long time_from_reg(u8 reg) +{ + return reg * 100; +} + +static inline u8 time_to_reg(unsigned long val) +{ + return SENSORS_LIMIT((val + 50) / 100, 0, 0xff); +} + +static inline long temp_from_reg(s8 reg) +{ + return reg * 1000; +} + +static inline s8 temp_to_reg(long val, s8 min, s8 max) +{ + return SENSORS_LIMIT(val / 1000, min, max); +} + +static const u16 pwm_freq_cksel0[16] = { + 1024, 512, 341, 256, 205, 171, 146, 128, + 85, 64, 32, 16, 8, 4, 2, 1 +}; + +static unsigned int pwm_freq_from_reg(u8 reg, u16 clkin) +{ + unsigned long base_clock; + + if (reg & 0x80) { + base_clock = clkin * 1000 / ((clkin == 48000) ? 384 : 256); + return base_clock / ((reg & 0x7f) + 1); + } else + return pwm_freq_cksel0[reg & 0x0f]; +} + +static u8 pwm_freq_to_reg(unsigned long val, u16 clkin) +{ + unsigned long base_clock; + u8 reg0, reg1; + unsigned long best0, best1; + + /* Best fit for cksel = 0 */ + for (reg0 = 0; reg0 < ARRAY_SIZE(pwm_freq_cksel0) - 1; reg0++) { + if (val > (pwm_freq_cksel0[reg0] + + pwm_freq_cksel0[reg0 + 1]) / 2) + break; + } + if (val < 375) /* cksel = 1 can't beat this */ + return reg0; + best0 = pwm_freq_cksel0[reg0]; + + /* Best fit for cksel = 1 */ + base_clock = clkin * 1000 / ((clkin == 48000) ? 384 : 256); + reg1 = SENSORS_LIMIT(DIV_ROUND_CLOSEST(base_clock, val), 1, 128); + best1 = base_clock / reg1; + reg1 = 0x80 | (reg1 - 1); + + /* Choose the closest one */ + if (abs(val - best0) > abs(val - best1)) + return reg1; + else + return reg0; +} + +enum chip_types {w83795g, w83795adg}; + +struct w83795_data { + struct device *hwmon_dev; + struct mutex update_lock; + unsigned long last_updated; /* In jiffies */ + enum chip_types chip_type; + + u8 bank; + + u32 has_in; /* Enable monitor VIN or not */ + u8 has_dyn_in; /* Only in2-0 can have this */ + u16 in[21][3]; /* Register value, read/high/low */ + u8 in_lsb[10][3]; /* LSB Register value, high/low */ + u8 has_gain; /* has gain: in17-20 * 8 */ + + u16 has_fan; /* Enable fan14-1 or not */ + u16 fan[14]; /* Register value combine */ + u16 fan_min[14]; /* Register value combine */ + + u8 has_temp; /* Enable monitor temp6-1 or not */ + s8 temp[6][5]; /* current, crit, crit_hyst, warn, warn_hyst */ + u8 temp_read_vrlsb[6]; + u8 temp_mode; /* Bit vector, 0 = TR, 1 = TD */ + u8 temp_src[3]; /* Register value */ + + u8 enable_dts; /* Enable PECI and SB-TSI, + * bit 0: =1 enable, =0 disable, + * bit 1: =1 AMD SB-TSI, =0 Intel PECI */ + u8 has_dts; /* Enable monitor DTS temp */ + s8 dts[8]; /* Register value */ + u8 dts_read_vrlsb[8]; /* Register value */ + s8 dts_ext[4]; /* Register value */ + + u8 has_pwm; /* 795g supports 8 pwm, 795adg only supports 2, + * no config register, only affected by chip + * type */ + u8 pwm[8][5]; /* Register value, output, freq, start, + * non stop, stop time */ + u16 clkin; /* CLKIN frequency in kHz */ + u8 pwm_fcms[2]; /* Register value */ + u8 pwm_tfmr[6]; /* Register value */ + u8 pwm_fomc; /* Register value */ + + u16 target_speed[8]; /* Register value, target speed for speed + * cruise */ + u8 tol_speed; /* tolerance of target speed */ + u8 pwm_temp[6][4]; /* TTTI, CTFS, HCT, HOT */ + u8 sf4_reg[6][2][7]; /* 6 temp, temp/dcpwm, 7 registers */ + + u8 setup_pwm[3]; /* Register value */ + + u8 alarms[6]; /* Register value */ + u8 beeps[6]; /* Register value */ + + char valid; + char valid_limits; + char valid_pwm_config; +}; + +/* + * Hardware access + * We assume that nobdody can change the bank outside the driver. + */ + +/* Must be called with data->update_lock held, except during initialization */ +static int w83795_set_bank(struct i2c_client *client, u8 bank) +{ + struct w83795_data *data = i2c_get_clientdata(client); + int err; + + /* If the same bank is already set, nothing to do */ + if ((data->bank & 0x07) == bank) + return 0; + + /* Change to new bank, preserve all other bits */ + bank |= data->bank & ~0x07; + err = i2c_smbus_write_byte_data(client, W83795_REG_BANKSEL, bank); + if (err < 0) { + dev_err(&client->dev, + "Failed to set bank to %d, err %d\n", + (int)bank, err); + return err; + } + data->bank = bank; + + return 0; +} + +/* Must be called with data->update_lock held, except during initialization */ +static u8 w83795_read(struct i2c_client *client, u16 reg) +{ + int err; + + err = w83795_set_bank(client, reg >> 8); + if (err < 0) + return 0x00; /* Arbitrary */ + + err = i2c_smbus_read_byte_data(client, reg & 0xff); + if (err < 0) { + dev_err(&client->dev, + "Failed to read from register 0x%03x, err %d\n", + (int)reg, err); + return 0x00; /* Arbitrary */ + } + return err; +} + +/* Must be called with data->update_lock held, except during initialization */ +static int w83795_write(struct i2c_client *client, u16 reg, u8 value) +{ + int err; + + err = w83795_set_bank(client, reg >> 8); + if (err < 0) + return err; + + err = i2c_smbus_write_byte_data(client, reg & 0xff, value); + if (err < 0) + dev_err(&client->dev, + "Failed to write to register 0x%03x, err %d\n", + (int)reg, err); + return err; +} + +static void w83795_update_limits(struct i2c_client *client) +{ + struct w83795_data *data = i2c_get_clientdata(client); + int i, limit; + + /* Read the voltage limits */ + for (i = 0; i < ARRAY_SIZE(data->in); i++) { + if (!(data->has_in & (1 << i))) + continue; + data->in[i][IN_MAX] = + w83795_read(client, W83795_REG_IN[i][IN_MAX]); + data->in[i][IN_LOW] = + w83795_read(client, W83795_REG_IN[i][IN_LOW]); + } + for (i = 0; i < ARRAY_SIZE(data->in_lsb); i++) { + if ((i == 2 && data->chip_type == w83795adg) || + (i >= 4 && !(data->has_in & (1 << (i + 11))))) + continue; + data->in_lsb[i][IN_MAX] = + w83795_read(client, IN_LSB_REG(i, IN_MAX)); + data->in_lsb[i][IN_LOW] = + w83795_read(client, IN_LSB_REG(i, IN_LOW)); + } + + /* Read the fan limits */ + for (i = 0; i < ARRAY_SIZE(data->fan); i++) { + u8 lsb; + + /* Each register contains LSB for 2 fans, but we want to + * read it only once to save time */ + if ((i & 1) == 0 && (data->has_fan & (3 << i))) + lsb = w83795_read(client, W83795_REG_FAN_MIN_LSB(i)); + + if (!(data->has_fan & (1 << i))) + continue; + data->fan_min[i] = + w83795_read(client, W83795_REG_FAN_MIN_HL(i)) << 4; + data->fan_min[i] |= + (lsb >> W83795_REG_FAN_MIN_LSB_SHIFT(i)) & 0x0F; + } + + /* Read the temperature limits */ + for (i = 0; i < ARRAY_SIZE(data->temp); i++) { + if (!(data->has_temp & (1 << i))) + continue; + for (limit = TEMP_CRIT; limit <= TEMP_WARN_HYST; limit++) + data->temp[i][limit] = + w83795_read(client, W83795_REG_TEMP[i][limit]); + } + + /* Read the DTS limits */ + if (data->enable_dts) { + for (limit = DTS_CRIT; limit <= DTS_WARN_HYST; limit++) + data->dts_ext[limit] = + w83795_read(client, W83795_REG_DTS_EXT(limit)); + } + + /* Read beep settings */ + for (i = 0; i < ARRAY_SIZE(data->beeps); i++) + data->beeps[i] = w83795_read(client, W83795_REG_BEEP(i)); + + data->valid_limits = 1; +} + +static struct w83795_data *w83795_update_pwm_config(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + int i, tmp; + + mutex_lock(&data->update_lock); + + if (data->valid_pwm_config) + goto END; + + /* Read temperature source selection */ + for (i = 0; i < ARRAY_SIZE(data->temp_src); i++) + data->temp_src[i] = w83795_read(client, W83795_REG_TSS(i)); + + /* Read automatic fan speed control settings */ + data->pwm_fcms[0] = w83795_read(client, W83795_REG_FCMS1); + data->pwm_fcms[1] = w83795_read(client, W83795_REG_FCMS2); + for (i = 0; i < ARRAY_SIZE(data->pwm_tfmr); i++) + data->pwm_tfmr[i] = w83795_read(client, W83795_REG_TFMR(i)); + data->pwm_fomc = w83795_read(client, W83795_REG_FOMC); + for (i = 0; i < data->has_pwm; i++) { + for (tmp = PWM_FREQ; tmp <= PWM_STOP_TIME; tmp++) + data->pwm[i][tmp] = + w83795_read(client, W83795_REG_PWM(i, tmp)); + } + for (i = 0; i < ARRAY_SIZE(data->target_speed); i++) { + data->target_speed[i] = + w83795_read(client, W83795_REG_FTSH(i)) << 4; + data->target_speed[i] |= + w83795_read(client, W83795_REG_FTSL(i)) >> 4; + } + data->tol_speed = w83795_read(client, W83795_REG_TFTS) & 0x3f; + + for (i = 0; i < ARRAY_SIZE(data->pwm_temp); i++) { + data->pwm_temp[i][TEMP_PWM_TTTI] = + w83795_read(client, W83795_REG_TTTI(i)) & 0x7f; + data->pwm_temp[i][TEMP_PWM_CTFS] = + w83795_read(client, W83795_REG_CTFS(i)); + tmp = w83795_read(client, W83795_REG_HT(i)); + data->pwm_temp[i][TEMP_PWM_HCT] = tmp >> 4; + data->pwm_temp[i][TEMP_PWM_HOT] = tmp & 0x0f; + } + + /* Read SmartFanIV trip points */ + for (i = 0; i < ARRAY_SIZE(data->sf4_reg); i++) { + for (tmp = 0; tmp < 7; tmp++) { + data->sf4_reg[i][SF4_TEMP][tmp] = + w83795_read(client, + W83795_REG_SF4_TEMP(i, tmp)); + data->sf4_reg[i][SF4_PWM][tmp] = + w83795_read(client, W83795_REG_SF4_PWM(i, tmp)); + } + } + + /* Read setup PWM */ + for (i = 0; i < ARRAY_SIZE(data->setup_pwm); i++) + data->setup_pwm[i] = + w83795_read(client, W83795_REG_SETUP_PWM(i)); + + data->valid_pwm_config = 1; + +END: + mutex_unlock(&data->update_lock); + return data; +} + +static struct w83795_data *w83795_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + u16 tmp; + int i; + + mutex_lock(&data->update_lock); + + if (!data->valid_limits) + w83795_update_limits(client); + + if (!(time_after(jiffies, data->last_updated + HZ * 2) + || !data->valid)) + goto END; + + /* Update the voltages value */ + for (i = 0; i < ARRAY_SIZE(data->in); i++) { + if (!(data->has_in & (1 << i))) + continue; + tmp = w83795_read(client, W83795_REG_IN[i][IN_READ]) << 2; + tmp |= w83795_read(client, W83795_REG_VRLSB) >> 6; + data->in[i][IN_READ] = tmp; + } + + /* in0-2 can have dynamic limits (W83795G only) */ + if (data->has_dyn_in) { + u8 lsb_max = w83795_read(client, IN_LSB_REG(0, IN_MAX)); + u8 lsb_low = w83795_read(client, IN_LSB_REG(0, IN_LOW)); + + for (i = 0; i < 3; i++) { + if (!(data->has_dyn_in & (1 << i))) + continue; + data->in[i][IN_MAX] = + w83795_read(client, W83795_REG_IN[i][IN_MAX]); + data->in[i][IN_LOW] = + w83795_read(client, W83795_REG_IN[i][IN_LOW]); + data->in_lsb[i][IN_MAX] = (lsb_max >> (2 * i)) & 0x03; + data->in_lsb[i][IN_LOW] = (lsb_low >> (2 * i)) & 0x03; + } + } + + /* Update fan */ + for (i = 0; i < ARRAY_SIZE(data->fan); i++) { + if (!(data->has_fan & (1 << i))) + continue; + data->fan[i] = w83795_read(client, W83795_REG_FAN(i)) << 4; + data->fan[i] |= w83795_read(client, W83795_REG_VRLSB) >> 4; + } + + /* Update temperature */ + for (i = 0; i < ARRAY_SIZE(data->temp); i++) { + data->temp[i][TEMP_READ] = + w83795_read(client, W83795_REG_TEMP[i][TEMP_READ]); + data->temp_read_vrlsb[i] = + w83795_read(client, W83795_REG_VRLSB); + } + + /* Update dts temperature */ + if (data->enable_dts) { + for (i = 0; i < ARRAY_SIZE(data->dts); i++) { + if (!(data->has_dts & (1 << i))) + continue; + data->dts[i] = + w83795_read(client, W83795_REG_DTS(i)); + data->dts_read_vrlsb[i] = + w83795_read(client, W83795_REG_VRLSB); + } + } + + /* Update pwm output */ + for (i = 0; i < data->has_pwm; i++) { + data->pwm[i][PWM_OUTPUT] = + w83795_read(client, W83795_REG_PWM(i, PWM_OUTPUT)); + } + + /* update alarm */ + for (i = 0; i < ARRAY_SIZE(data->alarms); i++) + data->alarms[i] = w83795_read(client, W83795_REG_ALARM(i)); + + data->last_updated = jiffies; + data->valid = 1; + +END: + mutex_unlock(&data->update_lock); + return data; +} + +/* + * Sysfs attributes + */ + +#define ALARM_STATUS 0 +#define BEEP_ENABLE 1 +static ssize_t +show_alarm_beep(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83795_data *data = w83795_update_device(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index >> 3; + int bit = sensor_attr->index & 0x07; + u8 val; + + if (nr == ALARM_STATUS) + val = (data->alarms[index] >> bit) & 1; + else /* BEEP_ENABLE */ + val = (data->beeps[index] >> bit) & 1; + + return sprintf(buf, "%u\n", val); +} + +static ssize_t +store_beep(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index >> 3; + int shift = sensor_attr->index & 0x07; + u8 beep_bit = 1 << shift; + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + if (val != 0 && val != 1) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->beeps[index] = w83795_read(client, W83795_REG_BEEP(index)); + data->beeps[index] &= ~beep_bit; + data->beeps[index] |= val << shift; + w83795_write(client, W83795_REG_BEEP(index), data->beeps[index]); + mutex_unlock(&data->update_lock); + + return count; +} + +/* Write 0 to clear chassis alarm */ +static ssize_t +store_chassis_clear(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0 || val != 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + val = w83795_read(client, W83795_REG_CLR_CHASSIS); + val |= 0x80; + w83795_write(client, W83795_REG_CLR_CHASSIS, val); + mutex_unlock(&data->update_lock); + return count; +} + +#define FAN_INPUT 0 +#define FAN_MIN 1 +static ssize_t +show_fan(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + struct w83795_data *data = w83795_update_device(dev); + u16 val; + + if (nr == FAN_INPUT) + val = data->fan[index] & 0x0fff; + else + val = data->fan_min[index] & 0x0fff; + + return sprintf(buf, "%lu\n", fan_from_reg(val)); +} + +static ssize_t +store_fan_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + unsigned long val; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + val = fan_to_reg(val); + + mutex_lock(&data->update_lock); + data->fan_min[index] = val; + w83795_write(client, W83795_REG_FAN_MIN_HL(index), (val >> 4) & 0xff); + val &= 0x0f; + if (index & 1) { + val <<= 4; + val |= w83795_read(client, W83795_REG_FAN_MIN_LSB(index)) + & 0x0f; + } else { + val |= w83795_read(client, W83795_REG_FAN_MIN_LSB(index)) + & 0xf0; + } + w83795_write(client, W83795_REG_FAN_MIN_LSB(index), val & 0xff); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t +show_pwm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83795_data *data; + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + unsigned int val; + + data = nr == PWM_OUTPUT ? w83795_update_device(dev) + : w83795_update_pwm_config(dev); + + switch (nr) { + case PWM_STOP_TIME: + val = time_from_reg(data->pwm[index][nr]); + break; + case PWM_FREQ: + val = pwm_freq_from_reg(data->pwm[index][nr], data->clkin); + break; + default: + val = data->pwm[index][nr]; + break; + } + + return sprintf(buf, "%u\n", val); +} + +static ssize_t +store_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + switch (nr) { + case PWM_STOP_TIME: + val = time_to_reg(val); + break; + case PWM_FREQ: + val = pwm_freq_to_reg(val, data->clkin); + break; + default: + val = SENSORS_LIMIT(val, 0, 0xff); + break; + } + w83795_write(client, W83795_REG_PWM(index, nr), val); + data->pwm[index][nr] = val; + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + struct w83795_data *data = w83795_update_pwm_config(dev); + int index = sensor_attr->index; + u8 tmp; + + if (1 == (data->pwm_fcms[0] & (1 << index))) { + tmp = 2; + goto out; + } + for (tmp = 0; tmp < 6; tmp++) { + if (data->pwm_tfmr[tmp] & (1 << index)) { + tmp = 3; + goto out; + } + } + if (data->pwm_fomc & (1 << index)) + tmp = 0; + else + tmp = 1; + +out: + return sprintf(buf, "%u\n", tmp); +} + +static ssize_t +store_pwm_enable(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = w83795_update_pwm_config(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index; + unsigned long val; + int i; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + if (val > 2) + return -EINVAL; + + mutex_lock(&data->update_lock); + switch (val) { + case 0: + case 1: + data->pwm_fcms[0] &= ~(1 << index); + w83795_write(client, W83795_REG_FCMS1, data->pwm_fcms[0]); + for (i = 0; i < 6; i++) { + data->pwm_tfmr[i] &= ~(1 << index); + w83795_write(client, W83795_REG_TFMR(i), + data->pwm_tfmr[i]); + } + data->pwm_fomc |= 1 << index; + data->pwm_fomc ^= val << index; + w83795_write(client, W83795_REG_FOMC, data->pwm_fomc); + break; + case 2: + data->pwm_fcms[0] |= (1 << index); + w83795_write(client, W83795_REG_FCMS1, data->pwm_fcms[0]); + break; + } + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_temp_src(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + struct w83795_data *data = w83795_update_pwm_config(dev); + int index = sensor_attr->index; + u8 val = index / 2; + u8 tmp = data->temp_src[val]; + + if (index & 1) + val = 4; + else + val = 0; + tmp >>= val; + tmp &= 0x0f; + + return sprintf(buf, "%u\n", tmp); +} + +static ssize_t +store_temp_src(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = w83795_update_pwm_config(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index; + unsigned long tmp; + u8 val = index / 2; + + if (strict_strtoul(buf, 10, &tmp) < 0) + return -EINVAL; + tmp = SENSORS_LIMIT(tmp, 0, 15); + + mutex_lock(&data->update_lock); + if (index & 1) { + tmp <<= 4; + data->temp_src[val] &= 0x0f; + } else { + data->temp_src[val] &= 0xf0; + } + data->temp_src[val] |= tmp; + w83795_write(client, W83795_REG_TSS(val), data->temp_src[val]); + mutex_unlock(&data->update_lock); + + return count; +} + +#define TEMP_PWM_ENABLE 0 +#define TEMP_PWM_FAN_MAP 1 +static ssize_t +show_temp_pwm_enable(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct w83795_data *data = w83795_update_pwm_config(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + u8 tmp = 0xff; + + switch (nr) { + case TEMP_PWM_ENABLE: + tmp = (data->pwm_fcms[1] >> index) & 1; + if (tmp) + tmp = 4; + else + tmp = 3; + break; + case TEMP_PWM_FAN_MAP: + tmp = data->pwm_tfmr[index]; + break; + } + + return sprintf(buf, "%u\n", tmp); +} + +static ssize_t +store_temp_pwm_enable(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = w83795_update_pwm_config(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + unsigned long tmp; + + if (strict_strtoul(buf, 10, &tmp) < 0) + return -EINVAL; + + switch (nr) { + case TEMP_PWM_ENABLE: + if (tmp != 3 && tmp != 4) + return -EINVAL; + tmp -= 3; + mutex_lock(&data->update_lock); + data->pwm_fcms[1] &= ~(1 << index); + data->pwm_fcms[1] |= tmp << index; + w83795_write(client, W83795_REG_FCMS2, data->pwm_fcms[1]); + mutex_unlock(&data->update_lock); + break; + case TEMP_PWM_FAN_MAP: + mutex_lock(&data->update_lock); + tmp = SENSORS_LIMIT(tmp, 0, 0xff); + w83795_write(client, W83795_REG_TFMR(index), tmp); + data->pwm_tfmr[index] = tmp; + mutex_unlock(&data->update_lock); + break; + } + return count; +} + +#define FANIN_TARGET 0 +#define FANIN_TOL 1 +static ssize_t +show_fanin(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83795_data *data = w83795_update_pwm_config(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + u16 tmp = 0; + + switch (nr) { + case FANIN_TARGET: + tmp = fan_from_reg(data->target_speed[index]); + break; + case FANIN_TOL: + tmp = data->tol_speed; + break; + } + + return sprintf(buf, "%u\n", tmp); +} + +static ssize_t +store_fanin(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + switch (nr) { + case FANIN_TARGET: + val = fan_to_reg(SENSORS_LIMIT(val, 0, 0xfff)); + w83795_write(client, W83795_REG_FTSH(index), val >> 4); + w83795_write(client, W83795_REG_FTSL(index), (val << 4) & 0xf0); + data->target_speed[index] = val; + break; + case FANIN_TOL: + val = SENSORS_LIMIT(val, 0, 0x3f); + w83795_write(client, W83795_REG_TFTS, val); + data->tol_speed = val; + break; + } + mutex_unlock(&data->update_lock); + + return count; +} + + +static ssize_t +show_temp_pwm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83795_data *data = w83795_update_pwm_config(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + long tmp = temp_from_reg(data->pwm_temp[index][nr]); + + return sprintf(buf, "%ld\n", tmp); +} + +static ssize_t +store_temp_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + unsigned long val; + u8 tmp; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + val /= 1000; + + mutex_lock(&data->update_lock); + switch (nr) { + case TEMP_PWM_TTTI: + val = SENSORS_LIMIT(val, 0, 0x7f); + w83795_write(client, W83795_REG_TTTI(index), val); + break; + case TEMP_PWM_CTFS: + val = SENSORS_LIMIT(val, 0, 0x7f); + w83795_write(client, W83795_REG_CTFS(index), val); + break; + case TEMP_PWM_HCT: + val = SENSORS_LIMIT(val, 0, 0x0f); + tmp = w83795_read(client, W83795_REG_HT(index)); + tmp &= 0x0f; + tmp |= (val << 4) & 0xf0; + w83795_write(client, W83795_REG_HT(index), tmp); + break; + case TEMP_PWM_HOT: + val = SENSORS_LIMIT(val, 0, 0x0f); + tmp = w83795_read(client, W83795_REG_HT(index)); + tmp &= 0xf0; + tmp |= val & 0x0f; + w83795_write(client, W83795_REG_HT(index), tmp); + break; + } + data->pwm_temp[index][nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t +show_sf4_pwm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83795_data *data = w83795_update_pwm_config(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + + return sprintf(buf, "%u\n", data->sf4_reg[index][SF4_PWM][nr]); +} + +static ssize_t +store_sf4_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + w83795_write(client, W83795_REG_SF4_PWM(index, nr), val); + data->sf4_reg[index][SF4_PWM][nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t +show_sf4_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83795_data *data = w83795_update_pwm_config(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + + return sprintf(buf, "%u\n", + (data->sf4_reg[index][SF4_TEMP][nr]) * 1000); +} + +static ssize_t +store_sf4_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + val /= 1000; + + mutex_lock(&data->update_lock); + w83795_write(client, W83795_REG_SF4_TEMP(index, nr), val); + data->sf4_reg[index][SF4_TEMP][nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + + +static ssize_t +show_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + struct w83795_data *data = w83795_update_device(dev); + long temp = temp_from_reg(data->temp[index][nr]); + + if (nr == TEMP_READ) + temp += (data->temp_read_vrlsb[index] >> 6) * 250; + return sprintf(buf, "%ld\n", temp); +} + +static ssize_t +store_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + long tmp; + + if (strict_strtol(buf, 10, &tmp) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->temp[index][nr] = temp_to_reg(tmp, -128, 127); + w83795_write(client, W83795_REG_TEMP[index][nr], data->temp[index][nr]); + mutex_unlock(&data->update_lock); + return count; +} + + +static ssize_t +show_dts_mode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83795_data *data = dev_get_drvdata(dev); + int tmp; + + if (data->enable_dts & 2) + tmp = 5; + else + tmp = 6; + + return sprintf(buf, "%d\n", tmp); +} + +static ssize_t +show_dts(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index; + struct w83795_data *data = w83795_update_device(dev); + long temp = temp_from_reg(data->dts[index]); + + temp += (data->dts_read_vrlsb[index] >> 6) * 250; + return sprintf(buf, "%ld\n", temp); +} + +static ssize_t +show_dts_ext(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + struct w83795_data *data = dev_get_drvdata(dev); + long temp = temp_from_reg(data->dts_ext[nr]); + + return sprintf(buf, "%ld\n", temp); +} + +static ssize_t +store_dts_ext(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + long tmp; + + if (strict_strtol(buf, 10, &tmp) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->dts_ext[nr] = temp_to_reg(tmp, -128, 127); + w83795_write(client, W83795_REG_DTS_EXT(nr), data->dts_ext[nr]); + mutex_unlock(&data->update_lock); + return count; +} + + +static ssize_t +show_temp_mode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83795_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index; + int tmp; + + if (data->temp_mode & (1 << index)) + tmp = 3; /* Thermal diode */ + else + tmp = 4; /* Thermistor */ + + return sprintf(buf, "%d\n", tmp); +} + +/* Only for temp1-4 (temp5-6 can only be thermistor) */ +static ssize_t +store_temp_mode(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int index = sensor_attr->index; + int reg_shift; + unsigned long val; + u8 tmp; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + if ((val != 4) && (val != 3)) + return -EINVAL; + + mutex_lock(&data->update_lock); + if (val == 3) { + /* Thermal diode */ + val = 0x01; + data->temp_mode |= 1 << index; + } else if (val == 4) { + /* Thermistor */ + val = 0x03; + data->temp_mode &= ~(1 << index); + } + + reg_shift = 2 * index; + tmp = w83795_read(client, W83795_REG_TEMP_CTRL2); + tmp &= ~(0x03 << reg_shift); + tmp |= val << reg_shift; + w83795_write(client, W83795_REG_TEMP_CTRL2, tmp); + + mutex_unlock(&data->update_lock); + return count; +} + + +/* show/store VIN */ +static ssize_t +show_in(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + struct w83795_data *data = w83795_update_device(dev); + u16 val = data->in[index][nr]; + u8 lsb_idx; + + switch (nr) { + case IN_READ: + /* calculate this value again by sensors as sensors3.conf */ + if ((index >= 17) && + !((data->has_gain >> (index - 17)) & 1)) + val *= 8; + break; + case IN_MAX: + case IN_LOW: + lsb_idx = IN_LSB_SHIFT_IDX[index][IN_LSB_IDX]; + val <<= 2; + val |= (data->in_lsb[lsb_idx][nr] >> + IN_LSB_SHIFT_IDX[index][IN_LSB_SHIFT]) & 0x03; + if ((index >= 17) && + !((data->has_gain >> (index - 17)) & 1)) + val *= 8; + break; + } + val = in_from_reg(index, val); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t +store_in(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int index = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + unsigned long val; + u8 tmp; + u8 lsb_idx; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + val = in_to_reg(index, val); + + if ((index >= 17) && + !((data->has_gain >> (index - 17)) & 1)) + val /= 8; + val = SENSORS_LIMIT(val, 0, 0x3FF); + mutex_lock(&data->update_lock); + + lsb_idx = IN_LSB_SHIFT_IDX[index][IN_LSB_IDX]; + tmp = w83795_read(client, IN_LSB_REG(lsb_idx, nr)); + tmp &= ~(0x03 << IN_LSB_SHIFT_IDX[index][IN_LSB_SHIFT]); + tmp |= (val & 0x03) << IN_LSB_SHIFT_IDX[index][IN_LSB_SHIFT]; + w83795_write(client, IN_LSB_REG(lsb_idx, nr), tmp); + data->in_lsb[lsb_idx][nr] = tmp; + + tmp = (val >> 2) & 0xff; + w83795_write(client, W83795_REG_IN[index][nr], tmp); + data->in[index][nr] = tmp; + + mutex_unlock(&data->update_lock); + return count; +} + + +#ifdef CONFIG_SENSORS_W83795_FANCTRL +static ssize_t +show_sf_setup(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + struct w83795_data *data = w83795_update_pwm_config(dev); + u16 val = data->setup_pwm[nr]; + + switch (nr) { + case SETUP_PWM_UPTIME: + case SETUP_PWM_DOWNTIME: + val = time_from_reg(val); + break; + } + + return sprintf(buf, "%d\n", val); +} + +static ssize_t +store_sf_setup(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + struct i2c_client *client = to_i2c_client(dev); + struct w83795_data *data = i2c_get_clientdata(client); + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + + switch (nr) { + case SETUP_PWM_DEFAULT: + val = SENSORS_LIMIT(val, 0, 0xff); + break; + case SETUP_PWM_UPTIME: + case SETUP_PWM_DOWNTIME: + val = time_to_reg(val); + if (val == 0) + return -EINVAL; + break; + } + + mutex_lock(&data->update_lock); + data->setup_pwm[nr] = val; + w83795_write(client, W83795_REG_SETUP_PWM(nr), val); + mutex_unlock(&data->update_lock); + return count; +} +#endif + + +#define NOT_USED -1 + +/* Don't change the attribute order, _max and _min are accessed by index + * somewhere else in the code */ +#define SENSOR_ATTR_IN(index) { \ + SENSOR_ATTR_2(in##index##_input, S_IRUGO, show_in, NULL, \ + IN_READ, index), \ + SENSOR_ATTR_2(in##index##_max, S_IRUGO | S_IWUSR, show_in, \ + store_in, IN_MAX, index), \ + SENSOR_ATTR_2(in##index##_min, S_IRUGO | S_IWUSR, show_in, \ + store_in, IN_LOW, index), \ + SENSOR_ATTR_2(in##index##_alarm, S_IRUGO, show_alarm_beep, \ + NULL, ALARM_STATUS, index + ((index > 14) ? 1 : 0)), \ + SENSOR_ATTR_2(in##index##_beep, S_IWUSR | S_IRUGO, \ + show_alarm_beep, store_beep, BEEP_ENABLE, \ + index + ((index > 14) ? 1 : 0)) } + +#define SENSOR_ATTR_FAN(index) { \ + SENSOR_ATTR_2(fan##index##_input, S_IRUGO, show_fan, \ + NULL, FAN_INPUT, index - 1), \ + SENSOR_ATTR_2(fan##index##_min, S_IWUSR | S_IRUGO, \ + show_fan, store_fan_min, FAN_MIN, index - 1), \ + SENSOR_ATTR_2(fan##index##_alarm, S_IRUGO, show_alarm_beep, \ + NULL, ALARM_STATUS, index + 31), \ + SENSOR_ATTR_2(fan##index##_beep, S_IWUSR | S_IRUGO, \ + show_alarm_beep, store_beep, BEEP_ENABLE, index + 31) } + +#define SENSOR_ATTR_PWM(index) { \ + SENSOR_ATTR_2(pwm##index, S_IWUSR | S_IRUGO, show_pwm, \ + store_pwm, PWM_OUTPUT, index - 1), \ + SENSOR_ATTR_2(pwm##index##_nonstop, S_IWUSR | S_IRUGO, \ + show_pwm, store_pwm, PWM_NONSTOP, index - 1), \ + SENSOR_ATTR_2(pwm##index##_start, S_IWUSR | S_IRUGO, \ + show_pwm, store_pwm, PWM_START, index - 1), \ + SENSOR_ATTR_2(pwm##index##_stop_time, S_IWUSR | S_IRUGO, \ + show_pwm, store_pwm, PWM_STOP_TIME, index - 1), \ + SENSOR_ATTR_2(pwm##index##_freq, S_IWUSR | S_IRUGO, \ + show_pwm, store_pwm, PWM_FREQ, index - 1), \ + SENSOR_ATTR_2(pwm##index##_enable, S_IWUSR | S_IRUGO, \ + show_pwm_enable, store_pwm_enable, NOT_USED, index - 1), \ + SENSOR_ATTR_2(fan##index##_target, S_IWUSR | S_IRUGO, \ + show_fanin, store_fanin, FANIN_TARGET, index - 1) } + +#define SENSOR_ATTR_DTS(index) { \ + SENSOR_ATTR_2(temp##index##_type, S_IRUGO , \ + show_dts_mode, NULL, NOT_USED, index - 7), \ + SENSOR_ATTR_2(temp##index##_input, S_IRUGO, show_dts, \ + NULL, NOT_USED, index - 7), \ + SENSOR_ATTR_2(temp##index##_crit, S_IRUGO | S_IWUSR, show_dts_ext, \ + store_dts_ext, DTS_CRIT, NOT_USED), \ + SENSOR_ATTR_2(temp##index##_crit_hyst, S_IRUGO | S_IWUSR, \ + show_dts_ext, store_dts_ext, DTS_CRIT_HYST, NOT_USED), \ + SENSOR_ATTR_2(temp##index##_max, S_IRUGO | S_IWUSR, show_dts_ext, \ + store_dts_ext, DTS_WARN, NOT_USED), \ + SENSOR_ATTR_2(temp##index##_max_hyst, S_IRUGO | S_IWUSR, \ + show_dts_ext, store_dts_ext, DTS_WARN_HYST, NOT_USED), \ + SENSOR_ATTR_2(temp##index##_alarm, S_IRUGO, \ + show_alarm_beep, NULL, ALARM_STATUS, index + 17), \ + SENSOR_ATTR_2(temp##index##_beep, S_IWUSR | S_IRUGO, \ + show_alarm_beep, store_beep, BEEP_ENABLE, index + 17) } + +#define SENSOR_ATTR_TEMP(index) { \ + SENSOR_ATTR_2(temp##index##_type, S_IRUGO | (index < 4 ? S_IWUSR : 0), \ + show_temp_mode, store_temp_mode, NOT_USED, index - 1), \ + SENSOR_ATTR_2(temp##index##_input, S_IRUGO, show_temp, \ + NULL, TEMP_READ, index - 1), \ + SENSOR_ATTR_2(temp##index##_crit, S_IRUGO | S_IWUSR, show_temp, \ + store_temp, TEMP_CRIT, index - 1), \ + SENSOR_ATTR_2(temp##index##_crit_hyst, S_IRUGO | S_IWUSR, \ + show_temp, store_temp, TEMP_CRIT_HYST, index - 1), \ + SENSOR_ATTR_2(temp##index##_max, S_IRUGO | S_IWUSR, show_temp, \ + store_temp, TEMP_WARN, index - 1), \ + SENSOR_ATTR_2(temp##index##_max_hyst, S_IRUGO | S_IWUSR, \ + show_temp, store_temp, TEMP_WARN_HYST, index - 1), \ + SENSOR_ATTR_2(temp##index##_alarm, S_IRUGO, \ + show_alarm_beep, NULL, ALARM_STATUS, \ + index + (index > 4 ? 11 : 17)), \ + SENSOR_ATTR_2(temp##index##_beep, S_IWUSR | S_IRUGO, \ + show_alarm_beep, store_beep, BEEP_ENABLE, \ + index + (index > 4 ? 11 : 17)), \ + SENSOR_ATTR_2(temp##index##_source_sel, S_IWUSR | S_IRUGO, \ + show_temp_src, store_temp_src, NOT_USED, index - 1), \ + SENSOR_ATTR_2(temp##index##_pwm_enable, S_IWUSR | S_IRUGO, \ + show_temp_pwm_enable, store_temp_pwm_enable, \ + TEMP_PWM_ENABLE, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_channels_pwm, S_IWUSR | S_IRUGO, \ + show_temp_pwm_enable, store_temp_pwm_enable, \ + TEMP_PWM_FAN_MAP, index - 1), \ + SENSOR_ATTR_2(thermal_cruise##index, S_IWUSR | S_IRUGO, \ + show_temp_pwm, store_temp_pwm, TEMP_PWM_TTTI, index - 1), \ + SENSOR_ATTR_2(temp##index##_warn, S_IWUSR | S_IRUGO, \ + show_temp_pwm, store_temp_pwm, TEMP_PWM_CTFS, index - 1), \ + SENSOR_ATTR_2(temp##index##_warn_hyst, S_IWUSR | S_IRUGO, \ + show_temp_pwm, store_temp_pwm, TEMP_PWM_HCT, index - 1), \ + SENSOR_ATTR_2(temp##index##_operation_hyst, S_IWUSR | S_IRUGO, \ + show_temp_pwm, store_temp_pwm, TEMP_PWM_HOT, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point1_pwm, S_IRUGO | S_IWUSR, \ + show_sf4_pwm, store_sf4_pwm, 0, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point2_pwm, S_IRUGO | S_IWUSR, \ + show_sf4_pwm, store_sf4_pwm, 1, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point3_pwm, S_IRUGO | S_IWUSR, \ + show_sf4_pwm, store_sf4_pwm, 2, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point4_pwm, S_IRUGO | S_IWUSR, \ + show_sf4_pwm, store_sf4_pwm, 3, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point5_pwm, S_IRUGO | S_IWUSR, \ + show_sf4_pwm, store_sf4_pwm, 4, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point6_pwm, S_IRUGO | S_IWUSR, \ + show_sf4_pwm, store_sf4_pwm, 5, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point7_pwm, S_IRUGO | S_IWUSR, \ + show_sf4_pwm, store_sf4_pwm, 6, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point1_temp, S_IRUGO | S_IWUSR,\ + show_sf4_temp, store_sf4_temp, 0, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point2_temp, S_IRUGO | S_IWUSR,\ + show_sf4_temp, store_sf4_temp, 1, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point3_temp, S_IRUGO | S_IWUSR,\ + show_sf4_temp, store_sf4_temp, 2, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point4_temp, S_IRUGO | S_IWUSR,\ + show_sf4_temp, store_sf4_temp, 3, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point5_temp, S_IRUGO | S_IWUSR,\ + show_sf4_temp, store_sf4_temp, 4, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point6_temp, S_IRUGO | S_IWUSR,\ + show_sf4_temp, store_sf4_temp, 5, index - 1), \ + SENSOR_ATTR_2(temp##index##_auto_point7_temp, S_IRUGO | S_IWUSR,\ + show_sf4_temp, store_sf4_temp, 6, index - 1) } + + +static struct sensor_device_attribute_2 w83795_in[][5] = { + SENSOR_ATTR_IN(0), + SENSOR_ATTR_IN(1), + SENSOR_ATTR_IN(2), + SENSOR_ATTR_IN(3), + SENSOR_ATTR_IN(4), + SENSOR_ATTR_IN(5), + SENSOR_ATTR_IN(6), + SENSOR_ATTR_IN(7), + SENSOR_ATTR_IN(8), + SENSOR_ATTR_IN(9), + SENSOR_ATTR_IN(10), + SENSOR_ATTR_IN(11), + SENSOR_ATTR_IN(12), + SENSOR_ATTR_IN(13), + SENSOR_ATTR_IN(14), + SENSOR_ATTR_IN(15), + SENSOR_ATTR_IN(16), + SENSOR_ATTR_IN(17), + SENSOR_ATTR_IN(18), + SENSOR_ATTR_IN(19), + SENSOR_ATTR_IN(20), +}; + +static const struct sensor_device_attribute_2 w83795_fan[][4] = { + SENSOR_ATTR_FAN(1), + SENSOR_ATTR_FAN(2), + SENSOR_ATTR_FAN(3), + SENSOR_ATTR_FAN(4), + SENSOR_ATTR_FAN(5), + SENSOR_ATTR_FAN(6), + SENSOR_ATTR_FAN(7), + SENSOR_ATTR_FAN(8), + SENSOR_ATTR_FAN(9), + SENSOR_ATTR_FAN(10), + SENSOR_ATTR_FAN(11), + SENSOR_ATTR_FAN(12), + SENSOR_ATTR_FAN(13), + SENSOR_ATTR_FAN(14), +}; + +static const struct sensor_device_attribute_2 w83795_temp[][29] = { + SENSOR_ATTR_TEMP(1), + SENSOR_ATTR_TEMP(2), + SENSOR_ATTR_TEMP(3), + SENSOR_ATTR_TEMP(4), + SENSOR_ATTR_TEMP(5), + SENSOR_ATTR_TEMP(6), +}; + +static const struct sensor_device_attribute_2 w83795_dts[][8] = { + SENSOR_ATTR_DTS(7), + SENSOR_ATTR_DTS(8), + SENSOR_ATTR_DTS(9), + SENSOR_ATTR_DTS(10), + SENSOR_ATTR_DTS(11), + SENSOR_ATTR_DTS(12), + SENSOR_ATTR_DTS(13), + SENSOR_ATTR_DTS(14), +}; + +static const struct sensor_device_attribute_2 w83795_pwm[][7] = { + SENSOR_ATTR_PWM(1), + SENSOR_ATTR_PWM(2), + SENSOR_ATTR_PWM(3), + SENSOR_ATTR_PWM(4), + SENSOR_ATTR_PWM(5), + SENSOR_ATTR_PWM(6), + SENSOR_ATTR_PWM(7), + SENSOR_ATTR_PWM(8), +}; + +static const struct sensor_device_attribute_2 sda_single_files[] = { + SENSOR_ATTR_2(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm_beep, + store_chassis_clear, ALARM_STATUS, 46), + SENSOR_ATTR_2(intrusion0_beep, S_IWUSR | S_IRUGO, show_alarm_beep, + store_beep, BEEP_ENABLE, 46), + SENSOR_ATTR_2(beep_enable, S_IWUSR | S_IRUGO, show_alarm_beep, + store_beep, BEEP_ENABLE, 47), +#ifdef CONFIG_SENSORS_W83795_FANCTRL + SENSOR_ATTR_2(speed_cruise_tolerance, S_IWUSR | S_IRUGO, show_fanin, + store_fanin, FANIN_TOL, NOT_USED), + SENSOR_ATTR_2(pwm_default, S_IWUSR | S_IRUGO, show_sf_setup, + store_sf_setup, SETUP_PWM_DEFAULT, NOT_USED), + SENSOR_ATTR_2(pwm_uptime, S_IWUSR | S_IRUGO, show_sf_setup, + store_sf_setup, SETUP_PWM_UPTIME, NOT_USED), + SENSOR_ATTR_2(pwm_downtime, S_IWUSR | S_IRUGO, show_sf_setup, + store_sf_setup, SETUP_PWM_DOWNTIME, NOT_USED), +#endif +}; + +/* + * Driver interface + */ + +static void w83795_init_client(struct i2c_client *client) +{ + struct w83795_data *data = i2c_get_clientdata(client); + static const u16 clkin[4] = { /* in kHz */ + 14318, 24000, 33333, 48000 + }; + u8 config; + + if (reset) + w83795_write(client, W83795_REG_CONFIG, 0x80); + + /* Start monitoring if needed */ + config = w83795_read(client, W83795_REG_CONFIG); + if (!(config & W83795_REG_CONFIG_START)) { + dev_info(&client->dev, "Enabling monitoring operations\n"); + w83795_write(client, W83795_REG_CONFIG, + config | W83795_REG_CONFIG_START); + } + + data->clkin = clkin[(config >> 3) & 0x3]; + dev_dbg(&client->dev, "clkin = %u kHz\n", data->clkin); +} + +static int w83795_get_device_id(struct i2c_client *client) +{ + int device_id; + + device_id = i2c_smbus_read_byte_data(client, W83795_REG_DEVICEID); + + /* Special case for rev. A chips; can't be checked first because later + revisions emulate this for compatibility */ + if (device_id < 0 || (device_id & 0xf0) != 0x50) { + int alt_id; + + alt_id = i2c_smbus_read_byte_data(client, + W83795_REG_DEVICEID_A); + if (alt_id == 0x50) + device_id = alt_id; + } + + return device_id; +} + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int w83795_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + int bank, vendor_id, device_id, expected, i2c_addr, config; + struct i2c_adapter *adapter = client->adapter; + unsigned short address = client->addr; + const char *chip_name; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + bank = i2c_smbus_read_byte_data(client, W83795_REG_BANKSEL); + if (bank < 0 || (bank & 0x7c)) { + dev_dbg(&adapter->dev, + "w83795: Detection failed at addr 0x%02hx, check %s\n", + address, "bank"); + return -ENODEV; + } + + /* Check Nuvoton vendor ID */ + vendor_id = i2c_smbus_read_byte_data(client, W83795_REG_VENDORID); + expected = bank & 0x80 ? 0x5c : 0xa3; + if (vendor_id != expected) { + dev_dbg(&adapter->dev, + "w83795: Detection failed at addr 0x%02hx, check %s\n", + address, "vendor id"); + return -ENODEV; + } + + /* Check device ID */ + device_id = w83795_get_device_id(client) | + (i2c_smbus_read_byte_data(client, W83795_REG_CHIPID) << 8); + if ((device_id >> 4) != 0x795) { + dev_dbg(&adapter->dev, + "w83795: Detection failed at addr 0x%02hx, check %s\n", + address, "device id\n"); + return -ENODEV; + } + + /* If Nuvoton chip, address of chip and W83795_REG_I2C_ADDR + should match */ + if ((bank & 0x07) == 0) { + i2c_addr = i2c_smbus_read_byte_data(client, + W83795_REG_I2C_ADDR); + if ((i2c_addr & 0x7f) != address) { + dev_dbg(&adapter->dev, + "w83795: Detection failed at addr 0x%02hx, " + "check %s\n", address, "i2c addr"); + return -ENODEV; + } + } + + /* Check 795 chip type: 795G or 795ADG + Usually we don't write to chips during detection, but here we don't + quite have the choice; hopefully it's OK, we are about to return + success anyway */ + if ((bank & 0x07) != 0) + i2c_smbus_write_byte_data(client, W83795_REG_BANKSEL, + bank & ~0x07); + config = i2c_smbus_read_byte_data(client, W83795_REG_CONFIG); + if (config & W83795_REG_CONFIG_CONFIG48) + chip_name = "w83795adg"; + else + chip_name = "w83795g"; + + strlcpy(info->type, chip_name, I2C_NAME_SIZE); + dev_info(&adapter->dev, "Found %s rev. %c at 0x%02hx\n", chip_name, + 'A' + (device_id & 0xf), address); + + return 0; +} + +static int w83795_handle_files(struct device *dev, int (*fn)(struct device *, + const struct device_attribute *)) +{ + struct w83795_data *data = dev_get_drvdata(dev); + int err, i, j; + + for (i = 0; i < ARRAY_SIZE(w83795_in); i++) { + if (!(data->has_in & (1 << i))) + continue; + for (j = 0; j < ARRAY_SIZE(w83795_in[0]); j++) { + err = fn(dev, &w83795_in[i][j].dev_attr); + if (err) + return err; + } + } + + for (i = 0; i < ARRAY_SIZE(w83795_fan); i++) { + if (!(data->has_fan & (1 << i))) + continue; + for (j = 0; j < ARRAY_SIZE(w83795_fan[0]); j++) { + err = fn(dev, &w83795_fan[i][j].dev_attr); + if (err) + return err; + } + } + + for (i = 0; i < ARRAY_SIZE(sda_single_files); i++) { + err = fn(dev, &sda_single_files[i].dev_attr); + if (err) + return err; + } + +#ifdef CONFIG_SENSORS_W83795_FANCTRL + for (i = 0; i < data->has_pwm; i++) { + for (j = 0; j < ARRAY_SIZE(w83795_pwm[0]); j++) { + err = fn(dev, &w83795_pwm[i][j].dev_attr); + if (err) + return err; + } + } +#endif + + for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) { + if (!(data->has_temp & (1 << i))) + continue; +#ifdef CONFIG_SENSORS_W83795_FANCTRL + for (j = 0; j < ARRAY_SIZE(w83795_temp[0]); j++) { +#else + for (j = 0; j < 8; j++) { +#endif + err = fn(dev, &w83795_temp[i][j].dev_attr); + if (err) + return err; + } + } + + if (data->enable_dts) { + for (i = 0; i < ARRAY_SIZE(w83795_dts); i++) { + if (!(data->has_dts & (1 << i))) + continue; + for (j = 0; j < ARRAY_SIZE(w83795_dts[0]); j++) { + err = fn(dev, &w83795_dts[i][j].dev_attr); + if (err) + return err; + } + } + } + + return 0; +} + +/* We need a wrapper that fits in w83795_handle_files */ +static int device_remove_file_wrapper(struct device *dev, + const struct device_attribute *attr) +{ + device_remove_file(dev, attr); + return 0; +} + +static void w83795_check_dynamic_in_limits(struct i2c_client *client) +{ + struct w83795_data *data = i2c_get_clientdata(client); + u8 vid_ctl; + int i, err_max, err_min; + + vid_ctl = w83795_read(client, W83795_REG_VID_CTRL); + + /* Return immediately if VRM isn't configured */ + if ((vid_ctl & 0x07) == 0x00 || (vid_ctl & 0x07) == 0x07) + return; + + data->has_dyn_in = (vid_ctl >> 3) & 0x07; + for (i = 0; i < 2; i++) { + if (!(data->has_dyn_in & (1 << i))) + continue; + + /* Voltage limits in dynamic mode, switch to read-only */ + err_max = sysfs_chmod_file(&client->dev.kobj, + &w83795_in[i][2].dev_attr.attr, + S_IRUGO); + err_min = sysfs_chmod_file(&client->dev.kobj, + &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); + else + dev_info(&client->dev, "in%d limits set dynamically " + "from VID\n", i); + } +} + +/* Check pins that can be used for either temperature or voltage monitoring */ +static void w83795_apply_temp_config(struct w83795_data *data, u8 config, + int temp_chan, int in_chan) +{ + /* config is a 2-bit value */ + switch (config) { + case 0x2: /* Voltage monitoring */ + data->has_in |= 1 << in_chan; + break; + case 0x1: /* Thermal diode */ + if (temp_chan >= 4) + break; + data->temp_mode |= 1 << temp_chan; + /* fall through */ + case 0x3: /* Thermistor */ + data->has_temp |= 1 << temp_chan; + break; + } +} + +static int w83795_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int i; + u8 tmp; + struct device *dev = &client->dev; + struct w83795_data *data; + int err; + + data = kzalloc(sizeof(struct w83795_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->chip_type = id->driver_data; + data->bank = i2c_smbus_read_byte_data(client, W83795_REG_BANKSEL); + mutex_init(&data->update_lock); + + /* Initialize the chip */ + w83795_init_client(client); + + /* Check which voltages and fans are present */ + data->has_in = w83795_read(client, W83795_REG_VOLT_CTRL1) + | (w83795_read(client, W83795_REG_VOLT_CTRL2) << 8); + data->has_fan = w83795_read(client, W83795_REG_FANIN_CTRL1) + | (w83795_read(client, W83795_REG_FANIN_CTRL2) << 8); + + /* Check which analog temperatures and extra voltages are present */ + tmp = w83795_read(client, W83795_REG_TEMP_CTRL1); + if (tmp & 0x20) + data->enable_dts = 1; + w83795_apply_temp_config(data, (tmp >> 2) & 0x3, 5, 16); + w83795_apply_temp_config(data, tmp & 0x3, 4, 15); + tmp = w83795_read(client, W83795_REG_TEMP_CTRL2); + w83795_apply_temp_config(data, tmp >> 6, 3, 20); + w83795_apply_temp_config(data, (tmp >> 4) & 0x3, 2, 19); + w83795_apply_temp_config(data, (tmp >> 2) & 0x3, 1, 18); + w83795_apply_temp_config(data, tmp & 0x3, 0, 17); + + /* Check DTS enable status */ + if (data->enable_dts) { + if (1 & w83795_read(client, W83795_REG_DTSC)) + data->enable_dts |= 2; + data->has_dts = w83795_read(client, W83795_REG_DTSE); + } + + /* Report PECI Tbase values */ + if (data->enable_dts == 1) { + for (i = 0; i < 8; i++) { + if (!(data->has_dts & (1 << i))) + continue; + tmp = w83795_read(client, W83795_REG_PECI_TBASE(i)); + dev_info(&client->dev, + "PECI agent %d Tbase temperature: %u\n", + i + 1, (unsigned int)tmp & 0x7f); + } + } + + data->has_gain = w83795_read(client, W83795_REG_VMIGB_CTRL) & 0x0f; + + /* pwm and smart fan */ + if (data->chip_type == w83795g) + data->has_pwm = 8; + else + data->has_pwm = 2; + + err = w83795_handle_files(dev, device_create_file); + if (err) + goto exit_remove; + + if (data->chip_type == w83795g) + w83795_check_dynamic_in_limits(client); + + 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: + w83795_handle_files(dev, device_remove_file_wrapper); + kfree(data); +exit: + return err; +} + +static int w83795_remove(struct i2c_client *client) +{ + struct w83795_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + w83795_handle_files(&client->dev, device_remove_file_wrapper); + kfree(data); + + return 0; +} + + +static const struct i2c_device_id w83795_id[] = { + { "w83795g", w83795g }, + { "w83795adg", w83795adg }, + { } +}; +MODULE_DEVICE_TABLE(i2c, w83795_id); + +static struct i2c_driver w83795_driver = { + .driver = { + .name = "w83795", + }, + .probe = w83795_probe, + .remove = w83795_remove, + .id_table = w83795_id, + + .class = I2C_CLASS_HWMON, + .detect = w83795_detect, + .address_list = normal_i2c, +}; + +static int __init sensors_w83795_init(void) +{ + return i2c_add_driver(&w83795_driver); +} + +static void __exit sensors_w83795_exit(void) +{ + i2c_del_driver(&w83795_driver); +} + +MODULE_AUTHOR("Wei Song, Jean Delvare <khali@linux-fr.org>"); +MODULE_DESCRIPTION("W83795G/ADG hardware monitoring driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_w83795_init); +module_exit(sensors_w83795_exit); |