diff options
Diffstat (limited to 'drivers/hwmon')
38 files changed, 4566 insertions, 2267 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 97499d00615..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 @@ -399,6 +371,15 @@ config SENSORS_GL520SM This driver can also be built as a module. If so, the module will be called gl520sm. +config SENSORS_GPIO_FAN + tristate "GPIO fan" + depends on GENERIC_GPIO + help + If you say yes here you get support for fans connected to GPIO lines. + + This driver can also be built as a module. If so, the module + will be called gpio-fan. + config SENSORS_CORETEMP tristate "Intel Core/Core2/Atom temperature sensor" depends on X86 && PCI && EXPERIMENTAL @@ -447,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. @@ -490,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 @@ -558,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. @@ -578,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. @@ -605,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. @@ -654,6 +636,17 @@ config SENSORS_LTC4245 This driver can also be built as a module. If so, the module will be called ltc4245. +config SENSORS_LTC4261 + tristate "Linear Technology LTC4261" + depends on I2C && EXPERIMENTAL + default n + help + If you say yes here you get support for Linear Technology LTC4261 + Negative Voltage Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4261. + config SENSORS_LM95241 tristate "National Semiconductor LM95241 sensor chip" depends on I2C @@ -706,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. @@ -743,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" @@ -834,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 @@ -890,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. @@ -948,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 @@ -992,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. @@ -1011,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 @@ -1088,26 +1106,6 @@ config SENSORS_ULTRA45 This driver provides support for the Ultra45 workstation environmental sensors. -config SENSORS_HDAPS - tristate "IBM Hard Drive Active Protection System (hdaps)" - depends on INPUT && X86 - select INPUT_POLLDEV - default n - help - This driver provides support for the IBM Hard Drive Active Protection - System (hdaps), which provides an accelerometer and other misc. data. - ThinkPads starting with the R50, T41, and X40 are supported. The - accelerometer data is readable via sysfs. - - This driver also provides an absolute input class device, allowing - the laptop to act as a pinball machine-esque joystick. - - If your ThinkPad is not recognized by the driver, please update to latest - BIOS. This is especially the case for some R52 ThinkPads. - - Say Y here if you have an applicable laptop and want to experience - the awesome power of hdaps. - config SENSORS_LIS3_SPI tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (SPI)" depends on !ACPI && SPI_MASTER && INPUT diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index e3c2484f6c5..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 @@ -51,8 +51,8 @@ obj-$(CONFIG_SENSORS_FSCHMD) += fschmd.o obj-$(CONFIG_SENSORS_G760A) += g760a.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o +obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o -obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o @@ -80,6 +80,7 @@ obj-$(CONFIG_SENSORS_LM93) += lm93.o obj-$(CONFIG_SENSORS_LM95241) += lm95241.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o +obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o diff --git a/drivers/hwmon/adm1025.c b/drivers/hwmon/adm1025.c index 251b63165e2..60befc0ee65 100644 --- a/drivers/hwmon/adm1025.c +++ b/drivers/hwmon/adm1025.c @@ -12,7 +12,7 @@ * resolution of about 0.5% of the nominal value). Temperature values are * reported with a 1 deg resolution and a 3 deg accuracy. Complete * datasheet can be obtained from Analog's website at: - * http://www.analog.com/Analog_Root/productPage/productHome/0,2121,ADM1025,00.html + * http://www.onsemi.com/PowerSolutions/product.do?id=ADM1025 * * This driver also supports the ADM1025A, which differs from the ADM1025 * only in that it has "open-drain VID inputs while the ADM1025 has diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index 65335b268fa..4bf969c0a32 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -6,7 +6,7 @@ Chip details at: - <http://www.analog.com/UploadedFiles/Data_Sheets/779263102ADM1026_a.pdf> + <http://www.onsemi.com/PowerSolutions/product.do?id=ADM1026> 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 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/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index 653db1bda93..23b8555215d 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -762,6 +762,7 @@ static const struct file_operations atk_debugfs_ggrp_fops = { .read = atk_debugfs_ggrp_read, .open = atk_debugfs_ggrp_open, .release = atk_debugfs_ggrp_release, + .llseek = no_llseek, }; static void atk_debugfs_init(struct atk_data *data) diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index a23b17a78ac..42de98d73ff 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -21,7 +21,6 @@ */ #include <linux/module.h> -#include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/jiffies.h> @@ -280,11 +279,9 @@ static int __devinit get_tjmax(struct cpuinfo_x86 *c, u32 id, case 0x1a: dev_warn(dev, "TjMax is assumed as 100 C!\n"); return 100000; - break; case 0x17: case 0x1c: /* Atom CPUs */ return adjust_tjmax(c, id, dev); - break; default: dev_warn(dev, "CPU (model=0x%x) is not supported yet," " using default TjMax of 100C.\n", c->x86_model); @@ -292,6 +289,15 @@ static int __devinit get_tjmax(struct cpuinfo_x86 *c, u32 id, } } +static void __devinit get_ucode_rev_on_cpu(void *edx) +{ + u32 eax; + + wrmsr(MSR_IA32_UCODE_REV, 0, 0); + sync_core(); + rdmsr(MSR_IA32_UCODE_REV, eax, *(u32 *)edx); +} + static int __devinit coretemp_probe(struct platform_device *pdev) { struct coretemp_data *data; @@ -327,8 +333,15 @@ static int __devinit coretemp_probe(struct platform_device *pdev) if ((c->x86_model == 0xe) && (c->x86_mask < 0xc)) { /* check for microcode update */ - rdmsr_on_cpu(data->id, MSR_IA32_UCODE_REV, &eax, &edx); - if (edx < 0x39) { + err = smp_call_function_single(data->id, get_ucode_rev_on_cpu, + &edx, 1); + if (err) { + dev_err(&pdev->dev, + "Cannot determine microcode revision of " + "CPU#%u (%d)!\n", data->id, err); + err = -ENODEV; + goto exit_free; + } else if (edx < 0x39) { err = -ENODEV; dev_err(&pdev->dev, "Errata AE18 not fixed, update BIOS or " @@ -490,7 +503,7 @@ exit: return err; } -static void coretemp_device_remove(unsigned int cpu) +static void __cpuinit coretemp_device_remove(unsigned int cpu) { struct pdev_entry *p; unsigned int i; @@ -569,9 +582,8 @@ exit: static void __exit coretemp_exit(void) { struct pdev_entry *p, *n; -#ifdef CONFIG_HOTPLUG_CPU + unregister_hotcpu_notifier(&coretemp_cpu_notifier); -#endif mutex_lock(&pdev_list_mutex); list_for_each_entry_safe(p, n, &pdev_list, list) { platform_device_unregister(p->pdev); diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index 9638d58f99f..95cbfb3a707 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -6,10 +6,10 @@ * Datasheets available at: * * f75375: - * http://www.fintek.com.tw/files/productfiles/2005111152950.pdf + * http://www.fintek.com.tw/files/productfiles/F75375_V026P.pdf * * f75373: - * http://www.fintek.com.tw/files/productfiles/2005111153128.pdf + * http://www.fintek.com.tw/files/productfiles/F75373_V025P.pdf * * 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 diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index b7ca2a9676c..d4d4ca65d37 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -38,7 +38,6 @@ #include <linux/i2c.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> -#include <linux/smp_lock.h> #include <linux/err.h> #include <linux/mutex.h> #include <linux/sysfs.h> @@ -50,6 +49,7 @@ #include <linux/kref.h> /* Addresses to scan */ +static DEFINE_MUTEX(watchdog_mutex); static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; /* Insmod parameters */ @@ -858,7 +858,7 @@ static long watchdog_ioctl(struct file *filp, unsigned int cmd, unsigned long ar int i, ret = 0; struct fschmd_data *data = filp->private_data; - lock_kernel(); + mutex_lock(&watchdog_mutex); switch (cmd) { case WDIOC_GETSUPPORT: ident.firmware_version = data->revision; @@ -915,7 +915,7 @@ static long watchdog_ioctl(struct file *filp, unsigned int cmd, unsigned long ar default: ret = -ENOTTY; } - unlock_kernel(); + mutex_unlock(&watchdog_mutex); return ret; } diff --git a/drivers/hwmon/g760a.c b/drivers/hwmon/g760a.c index 1f63d1a3af5..1d6a6fa31fb 100644 --- a/drivers/hwmon/g760a.c +++ b/drivers/hwmon/g760a.c @@ -5,7 +5,7 @@ Copyright (C) 2007 Herbert Valerio Riedel <hvr@gnu.org> Complete datasheet is available at GMT's website: - http://www.gmt.com.tw/datasheet/g760a.pdf + http://www.gmt.com.tw/product/datasheet/EDS-760A.pdf 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 diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c new file mode 100644 index 00000000000..aa701a18370 --- /dev/null +++ b/drivers/hwmon/gpio-fan.c @@ -0,0 +1,558 @@ +/* + * gpio-fan.c - Hwmon driver for fans connected to GPIO lines. + * + * Copyright (C) 2010 LaCie + * + * Author: Simon Guinot <sguinot@lacie.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/hwmon.h> +#include <linux/gpio.h> +#include <linux/gpio-fan.h> + +struct gpio_fan_data { + struct platform_device *pdev; + struct device *hwmon_dev; + struct mutex lock; /* lock GPIOs operations. */ + int num_ctrl; + unsigned *ctrl; + int num_speed; + struct gpio_fan_speed *speed; + int speed_index; +#ifdef CONFIG_PM + int resume_speed; +#endif + bool pwm_enable; + struct gpio_fan_alarm *alarm; + struct work_struct alarm_work; +}; + +/* + * Alarm GPIO. + */ + +static void fan_alarm_notify(struct work_struct *ws) +{ + struct gpio_fan_data *fan_data = + container_of(ws, struct gpio_fan_data, alarm_work); + + sysfs_notify(&fan_data->pdev->dev.kobj, NULL, "fan1_alarm"); + kobject_uevent(&fan_data->pdev->dev.kobj, KOBJ_CHANGE); +} + +static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id) +{ + struct gpio_fan_data *fan_data = dev_id; + + schedule_work(&fan_data->alarm_work); + + return IRQ_NONE; +} + +static ssize_t show_fan_alarm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + struct gpio_fan_alarm *alarm = fan_data->alarm; + int value = gpio_get_value(alarm->gpio); + + if (alarm->active_low) + value = !value; + + return sprintf(buf, "%d\n", value); +} + +static DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL); + +static int fan_alarm_init(struct gpio_fan_data *fan_data, + struct gpio_fan_alarm *alarm) +{ + int err; + int alarm_irq; + struct platform_device *pdev = fan_data->pdev; + + fan_data->alarm = alarm; + + err = gpio_request(alarm->gpio, "GPIO fan alarm"); + if (err) + return err; + + err = gpio_direction_input(alarm->gpio); + if (err) + goto err_free_gpio; + + err = device_create_file(&pdev->dev, &dev_attr_fan1_alarm); + if (err) + goto err_free_gpio; + + /* + * If the alarm GPIO don't support interrupts, just leave + * without initializing the fail notification support. + */ + alarm_irq = gpio_to_irq(alarm->gpio); + if (alarm_irq < 0) + return 0; + + INIT_WORK(&fan_data->alarm_work, fan_alarm_notify); + set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH); + err = request_irq(alarm_irq, fan_alarm_irq_handler, IRQF_SHARED, + "GPIO fan alarm", fan_data); + if (err) + goto err_free_sysfs; + + return 0; + +err_free_sysfs: + device_remove_file(&pdev->dev, &dev_attr_fan1_alarm); +err_free_gpio: + gpio_free(alarm->gpio); + + return err; +} + +static void fan_alarm_free(struct gpio_fan_data *fan_data) +{ + struct platform_device *pdev = fan_data->pdev; + int alarm_irq = gpio_to_irq(fan_data->alarm->gpio); + + if (alarm_irq >= 0) + free_irq(alarm_irq, fan_data); + device_remove_file(&pdev->dev, &dev_attr_fan1_alarm); + gpio_free(fan_data->alarm->gpio); +} + +/* + * Control GPIOs. + */ + +/* Must be called with fan_data->lock held, except during initialization. */ +static void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val) +{ + int i; + + for (i = 0; i < fan_data->num_ctrl; i++) + gpio_set_value(fan_data->ctrl[i], (ctrl_val >> i) & 1); +} + +static int __get_fan_ctrl(struct gpio_fan_data *fan_data) +{ + int i; + int ctrl_val = 0; + + for (i = 0; i < fan_data->num_ctrl; i++) { + int value; + + value = gpio_get_value(fan_data->ctrl[i]); + ctrl_val |= (value << i); + } + return ctrl_val; +} + +/* Must be called with fan_data->lock held, except during initialization. */ +static void set_fan_speed(struct gpio_fan_data *fan_data, int speed_index) +{ + if (fan_data->speed_index == speed_index) + return; + + __set_fan_ctrl(fan_data, fan_data->speed[speed_index].ctrl_val); + fan_data->speed_index = speed_index; +} + +static int get_fan_speed_index(struct gpio_fan_data *fan_data) +{ + int ctrl_val = __get_fan_ctrl(fan_data); + int i; + + for (i = 0; i < fan_data->num_speed; i++) + if (fan_data->speed[i].ctrl_val == ctrl_val) + return i; + + dev_warn(&fan_data->pdev->dev, + "missing speed array entry for GPIO value 0x%x\n", ctrl_val); + + return -EINVAL; +} + +static int rpm_to_speed_index(struct gpio_fan_data *fan_data, int rpm) +{ + struct gpio_fan_speed *speed = fan_data->speed; + int i; + + for (i = 0; i < fan_data->num_speed; i++) + if (speed[i].rpm >= rpm) + return i; + + return fan_data->num_speed - 1; +} + +static ssize_t show_pwm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + u8 pwm = fan_data->speed_index * 255 / (fan_data->num_speed - 1); + + return sprintf(buf, "%d\n", pwm); +} + +static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + unsigned long pwm; + int speed_index; + int ret = count; + + if (strict_strtoul(buf, 10, &pwm) || pwm > 255) + return -EINVAL; + + mutex_lock(&fan_data->lock); + + if (!fan_data->pwm_enable) { + ret = -EPERM; + goto exit_unlock; + } + + speed_index = DIV_ROUND_UP(pwm * (fan_data->num_speed - 1), 255); + set_fan_speed(fan_data, speed_index); + +exit_unlock: + mutex_unlock(&fan_data->lock); + + return ret; +} + +static ssize_t show_pwm_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", fan_data->pwm_enable); +} + +static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + unsigned long val; + + if (strict_strtoul(buf, 10, &val) || val > 1) + return -EINVAL; + + if (fan_data->pwm_enable == val) + return count; + + mutex_lock(&fan_data->lock); + + fan_data->pwm_enable = val; + + /* Disable manual control mode: set fan at full speed. */ + if (val == 0) + set_fan_speed(fan_data, fan_data->num_speed - 1); + + mutex_unlock(&fan_data->lock); + + return count; +} + +static ssize_t show_pwm_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "0\n"); +} + +static ssize_t show_rpm_min(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", fan_data->speed[0].rpm); +} + +static ssize_t show_rpm_max(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", + fan_data->speed[fan_data->num_speed - 1].rpm); +} + +static ssize_t show_rpm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", fan_data->speed[fan_data->speed_index].rpm); +} + +static ssize_t set_rpm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + unsigned long rpm; + int ret = count; + + if (strict_strtoul(buf, 10, &rpm)) + return -EINVAL; + + mutex_lock(&fan_data->lock); + + if (!fan_data->pwm_enable) { + ret = -EPERM; + goto exit_unlock; + } + + set_fan_speed(fan_data, rpm_to_speed_index(fan_data, rpm)); + +exit_unlock: + mutex_unlock(&fan_data->lock); + + return ret; +} + +static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm); +static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, + show_pwm_enable, set_pwm_enable); +static DEVICE_ATTR(pwm1_mode, S_IRUGO, show_pwm_mode, NULL); +static DEVICE_ATTR(fan1_min, S_IRUGO, show_rpm_min, NULL); +static DEVICE_ATTR(fan1_max, S_IRUGO, show_rpm_max, NULL); +static DEVICE_ATTR(fan1_input, S_IRUGO, show_rpm, NULL); +static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, show_rpm, set_rpm); + +static struct attribute *gpio_fan_ctrl_attributes[] = { + &dev_attr_pwm1.attr, + &dev_attr_pwm1_enable.attr, + &dev_attr_pwm1_mode.attr, + &dev_attr_fan1_input.attr, + &dev_attr_fan1_target.attr, + &dev_attr_fan1_min.attr, + &dev_attr_fan1_max.attr, + NULL +}; + +static const struct attribute_group gpio_fan_ctrl_group = { + .attrs = gpio_fan_ctrl_attributes, +}; + +static int fan_ctrl_init(struct gpio_fan_data *fan_data, + struct gpio_fan_platform_data *pdata) +{ + struct platform_device *pdev = fan_data->pdev; + int num_ctrl = pdata->num_ctrl; + unsigned *ctrl = pdata->ctrl; + int i, err; + + for (i = 0; i < num_ctrl; i++) { + err = gpio_request(ctrl[i], "GPIO fan control"); + if (err) + goto err_free_gpio; + + err = gpio_direction_output(ctrl[i], gpio_get_value(ctrl[i])); + if (err) { + gpio_free(ctrl[i]); + goto err_free_gpio; + } + } + + err = sysfs_create_group(&pdev->dev.kobj, &gpio_fan_ctrl_group); + if (err) + goto err_free_gpio; + + fan_data->num_ctrl = num_ctrl; + fan_data->ctrl = ctrl; + fan_data->num_speed = pdata->num_speed; + fan_data->speed = pdata->speed; + fan_data->pwm_enable = true; /* Enable manual fan speed control. */ + fan_data->speed_index = get_fan_speed_index(fan_data); + if (fan_data->speed_index < 0) { + err = -ENODEV; + goto err_free_gpio; + } + + return 0; + +err_free_gpio: + for (i = i - 1; i >= 0; i--) + gpio_free(ctrl[i]); + + return err; +} + +static void fan_ctrl_free(struct gpio_fan_data *fan_data) +{ + struct platform_device *pdev = fan_data->pdev; + int i; + + sysfs_remove_group(&pdev->dev.kobj, &gpio_fan_ctrl_group); + for (i = 0; i < fan_data->num_ctrl; i++) + gpio_free(fan_data->ctrl[i]); +} + +/* + * Platform driver. + */ + +static ssize_t show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "gpio-fan\n"); +} + +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static int __devinit gpio_fan_probe(struct platform_device *pdev) +{ + int err; + struct gpio_fan_data *fan_data; + struct gpio_fan_platform_data *pdata = pdev->dev.platform_data; + + if (!pdata) + return -EINVAL; + + fan_data = kzalloc(sizeof(struct gpio_fan_data), GFP_KERNEL); + if (!fan_data) + return -ENOMEM; + + fan_data->pdev = pdev; + platform_set_drvdata(pdev, fan_data); + mutex_init(&fan_data->lock); + + /* Configure alarm GPIO if available. */ + if (pdata->alarm) { + err = fan_alarm_init(fan_data, pdata->alarm); + if (err) + goto err_free_data; + } + + /* Configure control GPIOs if available. */ + if (pdata->ctrl && pdata->num_ctrl > 0) { + if (!pdata->speed || pdata->num_speed <= 1) { + err = -EINVAL; + goto err_free_alarm; + } + err = fan_ctrl_init(fan_data, pdata); + if (err) + goto err_free_alarm; + } + + err = device_create_file(&pdev->dev, &dev_attr_name); + if (err) + goto err_free_ctrl; + + /* Make this driver part of hwmon class. */ + fan_data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(fan_data->hwmon_dev)) { + err = PTR_ERR(fan_data->hwmon_dev); + goto err_remove_name; + } + + dev_info(&pdev->dev, "GPIO fan initialized\n"); + + return 0; + +err_remove_name: + device_remove_file(&pdev->dev, &dev_attr_name); +err_free_ctrl: + if (fan_data->ctrl) + fan_ctrl_free(fan_data); +err_free_alarm: + if (fan_data->alarm) + fan_alarm_free(fan_data); +err_free_data: + platform_set_drvdata(pdev, NULL); + kfree(fan_data); + + return err; +} + +static int __devexit gpio_fan_remove(struct platform_device *pdev) +{ + struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); + + hwmon_device_unregister(fan_data->hwmon_dev); + device_remove_file(&pdev->dev, &dev_attr_name); + if (fan_data->alarm) + fan_alarm_free(fan_data); + if (fan_data->ctrl) + fan_ctrl_free(fan_data); + kfree(fan_data); + + return 0; +} + +#ifdef CONFIG_PM +static int gpio_fan_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); + + if (fan_data->ctrl) { + fan_data->resume_speed = fan_data->speed_index; + set_fan_speed(fan_data, 0); + } + + return 0; +} + +static int gpio_fan_resume(struct platform_device *pdev) +{ + struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); + + if (fan_data->ctrl) + set_fan_speed(fan_data, fan_data->resume_speed); + + return 0; +} +#else +#define gpio_fan_suspend NULL +#define gpio_fan_resume NULL +#endif + +static struct platform_driver gpio_fan_driver = { + .probe = gpio_fan_probe, + .remove = __devexit_p(gpio_fan_remove), + .suspend = gpio_fan_suspend, + .resume = gpio_fan_resume, + .driver = { + .name = "gpio-fan", + }, +}; + +static int __init gpio_fan_init(void) +{ + return platform_driver_register(&gpio_fan_driver); +} + +static void __exit gpio_fan_exit(void) +{ + platform_driver_unregister(&gpio_fan_driver); +} + +module_init(gpio_fan_init); +module_exit(gpio_fan_exit); + +MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>"); +MODULE_DESCRIPTION("GPIO FAN driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:gpio-fan"); diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c deleted file mode 100644 index bfd42f18924..00000000000 --- a/drivers/hwmon/hdaps.c +++ /dev/null @@ -1,637 +0,0 @@ -/* - * drivers/hwmon/hdaps.c - driver for IBM's Hard Drive Active Protection System - * - * Copyright (C) 2005 Robert Love <rml@novell.com> - * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com> - * - * The HardDisk Active Protection System (hdaps) is present in IBM ThinkPads - * starting with the R40, T41, and X40. It provides a basic two-axis - * accelerometer and other data, such as the device's temperature. - * - * This driver is based on the document by Mark A. Smith available at - * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html and a lot of trial - * and error. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License v2 as published by the - * Free Software Foundation. - * - * 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/delay.h> -#include <linux/platform_device.h> -#include <linux/input-polldev.h> -#include <linux/kernel.h> -#include <linux/mutex.h> -#include <linux/module.h> -#include <linux/timer.h> -#include <linux/dmi.h> -#include <linux/jiffies.h> -#include <linux/io.h> - -#define HDAPS_LOW_PORT 0x1600 /* first port used by hdaps */ -#define HDAPS_NR_PORTS 0x30 /* number of ports: 0x1600 - 0x162f */ - -#define HDAPS_PORT_STATE 0x1611 /* device state */ -#define HDAPS_PORT_YPOS 0x1612 /* y-axis position */ -#define HDAPS_PORT_XPOS 0x1614 /* x-axis position */ -#define HDAPS_PORT_TEMP1 0x1616 /* device temperature, in Celsius */ -#define HDAPS_PORT_YVAR 0x1617 /* y-axis variance (what is this?) */ -#define HDAPS_PORT_XVAR 0x1619 /* x-axis variance (what is this?) */ -#define HDAPS_PORT_TEMP2 0x161b /* device temperature (again?) */ -#define HDAPS_PORT_UNKNOWN 0x161c /* what is this? */ -#define HDAPS_PORT_KMACT 0x161d /* keyboard or mouse activity */ - -#define STATE_FRESH 0x50 /* accelerometer data is fresh */ - -#define KEYBD_MASK 0x20 /* set if keyboard activity */ -#define MOUSE_MASK 0x40 /* set if mouse activity */ -#define KEYBD_ISSET(n) (!! (n & KEYBD_MASK)) /* keyboard used? */ -#define MOUSE_ISSET(n) (!! (n & MOUSE_MASK)) /* mouse used? */ - -#define INIT_TIMEOUT_MSECS 4000 /* wait up to 4s for device init ... */ -#define INIT_WAIT_MSECS 200 /* ... in 200ms increments */ - -#define HDAPS_POLL_INTERVAL 50 /* poll for input every 1/20s (50 ms)*/ -#define HDAPS_INPUT_FUZZ 4 /* input event threshold */ -#define HDAPS_INPUT_FLAT 4 - -#define HDAPS_X_AXIS (1 << 0) -#define HDAPS_Y_AXIS (1 << 1) -#define HDAPS_BOTH_AXES (HDAPS_X_AXIS | HDAPS_Y_AXIS) - -static struct platform_device *pdev; -static struct input_polled_dev *hdaps_idev; -static unsigned int hdaps_invert; -static u8 km_activity; -static int rest_x; -static int rest_y; - -static DEFINE_MUTEX(hdaps_mtx); - -/* - * __get_latch - Get the value from a given port. Callers must hold hdaps_mtx. - */ -static inline u8 __get_latch(u16 port) -{ - return inb(port) & 0xff; -} - -/* - * __check_latch - Check a port latch for a given value. Returns zero if the - * port contains the given value. Callers must hold hdaps_mtx. - */ -static inline int __check_latch(u16 port, u8 val) -{ - if (__get_latch(port) == val) - return 0; - return -EINVAL; -} - -/* - * __wait_latch - Wait up to 100us for a port latch to get a certain value, - * returning zero if the value is obtained. Callers must hold hdaps_mtx. - */ -static int __wait_latch(u16 port, u8 val) -{ - unsigned int i; - - for (i = 0; i < 20; i++) { - if (!__check_latch(port, val)) - return 0; - udelay(5); - } - - return -EIO; -} - -/* - * __device_refresh - request a refresh from the accelerometer. Does not wait - * for refresh to complete. Callers must hold hdaps_mtx. - */ -static void __device_refresh(void) -{ - udelay(200); - if (inb(0x1604) != STATE_FRESH) { - outb(0x11, 0x1610); - outb(0x01, 0x161f); - } -} - -/* - * __device_refresh_sync - request a synchronous refresh from the - * accelerometer. We wait for the refresh to complete. Returns zero if - * successful and nonzero on error. Callers must hold hdaps_mtx. - */ -static int __device_refresh_sync(void) -{ - __device_refresh(); - return __wait_latch(0x1604, STATE_FRESH); -} - -/* - * __device_complete - indicate to the accelerometer that we are done reading - * data, and then initiate an async refresh. Callers must hold hdaps_mtx. - */ -static inline void __device_complete(void) -{ - inb(0x161f); - inb(0x1604); - __device_refresh(); -} - -/* - * hdaps_readb_one - reads a byte from a single I/O port, placing the value in - * the given pointer. Returns zero on success or a negative error on failure. - * Can sleep. - */ -static int hdaps_readb_one(unsigned int port, u8 *val) -{ - int ret; - - mutex_lock(&hdaps_mtx); - - /* do a sync refresh -- we need to be sure that we read fresh data */ - ret = __device_refresh_sync(); - if (ret) - goto out; - - *val = inb(port); - __device_complete(); - -out: - mutex_unlock(&hdaps_mtx); - return ret; -} - -/* __hdaps_read_pair - internal lockless helper for hdaps_read_pair(). */ -static int __hdaps_read_pair(unsigned int port1, unsigned int port2, - int *x, int *y) -{ - /* do a sync refresh -- we need to be sure that we read fresh data */ - if (__device_refresh_sync()) - return -EIO; - - *y = inw(port2); - *x = inw(port1); - km_activity = inb(HDAPS_PORT_KMACT); - __device_complete(); - - /* hdaps_invert is a bitvector to negate the axes */ - if (hdaps_invert & HDAPS_X_AXIS) - *x = -*x; - if (hdaps_invert & HDAPS_Y_AXIS) - *y = -*y; - - return 0; -} - -/* - * hdaps_read_pair - reads the values from a pair of ports, placing the values - * in the given pointers. Returns zero on success. Can sleep. - */ -static int hdaps_read_pair(unsigned int port1, unsigned int port2, - int *val1, int *val2) -{ - int ret; - - mutex_lock(&hdaps_mtx); - ret = __hdaps_read_pair(port1, port2, val1, val2); - mutex_unlock(&hdaps_mtx); - - return ret; -} - -/* - * hdaps_device_init - initialize the accelerometer. Returns zero on success - * and negative error code on failure. Can sleep. - */ -static int hdaps_device_init(void) -{ - int total, ret = -ENXIO; - - mutex_lock(&hdaps_mtx); - - outb(0x13, 0x1610); - outb(0x01, 0x161f); - if (__wait_latch(0x161f, 0x00)) - goto out; - - /* - * Most ThinkPads return 0x01. - * - * Others--namely the R50p, T41p, and T42p--return 0x03. These laptops - * have "inverted" axises. - * - * The 0x02 value occurs when the chip has been previously initialized. - */ - if (__check_latch(0x1611, 0x03) && - __check_latch(0x1611, 0x02) && - __check_latch(0x1611, 0x01)) - goto out; - - printk(KERN_DEBUG "hdaps: initial latch check good (0x%02x).\n", - __get_latch(0x1611)); - - outb(0x17, 0x1610); - outb(0x81, 0x1611); - outb(0x01, 0x161f); - if (__wait_latch(0x161f, 0x00)) - goto out; - if (__wait_latch(0x1611, 0x00)) - goto out; - if (__wait_latch(0x1612, 0x60)) - goto out; - if (__wait_latch(0x1613, 0x00)) - goto out; - outb(0x14, 0x1610); - outb(0x01, 0x1611); - outb(0x01, 0x161f); - if (__wait_latch(0x161f, 0x00)) - goto out; - outb(0x10, 0x1610); - outb(0xc8, 0x1611); - outb(0x00, 0x1612); - outb(0x02, 0x1613); - outb(0x01, 0x161f); - if (__wait_latch(0x161f, 0x00)) - goto out; - if (__device_refresh_sync()) - goto out; - if (__wait_latch(0x1611, 0x00)) - goto out; - - /* we have done our dance, now let's wait for the applause */ - for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) { - int x, y; - - /* a read of the device helps push it into action */ - __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y); - if (!__wait_latch(0x1611, 0x02)) { - ret = 0; - break; - } - - msleep(INIT_WAIT_MSECS); - } - -out: - mutex_unlock(&hdaps_mtx); - return ret; -} - - -/* Device model stuff */ - -static int hdaps_probe(struct platform_device *dev) -{ - int ret; - - ret = hdaps_device_init(); - if (ret) - return ret; - - printk(KERN_INFO "hdaps: device successfully initialized.\n"); - return 0; -} - -static int hdaps_resume(struct platform_device *dev) -{ - return hdaps_device_init(); -} - -static struct platform_driver hdaps_driver = { - .probe = hdaps_probe, - .resume = hdaps_resume, - .driver = { - .name = "hdaps", - .owner = THIS_MODULE, - }, -}; - -/* - * hdaps_calibrate - Set our "resting" values. Callers must hold hdaps_mtx. - */ -static void hdaps_calibrate(void) -{ - __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &rest_x, &rest_y); -} - -static void hdaps_mousedev_poll(struct input_polled_dev *dev) -{ - struct input_dev *input_dev = dev->input; - int x, y; - - mutex_lock(&hdaps_mtx); - - if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y)) - goto out; - - input_report_abs(input_dev, ABS_X, x - rest_x); - input_report_abs(input_dev, ABS_Y, y - rest_y); - input_sync(input_dev); - -out: - mutex_unlock(&hdaps_mtx); -} - - -/* Sysfs Files */ - -static ssize_t hdaps_position_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int ret, x, y; - - ret = hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y); - if (ret) - return ret; - - return sprintf(buf, "(%d,%d)\n", x, y); -} - -static ssize_t hdaps_variance_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int ret, x, y; - - ret = hdaps_read_pair(HDAPS_PORT_XVAR, HDAPS_PORT_YVAR, &x, &y); - if (ret) - return ret; - - return sprintf(buf, "(%d,%d)\n", x, y); -} - -static ssize_t hdaps_temp1_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 temp; - int ret; - - ret = hdaps_readb_one(HDAPS_PORT_TEMP1, &temp); - if (ret < 0) - return ret; - - return sprintf(buf, "%u\n", temp); -} - -static ssize_t hdaps_temp2_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 temp; - int ret; - - ret = hdaps_readb_one(HDAPS_PORT_TEMP2, &temp); - if (ret < 0) - return ret; - - return sprintf(buf, "%u\n", temp); -} - -static ssize_t hdaps_keyboard_activity_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%u\n", KEYBD_ISSET(km_activity)); -} - -static ssize_t hdaps_mouse_activity_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%u\n", MOUSE_ISSET(km_activity)); -} - -static ssize_t hdaps_calibrate_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "(%d,%d)\n", rest_x, rest_y); -} - -static ssize_t hdaps_calibrate_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - mutex_lock(&hdaps_mtx); - hdaps_calibrate(); - mutex_unlock(&hdaps_mtx); - - return count; -} - -static ssize_t hdaps_invert_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%u\n", hdaps_invert); -} - -static ssize_t hdaps_invert_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int invert; - - if (sscanf(buf, "%d", &invert) != 1 || - invert < 0 || invert > HDAPS_BOTH_AXES) - return -EINVAL; - - hdaps_invert = invert; - hdaps_calibrate(); - - return count; -} - -static DEVICE_ATTR(position, 0444, hdaps_position_show, NULL); -static DEVICE_ATTR(variance, 0444, hdaps_variance_show, NULL); -static DEVICE_ATTR(temp1, 0444, hdaps_temp1_show, NULL); -static DEVICE_ATTR(temp2, 0444, hdaps_temp2_show, NULL); -static DEVICE_ATTR(keyboard_activity, 0444, hdaps_keyboard_activity_show, NULL); -static DEVICE_ATTR(mouse_activity, 0444, hdaps_mouse_activity_show, NULL); -static DEVICE_ATTR(calibrate, 0644, hdaps_calibrate_show,hdaps_calibrate_store); -static DEVICE_ATTR(invert, 0644, hdaps_invert_show, hdaps_invert_store); - -static struct attribute *hdaps_attributes[] = { - &dev_attr_position.attr, - &dev_attr_variance.attr, - &dev_attr_temp1.attr, - &dev_attr_temp2.attr, - &dev_attr_keyboard_activity.attr, - &dev_attr_mouse_activity.attr, - &dev_attr_calibrate.attr, - &dev_attr_invert.attr, - NULL, -}; - -static struct attribute_group hdaps_attribute_group = { - .attrs = hdaps_attributes, -}; - - -/* Module stuff */ - -/* hdaps_dmi_match - found a match. return one, short-circuiting the hunt. */ -static int __init hdaps_dmi_match(const struct dmi_system_id *id) -{ - printk(KERN_INFO "hdaps: %s detected.\n", id->ident); - return 1; -} - -/* hdaps_dmi_match_invert - found an inverted match. */ -static int __init hdaps_dmi_match_invert(const struct dmi_system_id *id) -{ - hdaps_invert = (unsigned long)id->driver_data; - printk(KERN_INFO "hdaps: inverting axis (%u) readings.\n", - hdaps_invert); - return hdaps_dmi_match(id); -} - -#define HDAPS_DMI_MATCH_INVERT(vendor, model, axes) { \ - .ident = vendor " " model, \ - .callback = hdaps_dmi_match_invert, \ - .driver_data = (void *)axes, \ - .matches = { \ - DMI_MATCH(DMI_BOARD_VENDOR, vendor), \ - DMI_MATCH(DMI_PRODUCT_VERSION, model) \ - } \ -} - -#define HDAPS_DMI_MATCH_NORMAL(vendor, model) \ - HDAPS_DMI_MATCH_INVERT(vendor, model, 0) - -/* Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match - "ThinkPad T42p", so the order of the entries matters. - If your ThinkPad is not recognized, please update to latest - BIOS. This is especially the case for some R52 ThinkPads. */ -static struct dmi_system_id __initdata hdaps_whitelist[] = { - HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R50"), - HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"), - HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R52"), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61i", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T41"), - HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T42"), - HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T43"), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T400", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61p", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"), - HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X41", HDAPS_Y_AXIS), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61s", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad Z60m"), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61m", HDAPS_BOTH_AXES), - HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61p", HDAPS_BOTH_AXES), - { .ident = NULL } -}; - -static int __init hdaps_init(void) -{ - struct input_dev *idev; - int ret; - - if (!dmi_check_system(hdaps_whitelist)) { - printk(KERN_WARNING "hdaps: supported laptop not found!\n"); - ret = -ENODEV; - goto out; - } - - if (!request_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS, "hdaps")) { - ret = -ENXIO; - goto out; - } - - ret = platform_driver_register(&hdaps_driver); - if (ret) - goto out_region; - - pdev = platform_device_register_simple("hdaps", -1, NULL, 0); - if (IS_ERR(pdev)) { - ret = PTR_ERR(pdev); - goto out_driver; - } - - ret = sysfs_create_group(&pdev->dev.kobj, &hdaps_attribute_group); - if (ret) - goto out_device; - - hdaps_idev = input_allocate_polled_device(); - if (!hdaps_idev) { - ret = -ENOMEM; - goto out_group; - } - - hdaps_idev->poll = hdaps_mousedev_poll; - hdaps_idev->poll_interval = HDAPS_POLL_INTERVAL; - - /* initial calibrate for the input device */ - hdaps_calibrate(); - - /* initialize the input class */ - idev = hdaps_idev->input; - idev->name = "hdaps"; - idev->phys = "isa1600/input0"; - idev->id.bustype = BUS_ISA; - idev->dev.parent = &pdev->dev; - idev->evbit[0] = BIT_MASK(EV_ABS); - input_set_abs_params(idev, ABS_X, - -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT); - input_set_abs_params(idev, ABS_Y, - -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT); - - ret = input_register_polled_device(hdaps_idev); - if (ret) - goto out_idev; - - printk(KERN_INFO "hdaps: driver successfully loaded.\n"); - return 0; - -out_idev: - input_free_polled_device(hdaps_idev); -out_group: - sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); -out_device: - platform_device_unregister(pdev); -out_driver: - platform_driver_unregister(&hdaps_driver); -out_region: - release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS); -out: - printk(KERN_WARNING "hdaps: driver init failed (ret=%d)!\n", ret); - return ret; -} - -static void __exit hdaps_exit(void) -{ - input_unregister_polled_device(hdaps_idev); - input_free_polled_device(hdaps_idev); - sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); - platform_device_unregister(pdev); - platform_driver_unregister(&hdaps_driver); - release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS); - - printk(KERN_INFO "hdaps: driver unloaded.\n"); -} - -module_init(hdaps_init); -module_exit(hdaps_exit); - -module_param_named(invert, hdaps_invert, int, 0); -MODULE_PARM_DESC(invert, "invert data along each axis. 1 invert x-axis, " - "2 invert y-axis, 3 invert both axes."); - -MODULE_AUTHOR("Robert Love"); -MODULE_DESCRIPTION("IBM Hard Drive Active Protection System (HDAPS) driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c index 36e95753223..a56a78412fc 100644 --- a/drivers/hwmon/hp_accel.c +++ b/drivers/hwmon/hp_accel.c @@ -146,7 +146,7 @@ int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val) static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) { - lis3_dev.ac = *((struct axis_conversion *)dmi->driver_data); + lis3_dev.ac = *((union axis_conversion *)dmi->driver_data); printk(KERN_INFO DRIVER_NAME ": hardware type %s found.\n", dmi->ident); return 1; @@ -154,16 +154,19 @@ static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) /* Represents, for each axis seen by userspace, the corresponding hw axis (+1). * If the value is negative, the opposite of the hw value is used. */ -static struct axis_conversion lis3lv02d_axis_normal = {1, 2, 3}; -static struct axis_conversion lis3lv02d_axis_y_inverted = {1, -2, 3}; -static struct axis_conversion lis3lv02d_axis_x_inverted = {-1, 2, 3}; -static struct axis_conversion lis3lv02d_axis_z_inverted = {1, 2, -3}; -static struct axis_conversion lis3lv02d_axis_xy_swap = {2, 1, 3}; -static struct axis_conversion lis3lv02d_axis_xy_rotated_left = {-2, 1, 3}; -static struct axis_conversion lis3lv02d_axis_xy_rotated_left_usd = {-2, 1, -3}; -static struct axis_conversion lis3lv02d_axis_xy_swap_inverted = {-2, -1, 3}; -static struct axis_conversion lis3lv02d_axis_xy_rotated_right = {2, -1, 3}; -static struct axis_conversion lis3lv02d_axis_xy_swap_yz_inverted = {2, -1, -3}; +#define DEFINE_CONV(name, x, y, z) \ + static union axis_conversion lis3lv02d_axis_##name = \ + { .as_array = { x, y, z } } +DEFINE_CONV(normal, 1, 2, 3); +DEFINE_CONV(y_inverted, 1, -2, 3); +DEFINE_CONV(x_inverted, -1, 2, 3); +DEFINE_CONV(z_inverted, 1, 2, -3); +DEFINE_CONV(xy_swap, 2, 1, 3); +DEFINE_CONV(xy_rotated_left, -2, 1, 3); +DEFINE_CONV(xy_rotated_left_usd, -2, 1, -3); +DEFINE_CONV(xy_swap_inverted, -2, -1, 3); +DEFINE_CONV(xy_rotated_right, 2, -1, 3); +DEFINE_CONV(xy_swap_yz_inverted, 2, -1, -3); #define AXIS_DMI_MATCH(_ident, _name, _axis) { \ .ident = _ident, \ @@ -222,7 +225,7 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = { AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted), AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted), - AXIS_DMI_MATCH("Mini5102", "HP Mini 5102", xy_rotated_left_usd), + AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd), { NULL, } /* Laptop models without axis info (yet): * "NC6910" "HP Compaq 6910" @@ -299,7 +302,10 @@ static int lis3lv02d_add(struct acpi_device *device) lis3lv02d_enum_resources(device); /* If possible use a "standard" axes order */ - if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { + if (lis3_dev.ac.x && lis3_dev.ac.y && lis3_dev.ac.z) { + printk(KERN_INFO DRIVER_NAME ": Using custom axes %d,%d,%d\n", + lis3_dev.ac.x, lis3_dev.ac.y, lis3_dev.ac.z); + } else if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { printk(KERN_INFO DRIVER_NAME ": laptop model unknown, " "using default axes configuration\n"); lis3_dev.ac = lis3lv02d_axis_normal; diff --git a/drivers/hwmon/hwmon-vid.c b/drivers/hwmon/hwmon-vid.c index bf0862a803c..2b2ca1694f9 100644 --- a/drivers/hwmon/hwmon-vid.c +++ b/drivers/hwmon/hwmon-vid.c @@ -38,7 +38,7 @@ * available at http://developer.intel.com/. * * AMD Athlon 64 and AMD Opteron Processors, AMD Publication 26094, - * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/26094.PDF + * http://support.amd.com/us/Processor_TechDocs/26094.PDF * Table 74. VID Code Voltages * This corresponds to an arbitrary VRM code of 24 in the functions below. * These CPU models (K8 revision <= E) have 5 VID pins. See also: 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/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c index fc591ae5310..0cee73a6124 100644 --- a/drivers/hwmon/lis3lv02d.c +++ b/drivers/hwmon/lis3lv02d.c @@ -31,9 +31,11 @@ #include <linux/delay.h> #include <linux/wait.h> #include <linux/poll.h> +#include <linux/slab.h> #include <linux/freezer.h> #include <linux/uaccess.h> #include <linux/miscdevice.h> +#include <linux/pm_runtime.h> #include <asm/atomic.h> #include "lis3lv02d.h" @@ -43,6 +45,16 @@ #define MDPS_POLL_INTERVAL 50 #define MDPS_POLL_MIN 0 #define MDPS_POLL_MAX 2000 + +#define LIS3_SYSFS_POWERDOWN_DELAY 5000 /* In milliseconds */ + +#define SELFTEST_OK 0 +#define SELFTEST_FAIL -1 +#define SELFTEST_IRQ -2 + +#define IRQ_LINE0 0 +#define IRQ_LINE1 1 + /* * The sensor can also generate interrupts (DRDY) but it's pretty pointless * because they are generated even if the data do not change. So it's better @@ -66,8 +78,10 @@ #define LIS3_SENSITIVITY_12B ((LIS3_ACCURACY * 1000) / 1024) #define LIS3_SENSITIVITY_8B (18 * LIS3_ACCURACY) -#define LIS3_DEFAULT_FUZZ 3 -#define LIS3_DEFAULT_FLAT 3 +#define LIS3_DEFAULT_FUZZ_12B 3 +#define LIS3_DEFAULT_FLAT_12B 3 +#define LIS3_DEFAULT_FUZZ_8B 1 +#define LIS3_DEFAULT_FLAT_8B 1 struct lis3lv02d lis3_dev = { .misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lis3_dev.misc_wait), @@ -75,6 +89,30 @@ struct lis3lv02d lis3_dev = { EXPORT_SYMBOL_GPL(lis3_dev); +/* just like param_set_int() but does sanity-check so that it won't point + * over the axis array size + */ +static int param_set_axis(const char *val, const struct kernel_param *kp) +{ + int ret = param_set_int(val, kp); + if (!ret) { + int val = *(int *)kp->arg; + if (val < 0) + val = -val; + if (!val || val > 3) + return -EINVAL; + } + return ret; +} + +static struct kernel_param_ops param_ops_axis = { + .set = param_set_axis, + .get = param_get_int, +}; + +module_param_array_named(axes, lis3_dev.ac.as_array, axis, NULL, 0644); +MODULE_PARM_DESC(axes, "Axis-mapping for x,y,z directions"); + static s16 lis3lv02d_read_8(struct lis3lv02d *lis3, int reg) { s8 lo; @@ -123,9 +161,24 @@ static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z) int position[3]; int i; - position[0] = lis3->read_data(lis3, OUTX); - position[1] = lis3->read_data(lis3, OUTY); - position[2] = lis3->read_data(lis3, OUTZ); + if (lis3->blkread) { + if (lis3_dev.whoami == WAI_12B) { + u16 data[3]; + lis3->blkread(lis3, OUTX_L, 6, (u8 *)data); + for (i = 0; i < 3; i++) + position[i] = (s16)le16_to_cpu(data[i]); + } else { + u8 data[5]; + /* Data: x, dummy, y, dummy, z */ + lis3->blkread(lis3, OUTX, 5, data); + for (i = 0; i < 3; i++) + position[i] = (s8)data[i * 2]; + } + } else { + position[0] = lis3->read_data(lis3, OUTX); + position[1] = lis3->read_data(lis3, OUTY); + position[2] = lis3->read_data(lis3, OUTZ); + } for (i = 0; i < 3; i++) position[i] = (position[i] * lis3->scale) / LIS3_ACCURACY; @@ -138,6 +191,7 @@ static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z) /* conversion btw sampling rate and the register values */ static int lis3_12_rates[4] = {40, 160, 640, 2560}; static int lis3_8_rates[2] = {100, 400}; +static int lis3_3dc_rates[16] = {0, 1, 10, 25, 50, 100, 200, 400, 1600, 5000}; /* ODR is Output Data Rate */ static int lis3lv02d_get_odr(void) @@ -156,6 +210,9 @@ static int lis3lv02d_set_odr(int rate) u8 ctrl; int i, len, shift; + if (!rate) + return -EINVAL; + lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl); ctrl &= ~lis3_dev.odr_mask; len = 1 << hweight_long(lis3_dev.odr_mask); /* # of possible values */ @@ -172,19 +229,42 @@ static int lis3lv02d_set_odr(int rate) static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3]) { - u8 reg; + u8 ctlreg, reg; s16 x, y, z; u8 selftest; int ret; + u8 ctrl_reg_data; + unsigned char irq_cfg; mutex_lock(&lis3->mutex); - if (lis3_dev.whoami == WAI_12B) - selftest = CTRL1_ST; - else - selftest = CTRL1_STP; - lis3->read(lis3, CTRL_REG1, ®); - lis3->write(lis3, CTRL_REG1, (reg | selftest)); + irq_cfg = lis3->irq_cfg; + if (lis3_dev.whoami == WAI_8B) { + lis3->data_ready_count[IRQ_LINE0] = 0; + lis3->data_ready_count[IRQ_LINE1] = 0; + + /* Change interrupt cfg to data ready for selftest */ + atomic_inc(&lis3_dev.wake_thread); + lis3->irq_cfg = LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY; + lis3->read(lis3, CTRL_REG3, &ctrl_reg_data); + lis3->write(lis3, CTRL_REG3, (ctrl_reg_data & + ~(LIS3_IRQ1_MASK | LIS3_IRQ2_MASK)) | + (LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY)); + } + + if (lis3_dev.whoami == WAI_3DC) { + ctlreg = CTRL_REG4; + selftest = CTRL4_ST0; + } else { + ctlreg = CTRL_REG1; + if (lis3_dev.whoami == WAI_12B) + selftest = CTRL1_ST; + else + selftest = CTRL1_STP; + } + + lis3->read(lis3, ctlreg, ®); + lis3->write(lis3, ctlreg, (reg | selftest)); msleep(lis3->pwron_delay / lis3lv02d_get_odr()); /* Read directly to avoid axis remap */ @@ -193,7 +273,7 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3]) z = lis3->read_data(lis3, OUTZ); /* back to normal settings */ - lis3->write(lis3, CTRL_REG1, reg); + lis3->write(lis3, ctlreg, reg); msleep(lis3->pwron_delay / lis3lv02d_get_odr()); results[0] = x - lis3->read_data(lis3, OUTX); @@ -201,13 +281,33 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3]) results[2] = z - lis3->read_data(lis3, OUTZ); ret = 0; + + if (lis3_dev.whoami == WAI_8B) { + /* Restore original interrupt configuration */ + atomic_dec(&lis3_dev.wake_thread); + lis3->write(lis3, CTRL_REG3, ctrl_reg_data); + lis3->irq_cfg = irq_cfg; + + if ((irq_cfg & LIS3_IRQ1_MASK) && + lis3->data_ready_count[IRQ_LINE0] < 2) { + ret = SELFTEST_IRQ; + goto fail; + } + + if ((irq_cfg & LIS3_IRQ2_MASK) && + lis3->data_ready_count[IRQ_LINE1] < 2) { + ret = SELFTEST_IRQ; + goto fail; + } + } + if (lis3->pdata) { int i; for (i = 0; i < 3; i++) { /* Check against selftest acceptance limits */ if ((results[i] < lis3->pdata->st_min_limits[i]) || (results[i] > lis3->pdata->st_max_limits[i])) { - ret = -EIO; + ret = SELFTEST_FAIL; goto fail; } } @@ -219,10 +319,46 @@ fail: return ret; } +/* + * Order of registers in the list affects to order of the restore process. + * Perhaps it is a good idea to set interrupt enable register as a last one + * after all other configurations + */ +static u8 lis3_wai8_regs[] = { FF_WU_CFG_1, FF_WU_THS_1, FF_WU_DURATION_1, + FF_WU_CFG_2, FF_WU_THS_2, FF_WU_DURATION_2, + CLICK_CFG, CLICK_SRC, CLICK_THSY_X, CLICK_THSZ, + CLICK_TIMELIMIT, CLICK_LATENCY, CLICK_WINDOW, + CTRL_REG1, CTRL_REG2, CTRL_REG3}; + +static u8 lis3_wai12_regs[] = {FF_WU_CFG, FF_WU_THS_L, FF_WU_THS_H, + FF_WU_DURATION, DD_CFG, DD_THSI_L, DD_THSI_H, + DD_THSE_L, DD_THSE_H, + CTRL_REG1, CTRL_REG3, CTRL_REG2}; + +static inline void lis3_context_save(struct lis3lv02d *lis3) +{ + int i; + for (i = 0; i < lis3->regs_size; i++) + lis3->read(lis3, lis3->regs[i], &lis3->reg_cache[i]); + lis3->regs_stored = true; +} + +static inline void lis3_context_restore(struct lis3lv02d *lis3) +{ + int i; + if (lis3->regs_stored) + for (i = 0; i < lis3->regs_size; i++) + lis3->write(lis3, lis3->regs[i], lis3->reg_cache[i]); +} + void lis3lv02d_poweroff(struct lis3lv02d *lis3) { + if (lis3->reg_ctrl) + lis3_context_save(lis3); /* disable X,Y,Z axis and power down */ lis3->write(lis3, CTRL_REG1, 0x00); + if (lis3->reg_ctrl) + lis3->reg_ctrl(lis3, LIS3_REG_OFF); } EXPORT_SYMBOL_GPL(lis3lv02d_poweroff); @@ -232,19 +368,24 @@ void lis3lv02d_poweron(struct lis3lv02d *lis3) lis3->init(lis3); - /* LIS3 power on delay is quite long */ - msleep(lis3->pwron_delay / lis3lv02d_get_odr()); - /* * Common configuration * BDU: (12 bits sensors only) LSB and MSB values are not updated until * both have been read. So the value read will always be correct. + * Set BOOT bit to refresh factory tuning values. */ - if (lis3->whoami == WAI_12B) { - lis3->read(lis3, CTRL_REG2, ®); - reg |= CTRL2_BDU; - lis3->write(lis3, CTRL_REG2, reg); - } + lis3->read(lis3, CTRL_REG2, ®); + if (lis3->whoami == WAI_12B) + reg |= CTRL2_BDU | CTRL2_BOOT; + else + reg |= CTRL2_BOOT_8B; + lis3->write(lis3, CTRL_REG2, reg); + + /* LIS3 power on delay is quite long */ + msleep(lis3->pwron_delay / lis3lv02d_get_odr()); + + if (lis3->reg_ctrl) + lis3_context_restore(lis3); } EXPORT_SYMBOL_GPL(lis3lv02d_poweron); @@ -262,6 +403,27 @@ static void lis3lv02d_joystick_poll(struct input_polled_dev *pidev) mutex_unlock(&lis3_dev.mutex); } +static void lis3lv02d_joystick_open(struct input_polled_dev *pidev) +{ + if (lis3_dev.pm_dev) + pm_runtime_get_sync(lis3_dev.pm_dev); + + if (lis3_dev.pdata && lis3_dev.whoami == WAI_8B && lis3_dev.idev) + atomic_set(&lis3_dev.wake_thread, 1); + /* + * Update coordinates for the case where poll interval is 0 and + * the chip in running purely under interrupt control + */ + lis3lv02d_joystick_poll(pidev); +} + +static void lis3lv02d_joystick_close(struct input_polled_dev *pidev) +{ + atomic_set(&lis3_dev.wake_thread, 0); + if (lis3_dev.pm_dev) + pm_runtime_put(lis3_dev.pm_dev); +} + static irqreturn_t lis302dl_interrupt(int irq, void *dummy) { if (!test_bit(0, &lis3_dev.misc_opened)) @@ -277,8 +439,7 @@ static irqreturn_t lis302dl_interrupt(int irq, void *dummy) wake_up_interruptible(&lis3_dev.misc_wait); kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN); out: - if (lis3_dev.pdata && lis3_dev.whoami == WAI_8B && lis3_dev.idev && - lis3_dev.idev->input->users) + if (atomic_read(&lis3_dev.wake_thread)) return IRQ_WAKE_THREAD; return IRQ_HANDLED; } @@ -309,44 +470,41 @@ static void lis302dl_interrupt_handle_click(struct lis3lv02d *lis3) mutex_unlock(&lis3->mutex); } -static void lis302dl_interrupt_handle_ff_wu(struct lis3lv02d *lis3) +static inline void lis302dl_data_ready(struct lis3lv02d *lis3, int index) { - u8 wu1_src; - u8 wu2_src; - - lis3->read(lis3, FF_WU_SRC_1, &wu1_src); - lis3->read(lis3, FF_WU_SRC_2, &wu2_src); + int dummy; - wu1_src = wu1_src & FF_WU_SRC_IA ? wu1_src : 0; - wu2_src = wu2_src & FF_WU_SRC_IA ? wu2_src : 0; - - /* joystick poll is internally protected by the lis3->mutex. */ - if (wu1_src || wu2_src) - lis3lv02d_joystick_poll(lis3_dev.idev); + /* Dummy read to ack interrupt */ + lis3lv02d_get_xyz(lis3, &dummy, &dummy, &dummy); + lis3->data_ready_count[index]++; } static irqreturn_t lis302dl_interrupt_thread1_8b(int irq, void *data) { - struct lis3lv02d *lis3 = data; + u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ1_MASK; - if ((lis3->pdata->irq_cfg & LIS3_IRQ1_MASK) == LIS3_IRQ1_CLICK) + if (irq_cfg == LIS3_IRQ1_CLICK) lis302dl_interrupt_handle_click(lis3); + else if (unlikely(irq_cfg == LIS3_IRQ1_DATA_READY)) + lis302dl_data_ready(lis3, IRQ_LINE0); else - lis302dl_interrupt_handle_ff_wu(lis3); + lis3lv02d_joystick_poll(lis3->idev); return IRQ_HANDLED; } static irqreturn_t lis302dl_interrupt_thread2_8b(int irq, void *data) { - struct lis3lv02d *lis3 = data; + u8 irq_cfg = lis3->irq_cfg & LIS3_IRQ2_MASK; - if ((lis3->pdata->irq_cfg & LIS3_IRQ2_MASK) == LIS3_IRQ2_CLICK) + if (irq_cfg == LIS3_IRQ2_CLICK) lis302dl_interrupt_handle_click(lis3); + else if (unlikely(irq_cfg == LIS3_IRQ2_DATA_READY)) + lis302dl_data_ready(lis3, IRQ_LINE1); else - lis302dl_interrupt_handle_ff_wu(lis3); + lis3lv02d_joystick_poll(lis3->idev); return IRQ_HANDLED; } @@ -356,6 +514,9 @@ static int lis3lv02d_misc_open(struct inode *inode, struct file *file) if (test_and_set_bit(0, &lis3_dev.misc_opened)) return -EBUSY; /* already open */ + if (lis3_dev.pm_dev) + pm_runtime_get_sync(lis3_dev.pm_dev); + atomic_set(&lis3_dev.count, 0); return 0; } @@ -364,6 +525,8 @@ static int lis3lv02d_misc_release(struct inode *inode, struct file *file) { fasync_helper(-1, file, 0, &lis3_dev.async_queue); clear_bit(0, &lis3_dev.misc_opened); /* release the device */ + if (lis3_dev.pm_dev) + pm_runtime_put(lis3_dev.pm_dev); return 0; } @@ -460,6 +623,8 @@ int lis3lv02d_joystick_enable(void) return -ENOMEM; lis3_dev.idev->poll = lis3lv02d_joystick_poll; + lis3_dev.idev->open = lis3lv02d_joystick_open; + lis3_dev.idev->close = lis3lv02d_joystick_close; lis3_dev.idev->poll_interval = MDPS_POLL_INTERVAL; lis3_dev.idev->poll_interval_min = MDPS_POLL_MIN; lis3_dev.idev->poll_interval_max = MDPS_POLL_MAX; @@ -473,8 +638,16 @@ int lis3lv02d_joystick_enable(void) set_bit(EV_ABS, input_dev->evbit); max_val = (lis3_dev.mdps_max_val * lis3_dev.scale) / LIS3_ACCURACY; - fuzz = (LIS3_DEFAULT_FUZZ * lis3_dev.scale) / LIS3_ACCURACY; - flat = (LIS3_DEFAULT_FLAT * lis3_dev.scale) / LIS3_ACCURACY; + if (lis3_dev.whoami == WAI_12B) { + fuzz = LIS3_DEFAULT_FUZZ_12B; + flat = LIS3_DEFAULT_FLAT_12B; + } else { + fuzz = LIS3_DEFAULT_FUZZ_8B; + flat = LIS3_DEFAULT_FLAT_8B; + } + fuzz = (fuzz * lis3_dev.scale) / LIS3_ACCURACY; + flat = (flat * lis3_dev.scale) / LIS3_ACCURACY; + input_set_abs_params(input_dev, ABS_X, -max_val, max_val, fuzz, flat); input_set_abs_params(input_dev, ABS_Y, -max_val, max_val, fuzz, flat); input_set_abs_params(input_dev, ABS_Z, -max_val, max_val, fuzz, flat); @@ -512,14 +685,47 @@ void lis3lv02d_joystick_disable(void) EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable); /* Sysfs stuff */ +static void lis3lv02d_sysfs_poweron(struct lis3lv02d *lis3) +{ + /* + * SYSFS functions are fast visitors so put-call + * immediately after the get-call. However, keep + * chip running for a while and schedule delayed + * suspend. This way periodic sysfs calls doesn't + * suffer from relatively long power up time. + */ + + if (lis3->pm_dev) { + pm_runtime_get_sync(lis3->pm_dev); + pm_runtime_put_noidle(lis3->pm_dev); + pm_schedule_suspend(lis3->pm_dev, LIS3_SYSFS_POWERDOWN_DELAY); + } +} + static ssize_t lis3lv02d_selftest_show(struct device *dev, struct device_attribute *attr, char *buf) { - int result; s16 values[3]; - result = lis3lv02d_selftest(&lis3_dev, values); - return sprintf(buf, "%s %d %d %d\n", result == 0 ? "OK" : "FAIL", + static const char ok[] = "OK"; + static const char fail[] = "FAIL"; + static const char irq[] = "FAIL_IRQ"; + const char *res; + + lis3lv02d_sysfs_poweron(&lis3_dev); + switch (lis3lv02d_selftest(&lis3_dev, values)) { + case SELFTEST_FAIL: + res = fail; + break; + case SELFTEST_IRQ: + res = irq; + break; + case SELFTEST_OK: + default: + res = ok; + break; + } + return sprintf(buf, "%s %d %d %d\n", res, values[0], values[1], values[2]); } @@ -528,6 +734,7 @@ static ssize_t lis3lv02d_position_show(struct device *dev, { int x, y, z; + lis3lv02d_sysfs_poweron(&lis3_dev); mutex_lock(&lis3_dev.mutex); lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z); mutex_unlock(&lis3_dev.mutex); @@ -537,6 +744,7 @@ static ssize_t lis3lv02d_position_show(struct device *dev, static ssize_t lis3lv02d_rate_show(struct device *dev, struct device_attribute *attr, char *buf) { + lis3lv02d_sysfs_poweron(&lis3_dev); return sprintf(buf, "%d\n", lis3lv02d_get_odr()); } @@ -549,6 +757,7 @@ static ssize_t lis3lv02d_rate_set(struct device *dev, if (strict_strtoul(buf, 0, &rate)) return -EINVAL; + lis3lv02d_sysfs_poweron(&lis3_dev); if (lis3lv02d_set_odr(rate)) return -EINVAL; @@ -585,6 +794,18 @@ int lis3lv02d_remove_fs(struct lis3lv02d *lis3) { sysfs_remove_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group); platform_device_unregister(lis3->pdev); + if (lis3->pm_dev) { + /* Barrier after the sysfs remove */ + pm_runtime_barrier(lis3->pm_dev); + + /* SYSFS may have left chip running. Turn off if necessary */ + if (!pm_runtime_suspended(lis3->pm_dev)) + lis3lv02d_poweroff(&lis3_dev); + + pm_runtime_disable(lis3->pm_dev); + pm_runtime_set_suspended(lis3->pm_dev); + } + kfree(lis3->reg_cache); return 0; } EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); @@ -616,16 +837,16 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *dev, if (p->wakeup_flags) { dev->write(dev, FF_WU_CFG_1, p->wakeup_flags); dev->write(dev, FF_WU_THS_1, p->wakeup_thresh & 0x7f); - /* default to 2.5ms for now */ - dev->write(dev, FF_WU_DURATION_1, 1); + /* pdata value + 1 to keep this backward compatible*/ + dev->write(dev, FF_WU_DURATION_1, p->duration1 + 1); ctrl2 ^= HP_FF_WU1; /* Xor to keep compatible with old pdata*/ } if (p->wakeup_flags2) { dev->write(dev, FF_WU_CFG_2, p->wakeup_flags2); dev->write(dev, FF_WU_THS_2, p->wakeup_thresh2 & 0x7f); - /* default to 2.5ms for now */ - dev->write(dev, FF_WU_DURATION_2, 1); + /* pdata value + 1 to keep this backward compatible*/ + dev->write(dev, FF_WU_DURATION_2, p->duration2 + 1); ctrl2 ^= HP_FF_WU2; /* Xor to keep compatible with old pdata*/ } /* Configure hipass filters */ @@ -635,8 +856,8 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *dev, err = request_threaded_irq(p->irq2, NULL, lis302dl_interrupt_thread2_8b, - IRQF_TRIGGER_RISING | - IRQF_ONESHOT, + IRQF_TRIGGER_RISING | IRQF_ONESHOT | + (p->irq_flags2 & IRQF_TRIGGER_MASK), DRIVER_NAME, &lis3_dev); if (err < 0) printk(KERN_ERR DRIVER_NAME @@ -652,6 +873,7 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) { int err; irq_handler_t thread_fn; + int irq_flags = 0; dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I); @@ -664,6 +886,8 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) dev->odrs = lis3_12_rates; dev->odr_mask = CTRL1_DF0 | CTRL1_DF1; dev->scale = LIS3_SENSITIVITY_12B; + dev->regs = lis3_wai12_regs; + dev->regs_size = ARRAY_SIZE(lis3_wai12_regs); break; case WAI_8B: printk(KERN_INFO DRIVER_NAME ": 8 bits sensor found\n"); @@ -673,6 +897,17 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) dev->odrs = lis3_8_rates; dev->odr_mask = CTRL1_DR; dev->scale = LIS3_SENSITIVITY_8B; + dev->regs = lis3_wai8_regs; + dev->regs_size = ARRAY_SIZE(lis3_wai8_regs); + break; + case WAI_3DC: + printk(KERN_INFO DRIVER_NAME ": 8 bits 3DC sensor found\n"); + dev->read_data = lis3lv02d_read_8; + dev->mdps_max_val = 128; + dev->pwron_delay = LIS3_PWRON_DELAY_WAI_8B; + dev->odrs = lis3_3dc_rates; + dev->odr_mask = CTRL1_ODR0|CTRL1_ODR1|CTRL1_ODR2|CTRL1_ODR3; + dev->scale = LIS3_SENSITIVITY_8B; break; default: printk(KERN_ERR DRIVER_NAME @@ -680,11 +915,25 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) return -EINVAL; } + dev->reg_cache = kzalloc(max(sizeof(lis3_wai8_regs), + sizeof(lis3_wai12_regs)), GFP_KERNEL); + + if (dev->reg_cache == NULL) { + printk(KERN_ERR DRIVER_NAME "out of memory\n"); + return -ENOMEM; + } + mutex_init(&dev->mutex); + atomic_set(&dev->wake_thread, 0); lis3lv02d_add_fs(dev); lis3lv02d_poweron(dev); + if (dev->pm_dev) { + pm_runtime_set_active(dev->pm_dev); + pm_runtime_enable(dev->pm_dev); + } + if (lis3lv02d_joystick_enable()) printk(KERN_ERR DRIVER_NAME ": joystick initialization failed\n"); @@ -696,8 +945,14 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) if (dev->whoami == WAI_8B) lis3lv02d_8b_configure(dev, p); + irq_flags = p->irq_flags1 & IRQF_TRIGGER_MASK; + + dev->irq_cfg = p->irq_cfg; if (p->irq_cfg) dev->write(dev, CTRL_REG3, p->irq_cfg); + + if (p->default_rate) + lis3lv02d_set_odr(p->default_rate); } /* bail if we did not get an IRQ from the bus layer */ @@ -725,7 +980,8 @@ int lis3lv02d_init_device(struct lis3lv02d *dev) err = request_threaded_irq(dev->irq, lis302dl_interrupt, thread_fn, - IRQF_TRIGGER_RISING | IRQF_ONESHOT, + IRQF_TRIGGER_RISING | IRQF_ONESHOT | + irq_flags, DRIVER_NAME, &lis3_dev); if (err < 0) { diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h index 854091380e3..a1939589eb2 100644 --- a/drivers/hwmon/lis3lv02d.h +++ b/drivers/hwmon/lis3lv02d.h @@ -20,6 +20,7 @@ */ #include <linux/platform_device.h> #include <linux/input-polldev.h> +#include <linux/regulator/consumer.h> /* * This driver tries to support the "digital" accelerometer chips from @@ -45,6 +46,7 @@ enum lis3_reg { CTRL_REG1 = 0x20, CTRL_REG2 = 0x21, CTRL_REG3 = 0x22, + CTRL_REG4 = 0x23, HP_FILTER_RESET = 0x23, STATUS_REG = 0x27, OUTX_L = 0x28, @@ -93,6 +95,7 @@ enum lis3lv02d_reg { }; enum lis3_who_am_i { + WAI_3DC = 0x33, /* 8 bits: LIS3DC, HP3DC */ WAI_12B = 0x3A, /* 12 bits: LIS3LV02D[LQ]... */ WAI_8B = 0x3B, /* 8 bits: LIS[23]02D[LQ]... */ WAI_6B = 0x52, /* 6 bits: LIS331DLF - not supported */ @@ -118,6 +121,13 @@ enum lis3lv02d_ctrl1_8b { CTRL1_DR = 0x80, }; +enum lis3lv02d_ctrl1_3dc { + CTRL1_ODR0 = 0x10, + CTRL1_ODR1 = 0x20, + CTRL1_ODR2 = 0x40, + CTRL1_ODR3 = 0x80, +}; + enum lis3lv02d_ctrl2 { CTRL2_DAS = 0x01, CTRL2_SIM = 0x02, @@ -129,9 +139,18 @@ enum lis3lv02d_ctrl2 { CTRL2_FS = 0x80, /* Full Scale selection */ }; +enum lis3lv02d_ctrl4_3dc { + CTRL4_SIM = 0x01, + CTRL4_ST0 = 0x02, + CTRL4_ST1 = 0x04, + CTRL4_FS0 = 0x10, + CTRL4_FS1 = 0x20, +}; + enum lis302d_ctrl2 { HP_FF_WU2 = 0x08, HP_FF_WU1 = 0x04, + CTRL2_BOOT_8B = 0x40, }; enum lis3lv02d_ctrl3 { @@ -206,19 +225,33 @@ enum lis3lv02d_click_src_8b { CLICK_IA = 0x40, }; -struct axis_conversion { - s8 x; - s8 y; - s8 z; +enum lis3lv02d_reg_state { + LIS3_REG_OFF = 0x00, + LIS3_REG_ON = 0x01, +}; + +union axis_conversion { + struct { + int x, y, z; + }; + int as_array[3]; + }; struct lis3lv02d { void *bus_priv; /* used by the bus layer only */ + struct device *pm_dev; /* for pm_runtime purposes */ int (*init) (struct lis3lv02d *lis3); int (*write) (struct lis3lv02d *lis3, int reg, u8 val); int (*read) (struct lis3lv02d *lis3, int reg, u8 *ret); + int (*blkread) (struct lis3lv02d *lis3, int reg, int len, u8 *ret); + int (*reg_ctrl) (struct lis3lv02d *lis3, bool state); int *odrs; /* Supported output data rates */ + u8 *regs; /* Regs to store / restore */ + int regs_size; + u8 *reg_cache; + bool regs_stored; u8 odr_mask; /* ODR bit mask */ u8 whoami; /* indicates measurement precision */ s16 (*read_data) (struct lis3lv02d *lis3, int reg); @@ -231,14 +264,18 @@ struct lis3lv02d { struct input_polled_dev *idev; /* input device */ struct platform_device *pdev; /* platform device */ + struct regulator_bulk_data regulators[2]; atomic_t count; /* interrupt count after last read */ - struct axis_conversion ac; /* hw -> logical axis */ + union axis_conversion ac; /* hw -> logical axis */ int mapped_btns[3]; u32 irq; /* IRQ number */ struct fasync_struct *async_queue; /* queue for the misc device */ wait_queue_head_t misc_wait; /* Wait queue for the misc device */ unsigned long misc_opened; /* bit0: whether the device is open */ + int data_ready_count[2]; + atomic_t wake_thread; + unsigned char irq_cfg; struct lis3lv02d_platform_data *pdata; /* for passing board config */ struct mutex mutex; /* Serialize poll and selftest */ diff --git a/drivers/hwmon/lis3lv02d_i2c.c b/drivers/hwmon/lis3lv02d_i2c.c index 8e5933b72d1..9f4bae07f71 100644 --- a/drivers/hwmon/lis3lv02d_i2c.c +++ b/drivers/hwmon/lis3lv02d_i2c.c @@ -29,10 +29,30 @@ #include <linux/init.h> #include <linux/err.h> #include <linux/i2c.h> +#include <linux/pm_runtime.h> +#include <linux/delay.h> #include "lis3lv02d.h" #define DRV_NAME "lis3lv02d_i2c" +static const char reg_vdd[] = "Vdd"; +static const char reg_vdd_io[] = "Vdd_IO"; + +static int lis3_reg_ctrl(struct lis3lv02d *lis3, bool state) +{ + int ret; + if (state == LIS3_REG_OFF) { + ret = regulator_bulk_disable(ARRAY_SIZE(lis3->regulators), + lis3->regulators); + } else { + ret = regulator_bulk_enable(ARRAY_SIZE(lis3->regulators), + lis3->regulators); + /* Chip needs time to wakeup. Not mentioned in datasheet */ + usleep_range(10000, 20000); + } + return ret; +} + static inline s32 lis3_i2c_write(struct lis3lv02d *lis3, int reg, u8 value) { struct i2c_client *c = lis3->bus_priv; @@ -46,24 +66,38 @@ static inline s32 lis3_i2c_read(struct lis3lv02d *lis3, int reg, u8 *v) return 0; } +static inline s32 lis3_i2c_blockread(struct lis3lv02d *lis3, int reg, int len, + u8 *v) +{ + struct i2c_client *c = lis3->bus_priv; + reg |= (1 << 7); /* 7th bit enables address auto incrementation */ + return i2c_smbus_read_i2c_block_data(c, reg, len, v); +} + static int lis3_i2c_init(struct lis3lv02d *lis3) { u8 reg; int ret; + if (lis3->reg_ctrl) + lis3_reg_ctrl(lis3, LIS3_REG_ON); + + lis3->read(lis3, WHO_AM_I, ®); + if (reg != lis3->whoami) + printk(KERN_ERR "lis3: power on failure\n"); + /* power up the device */ ret = lis3->read(lis3, CTRL_REG1, ®); if (ret < 0) return ret; - reg |= CTRL1_PD0; + reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen; return lis3->write(lis3, CTRL_REG1, reg); } /* Default axis mapping but it can be overwritten by platform data */ -static struct axis_conversion lis3lv02d_axis_map = { LIS3_DEV_X, - LIS3_DEV_Y, - LIS3_DEV_Z }; +static union axis_conversion lis3lv02d_axis_map = + { .as_array = { LIS3_DEV_X, LIS3_DEV_Y, LIS3_DEV_Z } }; static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -72,6 +106,15 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, struct lis3lv02d_platform_data *pdata = client->dev.platform_data; if (pdata) { + /* Regulator control is optional */ + if (pdata->driver_features & LIS3_USE_REGULATOR_CTRL) + lis3_dev.reg_ctrl = lis3_reg_ctrl; + + if ((pdata->driver_features & LIS3_USE_BLOCK_READ) && + (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_I2C_BLOCK))) + lis3_dev.blkread = lis3_i2c_blockread; + if (pdata->axis_x) lis3lv02d_axis_map.x = pdata->axis_x; @@ -88,6 +131,16 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, goto fail; } + if (lis3_dev.reg_ctrl) { + lis3_dev.regulators[0].supply = reg_vdd; + lis3_dev.regulators[1].supply = reg_vdd_io; + ret = regulator_bulk_get(&client->dev, + ARRAY_SIZE(lis3_dev.regulators), + lis3_dev.regulators); + if (ret < 0) + goto fail; + } + lis3_dev.pdata = pdata; lis3_dev.bus_priv = client; lis3_dev.init = lis3_i2c_init; @@ -95,10 +148,24 @@ static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, lis3_dev.write = lis3_i2c_write; lis3_dev.irq = client->irq; lis3_dev.ac = lis3lv02d_axis_map; + lis3_dev.pm_dev = &client->dev; i2c_set_clientdata(client, &lis3_dev); + + /* Provide power over the init call */ + if (lis3_dev.reg_ctrl) + lis3_reg_ctrl(&lis3_dev, LIS3_REG_ON); + ret = lis3lv02d_init_device(&lis3_dev); + + if (lis3_dev.reg_ctrl) + lis3_reg_ctrl(&lis3_dev, LIS3_REG_OFF); + + if (ret == 0) + return 0; fail: + if (pdata && pdata->release_resources) + pdata->release_resources(); return ret; } @@ -111,14 +178,18 @@ static int __devexit lis3lv02d_i2c_remove(struct i2c_client *client) pdata->release_resources(); lis3lv02d_joystick_disable(); - lis3lv02d_poweroff(lis3); + lis3lv02d_remove_fs(&lis3_dev); - return lis3lv02d_remove_fs(&lis3_dev); + if (lis3_dev.reg_ctrl) + regulator_bulk_free(ARRAY_SIZE(lis3->regulators), + lis3_dev.regulators); + return 0; } #ifdef CONFIG_PM -static int lis3lv02d_i2c_suspend(struct i2c_client *client, pm_message_t mesg) +static int lis3lv02d_i2c_suspend(struct device *dev) { + struct i2c_client *client = container_of(dev, struct i2c_client, dev); struct lis3lv02d *lis3 = i2c_get_clientdata(client); if (!lis3->pdata || !lis3->pdata->wakeup_flags) @@ -126,18 +197,21 @@ static int lis3lv02d_i2c_suspend(struct i2c_client *client, pm_message_t mesg) return 0; } -static int lis3lv02d_i2c_resume(struct i2c_client *client) +static int lis3lv02d_i2c_resume(struct device *dev) { + struct i2c_client *client = container_of(dev, struct i2c_client, dev); struct lis3lv02d *lis3 = i2c_get_clientdata(client); - if (!lis3->pdata || !lis3->pdata->wakeup_flags) + /* + * pm_runtime documentation says that devices should always + * be powered on at resume. Pm_runtime turns them off after system + * wide resume is complete. + */ + if (!lis3->pdata || !lis3->pdata->wakeup_flags || + pm_runtime_suspended(dev)) lis3lv02d_poweron(lis3); - return 0; -} -static void lis3lv02d_i2c_shutdown(struct i2c_client *client) -{ - lis3lv02d_i2c_suspend(client, PMSG_SUSPEND); + return 0; } #else #define lis3lv02d_i2c_suspend NULL @@ -145,6 +219,24 @@ static void lis3lv02d_i2c_shutdown(struct i2c_client *client) #define lis3lv02d_i2c_shutdown NULL #endif +static int lis3_i2c_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct lis3lv02d *lis3 = i2c_get_clientdata(client); + + lis3lv02d_poweroff(lis3); + return 0; +} + +static int lis3_i2c_runtime_resume(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct lis3lv02d *lis3 = i2c_get_clientdata(client); + + lis3lv02d_poweron(lis3); + return 0; +} + static const struct i2c_device_id lis3lv02d_id[] = { {"lis3lv02d", 0 }, {} @@ -152,14 +244,20 @@ static const struct i2c_device_id lis3lv02d_id[] = { MODULE_DEVICE_TABLE(i2c, lis3lv02d_id); +static const struct dev_pm_ops lis3_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(lis3lv02d_i2c_suspend, + lis3lv02d_i2c_resume) + SET_RUNTIME_PM_OPS(lis3_i2c_runtime_suspend, + lis3_i2c_runtime_resume, + NULL) +}; + static struct i2c_driver lis3lv02d_i2c_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .pm = &lis3_pm_ops, }, - .suspend = lis3lv02d_i2c_suspend, - .shutdown = lis3lv02d_i2c_shutdown, - .resume = lis3lv02d_i2c_resume, .probe = lis3lv02d_i2c_probe, .remove = __devexit_p(lis3lv02d_i2c_remove), .id_table = lis3lv02d_id, diff --git a/drivers/hwmon/lis3lv02d_spi.c b/drivers/hwmon/lis3lv02d_spi.c index b9be5e3a22b..2549de1de4e 100644 --- a/drivers/hwmon/lis3lv02d_spi.c +++ b/drivers/hwmon/lis3lv02d_spi.c @@ -50,11 +50,12 @@ static int lis3_spi_init(struct lis3lv02d *lis3) if (ret < 0) return ret; - reg |= CTRL1_PD0; + reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen; return lis3->write(lis3, CTRL_REG1, reg); } -static struct axis_conversion lis3lv02d_axis_normal = { 1, 2, 3 }; +static union axis_conversion lis3lv02d_axis_normal = + { .as_array = { 1, 2, 3 } }; static int __devinit lis302dl_spi_probe(struct spi_device *spi) { 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/ltc4261.c b/drivers/hwmon/ltc4261.c new file mode 100644 index 00000000000..26762617867 --- /dev/null +++ b/drivers/hwmon/ltc4261.c @@ -0,0 +1,315 @@ +/* + * Driver for Linear Technology LTC4261 I2C Negative Voltage Hot Swap Controller + * + * Copyright (C) 2010 Ericsson AB. + * + * Derived from: + * + * Driver for Linear Technology LTC4245 I2C Multiple Supply Hot Swap Controller + * Copyright (C) 2008 Ira W. Snyder <iws@ovro.caltech.edu> + * + * Datasheet: http://cds.linear.com/docs/Datasheet/42612fb.pdf + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> + +/* chip registers */ +#define LTC4261_STATUS 0x00 /* readonly */ +#define LTC4261_FAULT 0x01 +#define LTC4261_ALERT 0x02 +#define LTC4261_CONTROL 0x03 +#define LTC4261_SENSE_H 0x04 +#define LTC4261_SENSE_L 0x05 +#define LTC4261_ADIN2_H 0x06 +#define LTC4261_ADIN2_L 0x07 +#define LTC4261_ADIN_H 0x08 +#define LTC4261_ADIN_L 0x09 + +/* + * Fault register bits + */ +#define FAULT_OV (1<<0) +#define FAULT_UV (1<<1) +#define FAULT_OC (1<<2) + +struct ltc4261_data { + struct device *hwmon_dev; + + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + + /* Registers */ + u8 regs[10]; +}; + +static struct ltc4261_data *ltc4261_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ltc4261_data *data = i2c_get_clientdata(client); + struct ltc4261_data *ret = data; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ / 4) || !data->valid) { + int i; + + /* Read registers -- 0x00 to 0x09 */ + for (i = 0; i < ARRAY_SIZE(data->regs); i++) { + int val; + + val = i2c_smbus_read_byte_data(client, i); + if (unlikely(val < 0)) { + dev_dbg(dev, + "Failed to read ADC value: error %d", + val); + ret = ERR_PTR(val); + goto abort; + } + data->regs[i] = val; + } + data->last_updated = jiffies; + data->valid = 1; + } +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +/* Return the voltage from the given register in mV or mA */ +static int ltc4261_get_value(struct ltc4261_data *data, u8 reg) +{ + u32 val; + + val = (data->regs[reg] << 2) + (data->regs[reg + 1] >> 6); + + switch (reg) { + case LTC4261_ADIN_H: + case LTC4261_ADIN2_H: + /* 2.5mV resolution. Convert to mV. */ + val = val * 25 / 10; + break; + case LTC4261_SENSE_H: + /* + * 62.5uV resolution. Convert to current as measured with + * an 1 mOhm sense resistor, in mA. If a different sense + * resistor is installed, calculate the actual current by + * dividing the reported current by the sense resistor value + * in mOhm. + */ + val = val * 625 / 10; + break; + default: + /* If we get here, the developer messed up */ + WARN_ON_ONCE(1); + val = 0; + break; + } + + return val; +} + +static ssize_t ltc4261_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ltc4261_data *data = ltc4261_update_device(dev); + int value; + + if (IS_ERR(data)) + return PTR_ERR(data); + + value = ltc4261_get_value(data, attr->index); + return snprintf(buf, PAGE_SIZE, "%d\n", value); +} + +static ssize_t ltc4261_show_bool(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct ltc4261_data *data = ltc4261_update_device(dev); + u8 fault; + + if (IS_ERR(data)) + return PTR_ERR(data); + + fault = data->regs[LTC4261_FAULT] & attr->index; + if (fault) /* Clear reported faults in chip register */ + i2c_smbus_write_byte_data(client, LTC4261_FAULT, ~fault); + + return snprintf(buf, PAGE_SIZE, "%d\n", fault ? 1 : 0); +} + +/* + * These macros are used below in constructing device attribute objects + * for use with sysfs_create_group() to make a sysfs device file + * for each register. + */ + +#define LTC4261_VALUE(name, ltc4261_cmd_idx) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4261_show_value, NULL, ltc4261_cmd_idx) + +#define LTC4261_BOOL(name, mask) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4261_show_bool, NULL, (mask)) + +/* + * Input voltages. + */ +LTC4261_VALUE(in1_input, LTC4261_ADIN_H); +LTC4261_VALUE(in2_input, LTC4261_ADIN2_H); + +/* + * Voltage alarms. The chip has only one set of voltage alarm status bits, + * triggered by input voltage alarms. In many designs, those alarms are + * associated with the ADIN2 sensor, due to the proximity of the ADIN2 pin + * to the OV pin. ADIN2 is, however, not available on all chip variants. + * To ensure that the alarm condition is reported to the user, report it + * with both voltage sensors. + */ +LTC4261_BOOL(in1_min_alarm, FAULT_UV); +LTC4261_BOOL(in1_max_alarm, FAULT_OV); +LTC4261_BOOL(in2_min_alarm, FAULT_UV); +LTC4261_BOOL(in2_max_alarm, FAULT_OV); + +/* Currents (via sense resistor) */ +LTC4261_VALUE(curr1_input, LTC4261_SENSE_H); + +/* Overcurrent alarm */ +LTC4261_BOOL(curr1_max_alarm, FAULT_OC); + +static struct attribute *ltc4261_attributes[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, + &sensor_dev_attr_in1_max_alarm.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_min_alarm.dev_attr.attr, + &sensor_dev_attr_in2_max_alarm.dev_attr.attr, + + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + + NULL, +}; + +static const struct attribute_group ltc4261_group = { + .attrs = ltc4261_attributes, +}; + +static int ltc4261_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct ltc4261_data *data; + int ret; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + if (i2c_smbus_read_byte_data(client, LTC4261_STATUS) < 0) { + dev_err(&client->dev, "Failed to read register %d:%02x:%02x\n", + adapter->id, client->addr, LTC4261_STATUS); + return -ENODEV; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto out_kzalloc; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* Clear faults */ + i2c_smbus_write_byte_data(client, LTC4261_FAULT, 0x00); + + /* Register sysfs hooks */ + ret = sysfs_create_group(&client->dev.kobj, <c4261_group); + if (ret) + goto out_sysfs_create_group; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + goto out_hwmon_device_register; + } + + return 0; + +out_hwmon_device_register: + sysfs_remove_group(&client->dev.kobj, <c4261_group); +out_sysfs_create_group: + kfree(data); +out_kzalloc: + return ret; +} + +static int ltc4261_remove(struct i2c_client *client) +{ + struct ltc4261_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, <c4261_group); + + kfree(data); + + return 0; +} + +static const struct i2c_device_id ltc4261_id[] = { + {"ltc4261", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, ltc4261_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver ltc4261_driver = { + .driver = { + .name = "ltc4261", + }, + .probe = ltc4261_probe, + .remove = ltc4261_remove, + .id_table = ltc4261_id, +}; + +static int __init ltc4261_init(void) +{ + return i2c_add_driver(<c4261_driver); +} + +static void __exit ltc4261_exit(void) +{ + i2c_del_driver(<c4261_driver); +} + +MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>"); +MODULE_DESCRIPTION("LTC4261 driver"); +MODULE_LICENSE("GPL"); + +module_init(ltc4261_init); +module_exit(ltc4261_exit); 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/pkgtemp.c b/drivers/hwmon/pkgtemp.c index f11903936c8..0798210590b 100644 --- a/drivers/hwmon/pkgtemp.c +++ b/drivers/hwmon/pkgtemp.c @@ -21,7 +21,6 @@ */ #include <linux/module.h> -#include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/jiffies.h> @@ -35,6 +34,7 @@ #include <linux/cpu.h> #include <asm/msr.h> #include <asm/processor.h> +#include <asm/smp.h> #define DRVNAME "pkgtemp" @@ -339,8 +339,7 @@ exit: return err; } -#ifdef CONFIG_HOTPLUG_CPU -static void pkgtemp_device_remove(unsigned int cpu) +static void __cpuinit pkgtemp_device_remove(unsigned int cpu) { struct pdev_entry *p; unsigned int i; @@ -387,12 +386,10 @@ static int __cpuinit pkgtemp_cpu_callback(struct notifier_block *nfb, static struct notifier_block pkgtemp_cpu_notifier __refdata = { .notifier_call = pkgtemp_cpu_callback, }; -#endif /* !CONFIG_HOTPLUG_CPU */ static int __init pkgtemp_init(void) { int i, err = -ENODEV; - struct pdev_entry *p, *n; /* quick check if we run Intel */ if (cpu_data(0).x86_vendor != X86_VENDOR_INTEL) @@ -402,31 +399,23 @@ static int __init pkgtemp_init(void) if (err) goto exit; - for_each_online_cpu(i) { - err = pkgtemp_device_add(i); - if (err) - goto exit_devices_unreg; - } + for_each_online_cpu(i) + pkgtemp_device_add(i); + +#ifndef CONFIG_HOTPLUG_CPU if (list_empty(&pdev_list)) { err = -ENODEV; goto exit_driver_unreg; } +#endif -#ifdef CONFIG_HOTPLUG_CPU register_hotcpu_notifier(&pkgtemp_cpu_notifier); -#endif return 0; -exit_devices_unreg: - mutex_lock(&pdev_list_mutex); - list_for_each_entry_safe(p, n, &pdev_list, list) { - platform_device_unregister(p->pdev); - list_del(&p->list); - kfree(p); - } - mutex_unlock(&pdev_list_mutex); +#ifndef CONFIG_HOTPLUG_CPU exit_driver_unreg: platform_driver_unregister(&pkgtemp_driver); +#endif exit: return err; } @@ -434,9 +423,8 @@ exit: static void __exit pkgtemp_exit(void) { struct pdev_entry *p, *n; -#ifdef CONFIG_HOTPLUG_CPU + unregister_hotcpu_notifier(&pkgtemp_cpu_notifier); -#endif mutex_lock(&pdev_list_mutex); list_for_each_entry_safe(p, n, &pdev_list, list) { platform_device_unregister(p->pdev); 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/via-cputemp.c b/drivers/hwmon/via-cputemp.c index ffb793af680..ec7fad747ad 100644 --- a/drivers/hwmon/via-cputemp.c +++ b/drivers/hwmon/via-cputemp.c @@ -22,10 +22,8 @@ */ #include <linux/module.h> -#include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> -#include <linux/jiffies.h> #include <linux/hwmon.h> #include <linux/sysfs.h> #include <linux/hwmon-sysfs.h> @@ -237,8 +235,7 @@ exit: return err; } -#ifdef CONFIG_HOTPLUG_CPU -static void via_cputemp_device_remove(unsigned int cpu) +static void __cpuinit via_cputemp_device_remove(unsigned int cpu) { struct pdev_entry *p, *n; mutex_lock(&pdev_list_mutex); @@ -272,7 +269,6 @@ static int __cpuinit via_cputemp_cpu_callback(struct notifier_block *nfb, static struct notifier_block via_cputemp_cpu_notifier __refdata = { .notifier_call = via_cputemp_cpu_callback, }; -#endif /* !CONFIG_HOTPLUG_CPU */ static int __init via_cputemp_init(void) { @@ -313,9 +309,7 @@ static int __init via_cputemp_init(void) goto exit_driver_unreg; } -#ifdef CONFIG_HOTPLUG_CPU register_hotcpu_notifier(&via_cputemp_cpu_notifier); -#endif return 0; exit_devices_unreg: @@ -335,9 +329,8 @@ exit: static void __exit via_cputemp_exit(void) { struct pdev_entry *p, *n; -#ifdef CONFIG_HOTPLUG_CPU + unregister_hotcpu_notifier(&via_cputemp_cpu_notifier); -#endif mutex_lock(&pdev_list_mutex); list_for_each_entry_safe(p, n, &pdev_list, list) { platform_device_unregister(p->pdev); diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index 697202e2789..8e540ada47d 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -35,7 +35,6 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/hwmon.h> -#include <linux/smp_lock.h> #include <linux/hwmon-vid.h> #include <linux/hwmon-sysfs.h> #include <linux/err.h> @@ -52,6 +51,7 @@ #define WATCHDOG_TIMEOUT 2 /* 2 minute default timeout */ /* Addresses to scan */ +static DEFINE_MUTEX(watchdog_mutex); static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; @@ -1333,7 +1333,7 @@ static long watchdog_ioctl(struct file *filp, unsigned int cmd, int val, ret = 0; struct w83793_data *data = filp->private_data; - lock_kernel(); + mutex_lock(&watchdog_mutex); switch (cmd) { case WDIOC_GETSUPPORT: if (!nowayout) @@ -1387,7 +1387,7 @@ static long watchdog_ioctl(struct file *filp, unsigned int cmd, default: ret = -ENOTTY; } - unlock_kernel(); + mutex_unlock(&watchdog_mutex); return ret; } 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); |