diff options
Diffstat (limited to 'drivers/hwmon')
174 files changed, 59819 insertions, 18569 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 68cf87749a4..02d3d85829f 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -39,9 +39,22 @@ config HWMON_DEBUG_CHIP comment "Native drivers" +config SENSORS_AB8500 + tristate "AB8500 thermal monitoring" + depends on AB8500_GPADC && AB8500_BM + default n + help + If you say yes here you get support for the thermal sensor part + of the AB8500 chip. The driver includes thermal management for + AB8500 die and two GPADC channels. The GPADC channel are preferably + used to access sensors outside the AB8500 chip. + + This driver can also be built as a module. If so, the module + will be called abx500-temp. + config SENSORS_ABITUGURU tristate "Abit uGuru (rev 1 & 2)" - depends on X86 && EXPERIMENTAL + depends on X86 && DMI help If you say yes here you get support for the sensor part of the first and second revision of the Abit uGuru chip. The voltage and frequency @@ -56,7 +69,7 @@ config SENSORS_ABITUGURU config SENSORS_ABITUGURU3 tristate "Abit uGuru (rev 3)" - depends on X86 && EXPERIMENTAL + depends on X86 && DMI help If you say yes here you get support for the sensor part of the third revision of the Abit uGuru chip. Only reading the sensors @@ -68,9 +81,19 @@ config SENSORS_ABITUGURU3 This driver can also be built as a module. If so, the module will be called abituguru3. +config SENSORS_AD7314 + tristate "Analog Devices AD7314 and compatibles" + depends on SPI + help + If you say yes here you get support for the Analog Devices + AD7314, ADT7301 and ADT7302 temperature sensors. + + This driver can also be built as a module. If so, the module + will be called ad7314. + config SENSORS_AD7414 tristate "Analog Devices AD7414" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for the Analog Devices AD7414 temperature monitoring chip. @@ -80,7 +103,7 @@ config SENSORS_AD7414 config SENSORS_AD7418 tristate "Analog Devices AD7416, AD7417 and AD7418" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for the Analog Devices AD7416, AD7417 and AD7418 temperature monitoring chips. @@ -88,30 +111,13 @@ config SENSORS_AD7418 This driver can also be built as a module. If so, the module will be called ad7418. -config SENSORS_ADCXX - tristate "National Semiconductor ADCxxxSxxx" - depends on SPI_MASTER && EXPERIMENTAL - help - If you say yes here you get support for the National Semiconductor - ADC<bb><c>S<sss> chip family, where - * bb is the resolution in number of bits (8, 10, 12) - * c is the number of channels (1, 2, 4, 8) - * sss is the maximum conversion speed (021 for 200 kSPS, 051 for 500 - kSPS and 101 for 1 MSPS) - - Examples : ADC081S101, ADC124S501, ... - - This driver can also be built as a module. If so, the module - will be called adcxx. - config SENSORS_ADM1021 tristate "Analog Devices ADM1021 and compatibles" depends on I2C help If you say yes here you get support for Analog Devices ADM1021 and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A, - Genesys Logic GL523SM, National Semiconductor LM84, TI THMC10, - and the XEON processor built-in sensor. + Genesys Logic GL523SM, National Semiconductor LM84 and TI THMC10. This driver can also be built as a module. If so, the module will be called adm1021. @@ -129,7 +135,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 +146,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 +157,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. @@ -170,9 +176,49 @@ config SENSORS_ADM9240 This driver can also be built as a module. If so, the module will be called adm9240. +config SENSORS_ADT7X10 + tristate + help + This module contains common code shared by the ADT7310/ADT7320 and + ADT7410/ADT7420 temperature monitoring chip drivers. + + If build as a module, the module will be called adt7x10. + +config SENSORS_ADT7310 + tristate "Analog Devices ADT7310/ADT7320" + depends on SPI_MASTER + select SENSORS_ADT7X10 + help + If you say yes here you get support for the Analog Devices + ADT7310 and ADT7320 temperature monitoring chips. + + This driver can also be built as a module. If so, the module + will be called adt7310. + +config SENSORS_ADT7410 + tristate "Analog Devices ADT7410/ADT7420" + depends on I2C + select SENSORS_ADT7X10 + help + If you say yes here you get support for the Analog Devices + ADT7410 and ADT7420 temperature monitoring chips. + + This driver can also be built as a module. If so, the module + will be called adt7410. + +config SENSORS_ADT7411 + tristate "Analog Devices ADT7411" + depends on I2C + help + If you say yes here you get support for the Analog Devices + ADT7411 voltage and temperature monitoring chip. + + This driver can also be built as a module. If so, the module + will be called adt7411. + config SENSORS_ADT7462 tristate "Analog Devices ADT7462" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for the Analog Devices ADT7462 temperature monitoring chips. @@ -182,7 +228,7 @@ config SENSORS_ADT7462 config SENSORS_ADT7470 tristate "Analog Devices ADT7470" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for the Analog Devices ADT7470 temperature monitoring chips. @@ -190,23 +236,9 @@ config SENSORS_ADT7470 This driver can also be built as a module. If so, the module will be called adt7470. -config SENSORS_ADT7473 - tristate "Analog Devices ADT7473 (DEPRECATED)" - depends on I2C && EXPERIMENTAL - select SENSORS_ADT7475 - help - If you say yes here you get support for the Analog Devices - ADT7473 temperature monitoring chips. - - This driver is deprecated, you should use the adt7475 driver - instead. - - This driver can also be built as a module. If so, the module - will be called adt7473. - 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 @@ -216,9 +248,22 @@ config SENSORS_ADT7475 This driver can also be build as a module. If so, the module will be called adt7475. +config SENSORS_ASC7621 + tristate "Andigilog aSC7621" + depends on I2C + help + If you say yes here you get support for the aSC7621 + family of SMBus sensors chip found on most Intel X38, X48, X58, + 945, 965 and 975 desktop boards. Currently supported chips: + aSC7621 + aSC7621a + + This driver can also be built as a module. If so, the module + will be called asc7621. + config SENSORS_K8TEMP tristate "AMD Athlon64/FX or Opteron temperature sensor" - depends on X86 && PCI && EXPERIMENTAL + depends on X86 && PCI help If you say yes here you get support for the temperature sensor(s) inside your CPU. Supported is whole AMD K8 @@ -229,46 +274,56 @@ config SENSORS_K8TEMP will be called k8temp. config SENSORS_K10TEMP - tristate "AMD Phenom/Sempron/Turion/Opteron temperature sensor" + tristate "AMD Family 10h+ temperature sensor" depends on X86 && PCI help If you say yes here you get support for the temperature sensor(s) inside your CPU. Supported are later revisions of - the AMD Family 10h and all revisions of the AMD Family 11h - microarchitectures. + the AMD Family 10h and all revisions of the AMD Family 11h, + 12h (Llano), 14h (Brazos), 15h (Bulldozer/Trinity/Kaveri) and + 16h (Kabini/Mullins) microarchitectures. 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 +config SENSORS_FAM15H_POWER + tristate "AMD Family 15h processor power" + depends on X86 && PCI help - Support for the motion sensor included in PowerBooks. Includes - implementations for PMU and I2C. + If you say yes here you get support for processor power + information of your AMD family 15h CPU. - This driver can also be built as a module. If so, the module - will be called ams. + This driver can also be built as a module. If so, the module + will be called fam15h_power. -config SENSORS_AMS_PMU - bool "PMU variant" - depends on SENSORS_AMS && ADB_PMU - default y +config SENSORS_APPLESMC + tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)" + depends on INPUT && X86 + select NEW_LEDS + select LEDS_CLASS + select INPUT_POLLDEV + default n help - PMU variant of motion sensor, found in late 2005 PowerBooks. + This driver provides support for the Apple System Management + Controller, which provides an accelerometer (Apple Sudden Motion + Sensor), light sensors, temperature sensors, keyboard backlight + control and fan control. -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. + Only Intel-based Apple's computers are supported (MacBook Pro, + MacBook, MacMini). + + Data from the different sensors, keyboard backlight control and fan + control are accessible via sysfs. + + This driver also provides an absolute input class device, allowing + the laptop to act as a pinball machine-esque joystick. + + Say Y here if you have an applicable laptop and want to experience + the awesome power of applesmc. config SENSORS_ASB100 tristate "Asus ASB100 Bach" - depends on X86 && I2C && EXPERIMENTAL + depends on X86 && I2C select HWMON_VID help If you say yes here you get support for the ASB100 Bach sensor @@ -279,7 +334,7 @@ config SENSORS_ASB100 config SENSORS_ATXP1 tristate "Attansic ATXP1 VID controller" - depends on I2C && EXPERIMENTAL + depends on I2C select HWMON_VID help If you say yes here you get support for the Attansic ATXP1 VID @@ -291,19 +346,54 @@ config SENSORS_ATXP1 This driver can also be built as a module. If so, the module will be called atxp1. -config SENSORS_DS1621 - tristate "Dallas Semiconductor DS1621 and DS1625" +config SENSORS_DS620 + tristate "Dallas Semiconductor DS620" depends on I2C help If you say yes here you get support for Dallas Semiconductor - DS1621 and DS1625 sensor chips. + DS620 sensor chip. + + This driver can also be built as a module. If so, the module + will be called ds620. + +config SENSORS_DS1621 + tristate "Dallas Semiconductor DS1621 and compatibles" + depends on I2C + help + If you say yes here you get support for Dallas Semiconductor/Maxim + Integrated DS1621 sensor chips and compatible models including: + + - Dallas Semiconductor DS1625 + - Maxim Integrated DS1631 + - Maxim Integrated DS1721 + - Maxim Integrated DS1731 This driver can also be built as a module. If so, the module will be called ds1621. +config SENSORS_DA9052_ADC + tristate "Dialog DA9052/DA9053 ADC" + depends on PMIC_DA9052 + help + Say y here to support the ADC found on Dialog Semiconductor + DA9052-BC and DA9053-AA/Bx PMICs. + + This driver can also be built as module. If so, the module + will be called da9052-hwmon. + +config SENSORS_DA9055 + tristate "Dialog Semiconductor DA9055 ADC" + depends on MFD_DA9055 + help + If you say yes here you get support for ADC on the Dialog + Semiconductor DA9055 PMIC. + + This driver can also be built as a module. If so, the module + will be called da9055-hwmon. + config SENSORS_I5K_AMB tristate "FB-DIMM AMB temperature sensor on Intel 5000 series chipsets" - depends on PCI && EXPERIMENTAL + depends on PCI help If you say yes here you get support for FB-DIMM AMB temperature monitoring chips on systems with the Intel 5000 series chipset. @@ -313,7 +403,7 @@ config SENSORS_I5K_AMB config SENSORS_F71805F tristate "Fintek F71805F/FG, F71806F/FG and F71872F/FG" - depends on EXPERIMENTAL + depends on !PPC help If you say yes here you get support for hardware monitoring features of the Fintek F71805F/FG, F71806F/FG and F71872F/FG @@ -323,26 +413,43 @@ config SENSORS_F71805F will be called f71805f. config SENSORS_F71882FG - tristate "Fintek F71858FG, F71862FG, F71882FG, F71889FG and F8000" - depends on EXPERIMENTAL + tristate "Fintek F71882FG and compatibles" + depends on !PPC help If you say yes here you get support for hardware monitoring - features of the Fintek F71858FG, F71862FG/71863FG, F71882FG/F71883FG, - F71889FG and F8000 Super-I/O chips. + features of many Fintek Super-I/O (LPC) chips. The currently + supported chips are: + F71808E/A + F71858FG + F71862FG + F71863FG + F71869F/E/A + F71882FG + F71883FG + F71889FG/ED/A + F8000 + F81801U + F81865F This driver can also be built as a module. If so, the module will be called f71882fg. config SENSORS_F75375S - tristate "Fintek F75375S/SP and F75373"; - depends on I2C && EXPERIMENTAL + tristate "Fintek F75375S/SP, F75373 and F75387" + depends on I2C help If you say yes here you get support for hardware monitoring - features of the Fintek F75375S/SP and F75373 + features of the Fintek F75375S/SP, F75373 and F75387 This driver can also be built as a module. If so, the module will be called f75375s. +config SENSORS_MC13783_ADC + tristate "Freescale MC13783/MC13892 ADC" + depends on MFD_MC13XXX + help + Support for the A/D converter on MC13783 and MC13892 PMIC. + config SENSORS_FSCHMD tristate "Fujitsu Siemens Computers sensor chips" depends on X86 && I2C @@ -359,16 +466,6 @@ config SENSORS_FSCHMD This driver can also be built as a module. If so, the module will be called fschmd. -config SENSORS_G760A - tristate "GMT G760A" - depends on I2C - help - If you say yes here you get support for Global Mixed-mode - Technology Inc G760A fan speed PWM controller chips. - - This driver can also be built as a module. If so, the module - will be called g760a. - config SENSORS_GL518SM tristate "Genesys Logic GL518SM" depends on I2C @@ -390,13 +487,44 @@ config SENSORS_GL520SM This driver can also be built as a module. If so, the module will be called gl520sm. -config SENSORS_CORETEMP - tristate "Intel Core/Core2/Atom temperature sensor" - depends on X86 && PCI && EXPERIMENTAL +config SENSORS_G760A + tristate "GMT G760A" + depends on I2C help - If you say yes here you get support for the temperature - sensor inside your CPU. Most of the family 6 CPUs - are supported. Check documentation/driver for details. + If you say yes here you get support for Global Mixed-mode + Technology Inc G760A fan speed PWM controller chips. + + This driver can also be built as a module. If so, the module + will be called g760a. + +config SENSORS_G762 + tristate "GMT G762 and G763" + depends on I2C + help + If you say yes here you get support for Global Mixed-mode + Technology Inc G762 and G763 fan speed PWM controller chips. + + This driver can also be built as a module. If so, the module + will be called g762. + +config SENSORS_GPIO_FAN + tristate "GPIO fan" + depends on GPIOLIB + 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_HIH6130 + tristate "Honeywell Humidicon HIH-6130 humidity/temperature sensor" + depends on I2C + help + If you say yes here you get support for Honeywell Humidicon + HIH-6130 and HIH-6131 Humidicon humidity sensors. + + This driver can also be built as a module. If so, the module + will be called hih6130. config SENSORS_IBMAEM tristate "IBM Active Energy Manager temperature/power sensors and control" @@ -426,36 +554,304 @@ config SENSORS_IBMPEX This driver can also be built as a module. If so, the module will be called ibmpex. +config SENSORS_IIO_HWMON + tristate "Hwmon driver that uses channels specified via iio maps" + depends on IIO + help + This is a platform driver that in combination with a suitable + map allows IIO devices to provide basic hwmon functionality + for those channels specified in the map. This map can be provided + either via platform data or the device tree bindings. + +config SENSORS_CORETEMP + tristate "Intel Core/Core2/Atom temperature sensor" + depends on X86 + help + If you say yes here you get support for the temperature + sensor inside your CPU. Most of the family 6 CPUs + are supported. Check Documentation/hwmon/coretemp for details. + config SENSORS_IT87 tristate "ITE IT87xx and compatibles" + depends on !PPC 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, IT8728F, IT8758E, + IT8771E, IT8772E, IT8782F, IT8783E/F and IT8603E sensor chips, + and the SiS950 clone. This driver can also be built as a module. If so, the module will be called it87. -config SENSORS_LM63 - tristate "National Semiconductor LM63" +config SENSORS_JZ4740 + tristate "Ingenic JZ4740 SoC ADC driver" + depends on MACH_JZ4740 && MFD_JZ4740_ADC + help + If you say yes here you get support for reading adc values from the ADCIN + pin on Ingenic JZ4740 SoC based boards. + + This driver can also be build as a module. If so, the module will be + called jz4740-hwmon. + +config SENSORS_JC42 + tristate "JEDEC JC42.4 compliant memory module temperature sensors" + depends on I2C + help + If you say yes here, you get support for JEDEC JC42.4 compliant + temperature sensors, which are used on many DDR3 memory modules for + mobile devices and servers. Support will include, but not be limited + to, ADT7408, AT30TS00, CAT34TS02, CAT6095, MAX6604, MCP9804, MCP9805, + MCP98242, MCP98243, MCP98244, MCP9843, SE97, SE98, STTS424(E), + STTS2002, STTS3000, TSE2002B3, TSE2002GB2, TS3000B3, and TS3000GB2. + + This driver can also be built as a module. If so, the module + will be called jc42. + +config SENSORS_LINEAGE + tristate "Lineage Compact Power Line Power Entry Module" + depends on I2C + help + If you say yes here you get support for the Lineage Compact Power Line + series of DC/DC and AC/DC converters such as CP1800, CP2000AC, + CP2000DC, CP2725, and others. + + This driver can also be built as a module. If so, the module + will be called lineage-pem. + +config SENSORS_LTC2945 + tristate "Linear Technology LTC2945" + depends on I2C + select REGMAP_I2C + default n + help + If you say yes here you get support for Linear Technology LTC2945 + I2C System Monitor. + + This driver can also be built as a module. If so, the module will + be called ltc2945. + +config SENSORS_LTC4151 + tristate "Linear Technology LTC4151" + depends on I2C + default n + help + If you say yes here you get support for Linear Technology LTC4151 + High Voltage I2C Current and Voltage Monitor interface. + + This driver can also be built as a module. If so, the module will + be called ltc4151. + +config SENSORS_LTC4215 + tristate "Linear Technology LTC4215" + depends on I2C + default n + help + If you say yes here you get support for Linear Technology LTC4215 + Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4215. + +config SENSORS_LTC4222 + tristate "Linear Technology LTC4222" + depends on I2C + select REGMAP_I2C + default n + help + If you say yes here you get support for Linear Technology LTC4222 + Dual Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4222. + +config SENSORS_LTC4245 + tristate "Linear Technology LTC4245" + depends on I2C + default n + help + If you say yes here you get support for Linear Technology LTC4245 + Multiple Supply Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4245. + +config SENSORS_LTC4260 + tristate "Linear Technology LTC4260" + depends on I2C + select REGMAP_I2C + default n + help + If you say yes here you get support for Linear Technology LTC4260 + Positive Voltage Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4260. + +config SENSORS_LTC4261 + tristate "Linear Technology LTC4261" + depends on I2C + 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_MAX1111 + tristate "Maxim MAX1111 Serial 8-bit ADC chip and compatibles" + depends on SPI_MASTER + help + Say y here to support Maxim's MAX1110, MAX1111, MAX1112, and MAX1113 + ADC chips. + + This driver can also be built as a module. If so, the module + will be called max1111. + +config SENSORS_MAX16065 + tristate "Maxim MAX16065 System Manager and compatibles" + depends on I2C + help + If you say yes here you get support for hardware monitoring + capabilities of the following Maxim System Manager chips. + MAX16065 + MAX16066 + MAX16067 + MAX16068 + MAX16070 + MAX16071 + + This driver can also be built as a module. If so, the module + will be called max16065. + +config SENSORS_MAX1619 + tristate "Maxim MAX1619 sensor chip" + depends on I2C + help + If you say yes here you get support for MAX1619 sensor chip. + + This driver can also be built as a module. If so, the module + will be called max1619. + +config SENSORS_MAX1668 + tristate "Maxim MAX1668 and compatibles" + depends on I2C + help + If you say yes here you get support for MAX1668, MAX1989 and + MAX1805 chips. + + This driver can also be built as a module. If so, the module + will be called max1668. + +config SENSORS_MAX197 + tristate "Maxim MAX197 and compatibles" + help + Support for the Maxim MAX197 A/D converter. + Support will include, but not be limited to, MAX197, and MAX199. + + This driver can also be built as a module. If so, the module + will be called max197. + +config SENSORS_MAX6639 + tristate "Maxim MAX6639 sensor chip" + depends on I2C + help + If you say yes here you get support for the MAX6639 + sensor chips. + + This driver can also be built as a module. If so, the module + will be called max6639. + +config SENSORS_MAX6642 + tristate "Maxim MAX6642 sensor chip" + depends on I2C + help + If you say yes here you get support for MAX6642 sensor chip. + MAX6642 is a SMBus-Compatible Remote/Local Temperature Sensor + with Overtemperature Alarm from Maxim. + + This driver can also be built as a module. If so, the module + will be called max6642. + +config SENSORS_MAX6650 + tristate "Maxim MAX6650 sensor chip" depends on I2C help + If you say yes here you get support for the MAX6650 / MAX6651 + sensor chips. + + This driver can also be built as a module. If so, the module + will be called max6650. + +config SENSORS_MAX6697 + tristate "Maxim MAX6697 and compatibles" + depends on I2C + help + If you say yes here you get support for MAX6581, MAX6602, MAX6622, + MAX6636, MAX6689, MAX6693, MAX6694, MAX6697, MAX6698, and MAX6699 + temperature sensor chips. + + This driver can also be built as a module. If so, the module + will be called max6697. + +config SENSORS_HTU21 + tristate "Measurement Specialties HTU21D humidity/temperature sensors" + depends on I2C + help + If you say yes here you get support for the Measurement Specialties + HTU21D humidity and temperature sensors. + + This driver can also be built as a module. If so, the module + will be called htu21. + +config SENSORS_MCP3021 + tristate "Microchip MCP3021 and compatibles" + depends on I2C + help + If you say yes here you get support for MCP3021 and MCP3221. + The MCP3021 is a A/D converter (ADC) with 10-bit and the MCP3221 + with 12-bit resolution. + + This driver can also be built as a module. If so, the module + will be called mcp3021. + +config SENSORS_ADCXX + tristate "National Semiconductor ADCxxxSxxx" + depends on SPI_MASTER + help If you say yes here you get support for the National Semiconductor - LM63 remote diode digital temperature sensor with integrated fan - control. Such chips are found on the Tyan S4882 (Thunder K8QS Pro) - motherboard, among others. + ADC<bb><c>S<sss> chip family, where + * bb is the resolution in number of bits (8, 10, 12) + * c is the number of channels (1, 2, 4, 8) + * sss is the maximum conversion speed (021 for 200 kSPS, 051 for 500 + kSPS and 101 for 1 MSPS) + + Examples : ADC081S101, ADC124S501, ... + + This driver can also be built as a module. If so, the module + will be called adcxx. + +config SENSORS_LM63 + tristate "National Semiconductor LM63 and compatibles" + depends on I2C + help + If you say yes here you get support for the National + Semiconductor LM63, LM64, and LM96163 remote diode digital temperature + sensors with integrated fan control. Such chips are found + on the Tyan S4882 (Thunder K8QS Pro) motherboard, among + others. This driver can also be built as a module. If so, the module will be called lm63. config SENSORS_LM70 - tristate "National Semiconductor LM70 / Texas Instruments TMP121" - depends on SPI_MASTER && EXPERIMENTAL + tristate "National Semiconductor LM70 and compatibles" + 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 - sensor chips. + LM70, LM71, LM74 and Texas Instruments TMP121/TMP123 digital tempera- + ture sensor chips. This driver can also be built as a module. If so, the module will be called lm70. @@ -472,18 +868,22 @@ config SENSORS_LM73 config SENSORS_LM75 tristate "National Semiconductor LM75 and compatibles" depends on I2C + depends on THERMAL || !THERMAL_OF help If you say yes here you get support for one common type of temperature sensor chip, with models including: - - Dallas Semiconductor DS75 and DS1775 + - Analog Devices ADT75 + - Dallas Semiconductor DS75, DS1775 and DS7505 + - Global Mixed-mode Technology (GMT) G751 - Maxim MAX6625 and MAX6626 - Microchip MCP980x - - National Semiconductor LM75 + - National Semiconductor LM75, LM75A - NXP's LM75A - ST Microelectronics STDS75 - TelCom (now Microchip) TCN75 - - Texas Instruments TMP100, TMP101, TMP75, TMP175, TMP275 + - Texas Instruments TMP100, TMP101, TMP105, TMP75, TMP175, + TMP275 This driver supports driver model based binding through board specific I2C device tables. @@ -517,11 +917,11 @@ config SENSORS_LM78 will be called lm78. config SENSORS_LM80 - tristate "National Semiconductor LM80" - depends on I2C && EXPERIMENTAL + tristate "National Semiconductor LM80 and LM96080" + depends on I2C help If you say yes here you get support for National Semiconductor - LM80 sensor chips. + LM80 and LM96080 sensor chips. This driver can also be built as a module. If so, the module will be called lm80. @@ -538,11 +938,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, EMC6D102, and EMC6D103. This driver can also be built as a module. If so, the module will be called lm85. @@ -563,9 +964,11 @@ config SENSORS_LM90 depends on I2C help If you say yes here you get support for National Semiconductor LM90, - LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, and Maxim - MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659, - MAX6680, MAX6681 and MAX6692 sensor chips. + LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A, + Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659, + MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008, + Winbond/Nuvoton W83L771W/G/AWG/ASG, Philips SA56004, and GMT G781 + sensor chips. This driver can also be built as a module. If so, the module will be called lm90. @@ -585,73 +988,44 @@ config SENSORS_LM93 depends on I2C select HWMON_VID help - If you say yes here you get support for National Semiconductor LM93 - sensor chips. + If you say yes here you get support for National Semiconductor LM93, + LM94, and compatible sensor chips. This driver can also be built as a module. If so, the module will be called lm93. -config SENSORS_LTC4215 - tristate "Linear Technology LTC4215" - depends on I2C && EXPERIMENTAL - default n - help - If you say yes here you get support for Linear Technology LTC4215 - Hot Swap Controller I2C interface. - - This driver can also be built as a module. If so, the module will - be called ltc4215. - -config SENSORS_LTC4245 - tristate "Linear Technology LTC4245" - depends on I2C && EXPERIMENTAL - default n +config SENSORS_LM95234 + tristate "National Semiconductor LM95234" + depends on I2C help - If you say yes here you get support for Linear Technology LTC4245 - Multiple Supply Hot Swap Controller I2C interface. + If you say yes here you get support for the LM95234 temperature + sensor. - This driver can also be built as a module. If so, the module will - be called ltc4245. + This driver can also be built as a module. If so, the module + will be called lm95234. config SENSORS_LM95241 - tristate "National Semiconductor LM95241 sensor chip" + tristate "National Semiconductor LM95241 and compatibles" depends on I2C help - If you say yes here you get support for LM95241 sensor chip. + If you say yes here you get support for LM95231 and LM95241 sensor + chips. This driver can also be built as a module. If so, the module will be called lm95241. -config SENSORS_MAX1111 - tristate "Maxim MAX1111 Multichannel, Serial 8-bit ADC chip" - depends on SPI_MASTER - help - Say y here to support Maxim's MAX1111 ADC chips. - - This driver can also be built as a module. If so, the module - will be called max1111. - -config SENSORS_MAX1619 - tristate "Maxim MAX1619 sensor chip" +config SENSORS_LM95245 + tristate "National Semiconductor LM95245 sensor chip" depends on I2C help - If you say yes here you get support for MAX1619 sensor chip. + If you say yes here you get support for LM95245 sensor chip. This driver can also be built as a module. If so, the module - will be called max1619. - -config SENSORS_MAX6650 - tristate "Maxim MAX6650 sensor chip" - depends on I2C && EXPERIMENTAL - help - If you say yes here you get support for the MAX6650 / MAX6651 - sensor chips. - - This driver can also be built as a module. If so, the module - will be called max6650. + will be called lm95245. config SENSORS_PC87360 tristate "National Semiconductor PC87360 family" + depends on !PPC select HWMON_VID help If you say yes here you get access to the hardware monitoring @@ -665,17 +1039,56 @@ config SENSORS_PC87360 config SENSORS_PC87427 tristate "National Semiconductor PC87427" - depends on EXPERIMENTAL + depends on !PPC help If you say yes here you get access to the hardware monitoring functions of the National Semiconductor PC87427 Super-I/O chip. The chip has two distinct logical devices, one for fan speed monitoring and control, and one for voltage and temperature - monitoring. Only fan speed monitoring is supported right now. + monitoring. Fan speed monitoring and control are supported, as + well as temperature monitoring. Voltages aren't supported yet. This driver can also be built as a module. If so, the module will be called pc87427. +config SENSORS_NTC_THERMISTOR + tristate "NTC thermistor support from Murata" + depends on !OF || IIO=n || IIO + help + This driver supports NTC thermistors sensor reading and its + interpretation. The driver can also monitor the temperature and + send notifications about the temperature. + + Currently, this driver supports + NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, and NCP15WL333 + from Murata. + + This driver can also be built as a module. If so, the module + will be called ntc-thermistor. + +config SENSORS_NCT6683 + tristate "Nuvoton NCT6683D" + depends on !PPC + help + If you say yes here you get support for the hardware monitoring + functionality of the Nuvoton NCT6683D eSIO chip. + + This driver can also be built as a module. If so, the module + will be called nct6683. + +config SENSORS_NCT6775 + tristate "Nuvoton NCT6775F and compatibles" + depends on !PPC + select HWMON_VID + help + If you say yes here you get support for the hardware monitoring + functionality of the Nuvoton NCT6775F, NCT6776F, NCT6779D + and compatible Super-I/O chips. This driver replaces the + w83627ehf driver for NCT6775F and NCT6776F. + + This driver can also be built as a module. If so, the module + will be called nct6775. + config SENSORS_PCF8591 tristate "Philips PCF8591 ADC/DAC" depends on I2C @@ -690,9 +1103,11 @@ config SENSORS_PCF8591 These devices are hard to detect and rarely found on mainstream hardware. If unsure, say N. +source drivers/hwmon/pmbus/Kconfig + config SENSORS_SHT15 tristate "Sensiron humidity and temperature sensors. SHT15 and compat." - depends on GENERIC_GPIO + depends on GPIOLIB help If you say yes here you get support for the Sensiron SHT10, SHT11, SHT15, SHT71, SHT75 humidity and temperature sensors. @@ -700,15 +1115,35 @@ config SENSORS_SHT15 This driver can also be built as a module. If so, the module will be called sht15. +config SENSORS_SHT21 + tristate "Sensiron humidity and temperature sensors. SHT21 and compat." + depends on I2C + help + If you say yes here you get support for the Sensiron SHT21, SHT25 + humidity and temperature sensors. + + This driver can also be built as a module. If so, the module + will be called sht21. + +config SENSORS_SHTC1 + tristate "Sensiron humidity and temperature sensors. SHTC1 and compat." + depends on I2C + help + If you say yes here you get support for the Sensiron SHTC1 and SHTW1 + humidity and temperature sensors. + + This driver can also be built as a module. If so, the module + will be called shtc1. + 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" @@ -729,7 +1164,7 @@ config SENSORS_SIS5595 config SENSORS_DME1737 tristate "SMSC DME1737, SCH311x and compatibles" - depends on I2C && EXPERIMENTAL + depends on I2C && !PPC select HWMON_VID help If you say yes here you get support for the hardware monitoring @@ -739,8 +1174,40 @@ config SENSORS_DME1737 This driver can also be built as a module. If so, the module will be called dme1737. +config SENSORS_EMC1403 + tristate "SMSC EMC1403/23 thermal sensor" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for the SMSC EMC1403/23 + temperature monitoring chip. + + Threshold values can be configured using sysfs. + Data from the different diodes are accessible via sysfs. + +config SENSORS_EMC2103 + tristate "SMSC EMC2103" + depends on I2C + help + If you say yes here you get support for the temperature + and fan sensors of the SMSC EMC2103 chips. + + This driver can also be built as a module. If so, the module + will be called emc2103. + +config SENSORS_EMC6W201 + tristate "SMSC EMC6W201" + depends on I2C + help + If you say yes here you get support for the SMSC EMC6W201 + hardware monitoring chip. + + This driver can also be built as a module. If so, the module + will be called emc6w201. + config SENSORS_SMSC47M1 tristate "SMSC LPC47M10x and compatibles" + depends on !PPC help If you say yes here you get support for the integrated fan monitoring and control capabilities of the SMSC LPC47B27x, @@ -757,7 +1224,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 @@ -774,7 +1241,7 @@ config SENSORS_SMSC47M192 config SENSORS_SMSC47B397 tristate "SMSC LPC47B397-NC" - depends on EXPERIMENTAL + depends on !PPC help If you say yes here you get support for the SMSC LPC47B397-NC sensor chip. @@ -782,19 +1249,99 @@ config SENSORS_SMSC47B397 This driver can also be built as a module. If so, the module will be called smsc47b397. +config SENSORS_SCH56XX_COMMON + tristate + default n + +config SENSORS_SCH5627 + tristate "SMSC SCH5627" + depends on !PPC && WATCHDOG + select SENSORS_SCH56XX_COMMON + select WATCHDOG_CORE + help + If you say yes here you get support for the hardware monitoring + features of the SMSC SCH5627 Super-I/O chip including support for + the integrated watchdog. + + This driver can also be built as a module. If so, the module + will be called sch5627. + +config SENSORS_SCH5636 + tristate "SMSC SCH5636" + depends on !PPC && WATCHDOG + select SENSORS_SCH56XX_COMMON + select WATCHDOG_CORE + help + SMSC SCH5636 Super I/O chips include an embedded microcontroller for + hardware monitoring solutions, allowing motherboard manufacturers to + create their own custom hwmon solution based upon the SCH5636. + + Currently this driver only supports the Fujitsu Theseus SCH5636 based + hwmon solution. Say yes here if you want support for the Fujitsu + Theseus' hardware monitoring features including support for the + integrated watchdog. + + This driver can also be built as a module. If so, the module + will be called sch5636. + +config SENSORS_SMM665 + tristate "Summit Microelectronics SMM665" + depends on I2C + default n + help + If you say yes here you get support for the hardware monitoring + features of the Summit Microelectronics SMM665/SMM665B Six-Channel + Active DC Output Controller / Monitor. + + Other supported chips are SMM465, SMM665C, SMM764, and SMM766. + Support for those chips is untested. + + This driver can also be built as a module. If so, the module will + be called smm665. + +config SENSORS_ADC128D818 + tristate "Texas Instruments ADC128D818" + depends on I2C + help + If you say yes here you get support for the Texas Instruments + ADC128D818 System Monitor with Temperature Sensor chip. + + This driver can also be built as a module. If so, the module + will be called adc128d818. + +config SENSORS_ADS1015 + tristate "Texas Instruments ADS1015" + depends on I2C + help + If you say yes here you get support for Texas Instruments + ADS1015/ADS1115 12/16-bit 4-input ADC device. + + This driver can also be built as a module. If so, the module + will be called ads1015. + config SENSORS_ADS7828 - tristate "Texas Instruments ADS7828" + tristate "Texas Instruments ADS7828 and compatibles" depends on I2C help - If you say yes here you get support for Texas Instruments ADS7828 - 12-bit 8-channel ADC device. + If you say yes here you get support for Texas Instruments ADS7828 and + ADS7830 8-channel A/D converters. ADS7828 resolution is 12-bit, while + it is 8-bit on ADS7830. This driver can also be built as a module. If so, the module will be called ads7828. +config SENSORS_ADS7871 + tristate "Texas Instruments ADS7871 A/D converter" + depends on SPI + help + If you say yes here you get support for TI ADS7871 & ADS7870 + + This driver can also be built as a module. If so, the module + will be called ads7871. + config SENSORS_AMC6821 tristate "Texas Instruments AMC6821" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for the Texas Instruments AMC6821 hardware monitoring chips. @@ -802,9 +1349,32 @@ config SENSORS_AMC6821 This driver can also be build as a module. If so, the module will be called amc6821. +config SENSORS_INA209 + tristate "TI / Burr Brown INA209" + depends on I2C + help + If you say yes here you get support for the TI / Burr Brown INA209 + voltage / current / power monitor I2C interface. + + This driver can also be built as a module. If so, the module will + be called ina209. + +config SENSORS_INA2XX + tristate "Texas Instruments INA219 and compatibles" + depends on I2C + help + If you say yes here you get support for INA219, INA220, INA226, and + INA230 power monitor chips. + + The INA2xx driver is configured for the default configuration of + the part as described in the datasheet. + Default value for Rshunt is 10 mOhms. + This driver can also be built as a module. If so, the module + will be called ina2xx. + 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. @@ -812,19 +1382,30 @@ config SENSORS_THMC50 This driver can also be built as a module. If so, the module will be called thmc50. +config SENSORS_TMP102 + tristate "Texas Instruments TMP102" + depends on I2C + depends on THERMAL || !THERMAL_OF + help + If you say yes here you get support for Texas Instruments TMP102 + sensor chips. + + This driver can also be built as a module. If so, the module + will be called tmp102. + config SENSORS_TMP401 tristate "Texas Instruments TMP401 and compatibles" - depends on I2C && EXPERIMENTAL + depends on I2C help - If you say yes here you get support for Texas Instruments TMP401 and - TMP411 temperature sensor chips. + If you say yes here you get support for Texas Instruments TMP401, + TMP411, TMP431, and TMP432 temperature sensor chips. This driver can also be built as a module. If so, the module will be called tmp401. config SENSORS_TMP421 tristate "Texas Instruments TMP421 and compatible" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for Texas Instruments TMP421, TMP422 and TMP423 temperature sensor chips. @@ -832,9 +1413,28 @@ config SENSORS_TMP421 This driver can also be built as a module. If so, the module will be called tmp421. +config SENSORS_TWL4030_MADC + tristate "Texas Instruments TWL4030 MADC Hwmon" + depends on TWL4030_MADC + help + If you say yes here you get hwmon support for triton + TWL4030-MADC. + + This driver can also be built as a module. If so it will be called + twl4030-madc-hwmon. + +config SENSORS_VEXPRESS + tristate "Versatile Express" + depends on VEXPRESS_CONFIG + help + This driver provides support for hardware sensors available on + the ARM Ltd's Versatile Express platform. It can provide wide + range of information like temperature, power, energy. + config SENSORS_VIA_CPUTEMP tristate "VIA CPU temperature sensor" depends on X86 + select HWMON_VID help If you say yes here you get support for the temperature sensor inside your CPU. Supported are all known variants of @@ -852,7 +1452,7 @@ config SENSORS_VIA686A config SENSORS_VT1211 tristate "VIA VT1211" - depends on EXPERIMENTAL + depends on !PPC select HWMON_VID help If you say yes here then you get support for hardware monitoring @@ -896,7 +1496,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. @@ -905,18 +1505,47 @@ config SENSORS_W83792D config SENSORS_W83793 tristate "Winbond W83793" - depends on I2C && EXPERIMENTAL + depends on I2C select HWMON_VID help If you say yes here you get support for the Winbond W83793 - hardware monitoring chip. + hardware monitoring chip, including support for the integrated + watchdog. 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 + help + If you say yes here you get support for the Winbond W83795G and + W83795ADG hardware monitoring chip, including manual fan speed + control. + + This driver can also be built as a module. If so, the module + will be called w83795. + +config SENSORS_W83795_FANCTRL + boolean "Include automatic fan control support (DANGEROUS)" + depends on SENSORS_W83795 + default n + help + If you say yes here, support for automatic fan speed control + 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 + depends on I2C help If you say yes here you get support for the Winbond W83L785TS-S sensor chip, which is used on the Asus A7N8X, among other @@ -927,7 +1556,7 @@ config SENSORS_W83L785TS config SENSORS_W83L786NG tristate "Winbond W83L786NG, W83L786NR" - depends on I2C && EXPERIMENTAL + depends on I2C help If you say yes here you get support for the Winbond W83L786NG and W83L786NR sensor chips. @@ -937,6 +1566,7 @@ config SENSORS_W83L786NG config SENSORS_W83627HF tristate "Winbond W83627HF, W83627THF, W83637HF, W83687THF, W83697HF" + depends on !PPC select HWMON_VID help If you say yes here you get support for the Winbond W836X7 series @@ -947,7 +1577,8 @@ config SENSORS_W83627HF will be called w83627hf. config SENSORS_W83627EHF - tristate "Winbond W83627EHF/EHG/DHG, W83667HG" + tristate "Winbond W83627EHF/EHG/DHG/UHG, W83667HG, NCT6775F, NCT6776F" + depends on !PPC select HWMON_VID help If you say yes here you get support for the hardware @@ -956,9 +1587,11 @@ config SENSORS_W83627EHF This driver also supports the W83627EHG, which is the lead-free version of the W83627EHF, and the W83627DHG, which is a similar chip suited for specific Intel processors that use PECI such as - the Core 2 Duo. + the Core 2 Duo. And also the W83627UHG, which is a stripped down + version of the W83627DHG (as far as hardware monitoring goes.) - This driver also supports the W83667HG chip. + This driver also supports Nuvoton W83667HG, W83667HG-B, NCT6775F + (also known as W83667HG-I), and NCT6776F. This driver can also be built as a module. If so, the module will be called w83627ehf. @@ -991,98 +1624,23 @@ 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 - select INPUT_POLLDEV - default n - help - This driver provides support for the LIS3LV02Dx accelerometer connected - via SPI. The accelerometer data is readable via - /sys/devices/platform/lis3lv02d. - - This driver also provides an absolute input class device, allowing - the laptop to act as a pinball machine-esque joystick. - - This driver can also be built as modules. If so, the core module - will be called lis3lv02d and a specific module for the SPI transport - is called lis3lv02d_spi. - -config SENSORS_LIS3_I2C - tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer (I2C)" - depends on I2C && INPUT - select INPUT_POLLDEV - default n - help - This driver provides support for the LIS3LV02Dx accelerometer connected - via I2C. The accelerometer data is readable via - /sys/devices/platform/lis3lv02d. - - This driver also provides an absolute input class device, allowing - the device to act as a pinball machine-esque joystick. +if ACPI - This driver can also be built as modules. If so, the core module - will be called lis3lv02d and a specific module for the I2C transport - is called lis3lv02d_i2c. +comment "ACPI drivers" -config SENSORS_APPLESMC - tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)" - depends on INPUT && X86 - select NEW_LEDS - select LEDS_CLASS - select INPUT_POLLDEV - default n +config SENSORS_ACPI_POWER + tristate "ACPI 4.0 power meter" help - This driver provides support for the Apple System Management - Controller, which provides an accelerometer (Apple Sudden Motion - Sensor), light sensors, temperature sensors, keyboard backlight - control and fan control. - - Only Intel-based Apple's computers are supported (MacBook Pro, - MacBook, MacMini). - - Data from the different sensors, keyboard backlight control and fan - control are accessible via sysfs. - - This driver also provides an absolute input class device, allowing - the laptop to act as a pinball machine-esque joystick. - - Say Y here if you have an applicable laptop and want to experience - the awesome power of applesmc. - -config SENSORS_MC13783_ADC - tristate "Freescale MC13783 ADC" - depends on MFD_MC13783 - help - Support for the A/D converter on MC13783 PMIC. + This driver exposes ACPI 4.0 power meters as hardware monitoring + devices. Say Y (or M) if you have a computer with ACPI 4.0 firmware + and a power meter. -if ACPI - -comment "ACPI drivers" + To compile this driver as a module, choose M here: + the module will be called acpi_power_meter. config SENSORS_ATK0110 tristate "ASUS ATK0110" - depends on X86 && EXPERIMENTAL + depends on X86 help If you say yes here you get support for the ACPI hardware monitoring interface found in many ASUS motherboards. This @@ -1092,36 +1650,6 @@ config SENSORS_ATK0110 This driver can also be built as a module. If so, the module will be called asus_atk0110. -config SENSORS_LIS3LV02D - tristate "STMicroeletronics LIS3* three-axis digital accelerometer" - depends on INPUT - select INPUT_POLLDEV - select NEW_LEDS - select LEDS_CLASS - default n - help - This driver provides support for the LIS3* accelerometers, such as the - LIS3LV02DL or the LIS331DL. In particular, it can be found in a number - of HP laptops, which have the "Mobile Data Protection System 3D" or - "3D DriveGuard" feature. On such systems the driver should load - automatically (via ACPI alias). The accelerometer might also be found - in other systems, connected via SPI or I2C. The accelerometer data is - readable via /sys/devices/platform/lis3lv02d. - - This driver also provides an absolute input class device, allowing - a laptop to act as a pinball machine-esque joystick. It provides also - a misc device which can be used to detect free-fall. On HP laptops, - if the led infrastructure is activated, support for a led indicating - disk protection will be provided as hp::hddprotect. For more - information on the feature, refer to Documentation/hwmon/lis3lv02d. - - This driver can also be built as modules. If so, the core module - will be called lis3lv02d and a specific module for HP laptops will be - called hp_accel. - - Say Y here if you have an applicable laptop and want to experience - the awesome power of lis3lv02d. - endif # ACPI endif # HWMON diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 4bc215c0953..3dc0f02f71d 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_HWMON) += hwmon.o obj-$(CONFIG_HWMON_VID) += hwmon-vid.o # APCI drivers +obj-$(CONFIG_SENSORS_ACPI_POWER) += acpi_power_meter.o obj-$(CONFIG_SENSORS_ATK0110) += asus_atk0110.o # Native drivers @@ -14,13 +15,17 @@ 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 +obj-$(CONFIG_SENSORS_AB8500) += abx500.o ab8500.o obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o +obj-$(CONFIG_SENSORS_AD7314) += ad7314.o obj-$(CONFIG_SENSORS_AD7414) += ad7414.o obj-$(CONFIG_SENSORS_AD7418) += ad7418.o +obj-$(CONFIG_SENSORS_ADC128D818) += adc128d818.o obj-$(CONFIG_SENSORS_ADCXX) += adcxx.o obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o @@ -28,35 +33,53 @@ obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o obj-$(CONFIG_SENSORS_ADM1029) += adm1029.o obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o +obj-$(CONFIG_SENSORS_ADS1015) += ads1015.o obj-$(CONFIG_SENSORS_ADS7828) += ads7828.o +obj-$(CONFIG_SENSORS_ADS7871) += ads7871.o +obj-$(CONFIG_SENSORS_ADT7X10) += adt7x10.o +obj-$(CONFIG_SENSORS_ADT7310) += adt7310.o +obj-$(CONFIG_SENSORS_ADT7410) += adt7410.o +obj-$(CONFIG_SENSORS_ADT7411) += adt7411.o obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o -obj-$(CONFIG_SENSORS_ADT7473) += adt7473.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 +obj-$(CONFIG_SENSORS_DA9052_ADC)+= da9052-hwmon.o +obj-$(CONFIG_SENSORS_DA9055)+= da9055-hwmon.o obj-$(CONFIG_SENSORS_DME1737) += dme1737.o +obj-$(CONFIG_SENSORS_DS620) += ds620.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o +obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o +obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o +obj-$(CONFIG_SENSORS_EMC6W201) += emc6w201.o obj-$(CONFIG_SENSORS_F71805F) += f71805f.o obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o obj-$(CONFIG_SENSORS_F75375S) += f75375s.o +obj-$(CONFIG_SENSORS_FAM15H_POWER) += fam15h_power.o obj-$(CONFIG_SENSORS_FSCHMD) += fschmd.o obj-$(CONFIG_SENSORS_G760A) += g760a.o +obj-$(CONFIG_SENSORS_G762) += g762.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o +obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o +obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o +obj-$(CONFIG_SENSORS_HTU21) += htu21.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 +obj-$(CONFIG_SENSORS_IIO_HWMON) += iio_hwmon.o +obj-$(CONFIG_SENSORS_INA209) += ina209.o +obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o obj-$(CONFIG_SENSORS_IT87) += it87.o +obj-$(CONFIG_SENSORS_JC42) += jc42.o +obj-$(CONFIG_SENSORS_JZ4740) += jz4740-hwmon.o obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o -obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o -obj-$(CONFIG_SENSORS_LIS3_SPI) += lis3lv02d.o lis3lv02d_spi.o -obj-$(CONFIG_SENSORS_LIS3_I2C) += lis3lv02d.o lis3lv02d_i2c.o +obj-$(CONFIG_SENSORS_LINEAGE) += lineage-pem.o obj-$(CONFIG_SENSORS_LM63) += lm63.o obj-$(CONFIG_SENSORS_LM70) += lm70.o obj-$(CONFIG_SENSORS_LM73) += lm73.o @@ -70,26 +93,52 @@ obj-$(CONFIG_SENSORS_LM87) += lm87.o obj-$(CONFIG_SENSORS_LM90) += lm90.o obj-$(CONFIG_SENSORS_LM92) += lm92.o obj-$(CONFIG_SENSORS_LM93) += lm93.o +obj-$(CONFIG_SENSORS_LM95234) += lm95234.o obj-$(CONFIG_SENSORS_LM95241) += lm95241.o +obj-$(CONFIG_SENSORS_LM95245) += lm95245.o +obj-$(CONFIG_SENSORS_LTC2945) += ltc2945.o +obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o +obj-$(CONFIG_SENSORS_LTC4222) += ltc4222.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o +obj-$(CONFIG_SENSORS_LTC4260) += ltc4260.o +obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o +obj-$(CONFIG_SENSORS_MAX16065) += max16065.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o +obj-$(CONFIG_SENSORS_MAX1668) += max1668.o +obj-$(CONFIG_SENSORS_MAX197) += max197.o +obj-$(CONFIG_SENSORS_MAX6639) += max6639.o +obj-$(CONFIG_SENSORS_MAX6642) += max6642.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o +obj-$(CONFIG_SENSORS_MAX6697) += max6697.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o +obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o +obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o +obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o +obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o +obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o +obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o +obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o obj-$(CONFIG_SENSORS_SHT15) += sht15.o +obj-$(CONFIG_SENSORS_SHT21) += sht21.o +obj-$(CONFIG_SENSORS_SHTC1) += shtc1.o obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o +obj-$(CONFIG_SENSORS_SMM665) += smm665.o obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o obj-$(CONFIG_SENSORS_THMC50) += thmc50.o +obj-$(CONFIG_SENSORS_TMP102) += tmp102.o obj-$(CONFIG_SENSORS_TMP401) += tmp401.o obj-$(CONFIG_SENSORS_TMP421) += tmp421.o +obj-$(CONFIG_SENSORS_TWL4030_MADC)+= twl4030-madc-hwmon.o +obj-$(CONFIG_SENSORS_VEXPRESS) += vexpress.o obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o obj-$(CONFIG_SENSORS_VIA686A) += via686a.o obj-$(CONFIG_SENSORS_VT1211) += vt1211.o @@ -100,7 +149,7 @@ obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o -ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y) -EXTRA_CFLAGS += -DDEBUG -endif +obj-$(CONFIG_PMBUS) += pmbus/ + +ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG diff --git a/drivers/hwmon/ab8500.c b/drivers/hwmon/ab8500.c new file mode 100644 index 00000000000..d844dc80685 --- /dev/null +++ b/drivers/hwmon/ab8500.c @@ -0,0 +1,206 @@ +/* + * Copyright (C) ST-Ericsson 2010 - 2013 + * Author: Martin Persson <martin.persson@stericsson.com> + * Hongbo Zhang <hongbo.zhang@linaro.org> + * License Terms: GNU General Public License v2 + * + * When the AB8500 thermal warning temperature is reached (threshold cannot + * be changed by SW), an interrupt is set, and if no further action is taken + * within a certain time frame, pm_power off will be called. + * + * When AB8500 thermal shutdown temperature is reached a hardware shutdown of + * the AB8500 will occur. + */ + +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/mfd/abx500.h> +#include <linux/mfd/abx500/ab8500-bm.h> +#include <linux/mfd/abx500/ab8500-gpadc.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/power/ab8500.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include "abx500.h" + +#define DEFAULT_POWER_OFF_DELAY (HZ * 10) +#define THERMAL_VCC 1800 +#define PULL_UP_RESISTOR 47000 +/* Number of monitored sensors should not greater than NUM_SENSORS */ +#define NUM_MONITORED_SENSORS 4 + +struct ab8500_gpadc_cfg { + const struct abx500_res_to_temp *temp_tbl; + int tbl_sz; + int vcc; + int r_up; +}; + +struct ab8500_temp { + struct ab8500_gpadc *gpadc; + struct ab8500_btemp *btemp; + struct delayed_work power_off_work; + struct ab8500_gpadc_cfg cfg; + struct abx500_temp *abx500_data; +}; + +/* + * The hardware connection is like this: + * VCC----[ R_up ]-----[ NTC ]----GND + * where R_up is pull-up resistance, and GPADC measures voltage on NTC. + * and res_to_temp table is strictly sorted by falling resistance values. + */ +static int ab8500_voltage_to_temp(struct ab8500_gpadc_cfg *cfg, + int v_ntc, int *temp) +{ + int r_ntc, i = 0, tbl_sz = cfg->tbl_sz; + const struct abx500_res_to_temp *tbl = cfg->temp_tbl; + + if (cfg->vcc < 0 || v_ntc >= cfg->vcc) + return -EINVAL; + + r_ntc = v_ntc * cfg->r_up / (cfg->vcc - v_ntc); + if (r_ntc > tbl[0].resist || r_ntc < tbl[tbl_sz - 1].resist) + return -EINVAL; + + while (!(r_ntc <= tbl[i].resist && r_ntc > tbl[i + 1].resist) && + i < tbl_sz - 2) + i++; + + /* return milli-Celsius */ + *temp = tbl[i].temp * 1000 + ((tbl[i + 1].temp - tbl[i].temp) * 1000 * + (r_ntc - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist); + + return 0; +} + +static int ab8500_read_sensor(struct abx500_temp *data, u8 sensor, int *temp) +{ + int voltage, ret; + struct ab8500_temp *ab8500_data = data->plat_data; + + if (sensor == BAT_CTRL) { + *temp = ab8500_btemp_get_batctrl_temp(ab8500_data->btemp); + } else if (sensor == BTEMP_BALL) { + *temp = ab8500_btemp_get_temp(ab8500_data->btemp); + } else { + voltage = ab8500_gpadc_convert(ab8500_data->gpadc, sensor); + if (voltage < 0) + return voltage; + + ret = ab8500_voltage_to_temp(&ab8500_data->cfg, voltage, temp); + if (ret < 0) + return ret; + } + + return 0; +} + +static void ab8500_thermal_power_off(struct work_struct *work) +{ + struct ab8500_temp *ab8500_data = container_of(work, + struct ab8500_temp, power_off_work.work); + struct abx500_temp *abx500_data = ab8500_data->abx500_data; + + dev_warn(&abx500_data->pdev->dev, "Power off due to critical temp\n"); + + pm_power_off(); +} + +static ssize_t ab8500_show_name(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + return sprintf(buf, "ab8500\n"); +} + +static ssize_t ab8500_show_label(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + char *label; + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int index = attr->index; + + switch (index) { + case 1: + label = "ext_adc1"; + break; + case 2: + label = "ext_adc2"; + break; + case 3: + label = "bat_temp"; + break; + case 4: + label = "bat_ctrl"; + break; + default: + return -EINVAL; + } + + return sprintf(buf, "%s\n", label); +} + +static int ab8500_temp_irq_handler(int irq, struct abx500_temp *data) +{ + struct ab8500_temp *ab8500_data = data->plat_data; + + dev_warn(&data->pdev->dev, "Power off in %d s\n", + DEFAULT_POWER_OFF_DELAY / HZ); + + schedule_delayed_work(&ab8500_data->power_off_work, + DEFAULT_POWER_OFF_DELAY); + return 0; +} + +int abx500_hwmon_init(struct abx500_temp *data) +{ + struct ab8500_temp *ab8500_data; + + ab8500_data = devm_kzalloc(&data->pdev->dev, sizeof(*ab8500_data), + GFP_KERNEL); + if (!ab8500_data) + return -ENOMEM; + + ab8500_data->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); + if (IS_ERR(ab8500_data->gpadc)) + return PTR_ERR(ab8500_data->gpadc); + + ab8500_data->btemp = ab8500_btemp_get(); + if (IS_ERR(ab8500_data->btemp)) + return PTR_ERR(ab8500_data->btemp); + + INIT_DELAYED_WORK(&ab8500_data->power_off_work, + ab8500_thermal_power_off); + + ab8500_data->cfg.vcc = THERMAL_VCC; + ab8500_data->cfg.r_up = PULL_UP_RESISTOR; + ab8500_data->cfg.temp_tbl = ab8500_temp_tbl_a_thermistor; + ab8500_data->cfg.tbl_sz = ab8500_temp_tbl_a_size; + + data->plat_data = ab8500_data; + + /* + * ADC_AUX1 and ADC_AUX2, connected to external NTC + * BTEMP_BALL and BAT_CTRL, fixed usage + */ + data->gpadc_addr[0] = ADC_AUX1; + data->gpadc_addr[1] = ADC_AUX2; + data->gpadc_addr[2] = BTEMP_BALL; + data->gpadc_addr[3] = BAT_CTRL; + data->monitored_sensors = NUM_MONITORED_SENSORS; + + data->ops.read_sensor = ab8500_read_sensor; + data->ops.irq_handler = ab8500_temp_irq_handler; + data->ops.show_name = ab8500_show_name; + data->ops.show_label = ab8500_show_label; + data->ops.is_visible = NULL; + + return 0; +} +EXPORT_SYMBOL(abx500_hwmon_init); + +MODULE_AUTHOR("Hongbo Zhang <hongbo.zhang@linaro.org>"); +MODULE_DESCRIPTION("AB8500 temperature driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/abituguru.c b/drivers/hwmon/abituguru.c index 03694cc17a3..9c8a6bab822 100644 --- a/drivers/hwmon/abituguru.c +++ b/drivers/hwmon/abituguru.c @@ -1,25 +1,28 @@ /* - abituguru.c Copyright (c) 2005-2006 Hans de Goede <j.w.r.degoede@hhs.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. -*/ + * abituguru.c Copyright (c) 2005-2006 Hans de Goede <hdegoede@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ /* - This driver supports the sensor part of the first and second revision of - the custom Abit uGuru chip found on Abit uGuru motherboards. Note: because - of lack of specs the CPU/RAM voltage & frequency control is not supported! -*/ + * This driver supports the sensor part of the first and second revision of + * the custom Abit uGuru chip found on Abit uGuru motherboards. Note: because + * of lack of specs the CPU/RAM voltage & frequency control is not supported! + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/sched.h> #include <linux/init.h> @@ -41,8 +44,10 @@ #define ABIT_UGURU_SENSOR_BANK2 0x26 /* fans */ /* max nr of sensors in bank1, a bank1 sensor can be in, temp or nc */ #define ABIT_UGURU_MAX_BANK1_SENSORS 16 -/* Warning if you increase one of the 2 MAX defines below to 10 or higher you - should adjust the belonging _NAMES_LENGTH macro for the 2 digit number! */ +/* + * Warning if you increase one of the 2 MAX defines below to 10 or higher you + * should adjust the belonging _NAMES_LENGTH macro for the 2 digit number! + */ /* max nr of sensors in bank2, currently mb's with max 6 fans are known */ #define ABIT_UGURU_MAX_BANK2_SENSORS 6 /* max nr of pwm outputs, currently mb's with max 5 pwm outputs are known */ @@ -67,16 +72,22 @@ #define ABIT_UGURU_IN_SENSOR 0 #define ABIT_UGURU_TEMP_SENSOR 1 #define ABIT_UGURU_NC 2 -/* In many cases we need to wait for the uGuru to reach a certain status, most - of the time it will reach this status within 30 - 90 ISA reads, and thus we - can best busy wait. This define gives the total amount of reads to try. */ +/* + * In many cases we need to wait for the uGuru to reach a certain status, most + * of the time it will reach this status within 30 - 90 ISA reads, and thus we + * can best busy wait. This define gives the total amount of reads to try. + */ #define ABIT_UGURU_WAIT_TIMEOUT 125 -/* However sometimes older versions of the uGuru seem to be distracted and they - do not respond for a long time. To handle this we sleep before each of the - last ABIT_UGURU_WAIT_TIMEOUT_SLEEP tries. */ +/* + * However sometimes older versions of the uGuru seem to be distracted and they + * do not respond for a long time. To handle this we sleep before each of the + * last ABIT_UGURU_WAIT_TIMEOUT_SLEEP tries. + */ #define ABIT_UGURU_WAIT_TIMEOUT_SLEEP 5 -/* Normally all expected status in abituguru_ready, are reported after the - first read, but sometimes not and we need to poll. */ +/* + * Normally all expected status in abituguru_ready, are reported after the + * first read, but sometimes not and we need to poll. + */ #define ABIT_UGURU_READY_TIMEOUT 5 /* Maximum 3 retries on timedout reads/writes, delay 200 ms before retrying */ #define ABIT_UGURU_MAX_RETRIES 3 @@ -85,21 +96,32 @@ #define ABIT_UGURU_MAX_TIMEOUTS 2 /* utility macros */ #define ABIT_UGURU_NAME "abituguru" -#define ABIT_UGURU_DEBUG(level, format, arg...) \ - if (level <= verbose) \ - printk(KERN_DEBUG ABIT_UGURU_NAME ": " format , ## arg) +#define ABIT_UGURU_DEBUG(level, format, arg...) \ + do { \ + if (level <= verbose) \ + pr_debug(format , ## arg); \ + } while (0) + /* Macros to help calculate the sysfs_names array length */ -/* sum of strlen of: in??_input\0, in??_{min,max}\0, in??_{min,max}_alarm\0, - in??_{min,max}_alarm_enable\0, in??_beep\0, in??_shutdown\0 */ +/* + * sum of strlen of: in??_input\0, in??_{min,max}\0, in??_{min,max}_alarm\0, + * in??_{min,max}_alarm_enable\0, in??_beep\0, in??_shutdown\0 + */ #define ABITUGURU_IN_NAMES_LENGTH (11 + 2 * 9 + 2 * 15 + 2 * 22 + 10 + 14) -/* sum of strlen of: temp??_input\0, temp??_max\0, temp??_crit\0, - temp??_alarm\0, temp??_alarm_enable\0, temp??_beep\0, temp??_shutdown\0 */ +/* + * sum of strlen of: temp??_input\0, temp??_max\0, temp??_crit\0, + * temp??_alarm\0, temp??_alarm_enable\0, temp??_beep\0, temp??_shutdown\0 + */ #define ABITUGURU_TEMP_NAMES_LENGTH (13 + 11 + 12 + 13 + 20 + 12 + 16) -/* sum of strlen of: fan?_input\0, fan?_min\0, fan?_alarm\0, - fan?_alarm_enable\0, fan?_beep\0, fan?_shutdown\0 */ +/* + * sum of strlen of: fan?_input\0, fan?_min\0, fan?_alarm\0, + * fan?_alarm_enable\0, fan?_beep\0, fan?_shutdown\0 + */ #define ABITUGURU_FAN_NAMES_LENGTH (11 + 9 + 11 + 18 + 10 + 14) -/* sum of strlen of: pwm?_enable\0, pwm?_auto_channels_temp\0, - pwm?_auto_point{1,2}_pwm\0, pwm?_auto_point{1,2}_temp\0 */ +/* + * sum of strlen of: pwm?_enable\0, pwm?_auto_channels_temp\0, + * pwm?_auto_point{1,2}_pwm\0, pwm?_auto_point{1,2}_temp\0 + */ #define ABITUGURU_PWM_NAMES_LENGTH (12 + 24 + 2 * 21 + 2 * 22) /* IN_NAMES_LENGTH > TEMP_NAMES_LENGTH so assume all bank1 sensors are in */ #define ABITUGURU_SYSFS_NAMES_LENGTH ( \ @@ -107,10 +129,12 @@ ABIT_UGURU_MAX_BANK2_SENSORS * ABITUGURU_FAN_NAMES_LENGTH + \ ABIT_UGURU_MAX_PWMS * ABITUGURU_PWM_NAMES_LENGTH) -/* All the macros below are named identical to the oguru and oguru2 programs - reverse engineered by Olle Sandberg, hence the names might not be 100% - logical. I could come up with better names, but I prefer keeping the names - identical so that this driver can be compared with his work more easily. */ +/* + * All the macros below are named identical to the oguru and oguru2 programs + * reverse engineered by Olle Sandberg, hence the names might not be 100% + * logical. I could come up with better names, but I prefer keeping the names + * identical so that this driver can be compared with his work more easily. + */ /* Two i/o-ports are used by uGuru */ #define ABIT_UGURU_BASE 0x00E0 /* Used to tell uGuru what to read and to read the actual data */ @@ -127,22 +151,28 @@ /* Constants */ /* in (Volt) sensors go up to 3494 mV, temp to 255000 millidegrees Celsius */ static const int abituguru_bank1_max_value[2] = { 3494, 255000 }; -/* Min / Max allowed values for sensor2 (fan) alarm threshold, these values - correspond to 300-3000 RPM */ +/* + * Min / Max allowed values for sensor2 (fan) alarm threshold, these values + * correspond to 300-3000 RPM + */ static const u8 abituguru_bank2_min_threshold = 5; static const u8 abituguru_bank2_max_threshold = 50; -/* Register 0 is a bitfield, 1 and 2 are pwm settings (255 = 100%), 3 and 4 - are temperature trip points. */ +/* + * Register 0 is a bitfield, 1 and 2 are pwm settings (255 = 100%), 3 and 4 + * are temperature trip points. + */ static const int abituguru_pwm_settings_multiplier[5] = { 0, 1, 1, 1000, 1000 }; -/* Min / Max allowed values for pwm_settings. Note: pwm1 (CPU fan) is a - special case the minium allowed pwm% setting for this is 30% (77) on - some MB's this special case is handled in the code! */ +/* + * Min / Max allowed values for pwm_settings. Note: pwm1 (CPU fan) is a + * special case the minimum allowed pwm% setting for this is 30% (77) on + * some MB's this special case is handled in the code! + */ static const u8 abituguru_pwm_min[5] = { 0, 170, 170, 25, 25 }; static const u8 abituguru_pwm_max[5] = { 0, 255, 255, 75, 75 }; /* Insmod parameters */ -static int force; +static bool force; module_param(force, bool, 0); MODULE_PARM_DESC(force, "Set to one to force detection."); static int bank1_types[ABIT_UGURU_MAX_BANK1_SENSORS] = { -1, -1, -1, -1, -1, @@ -172,23 +202,29 @@ MODULE_PARM_DESC(verbose, "How verbose should the driver be? (0-3):\n" " 3 + retryable error reporting"); -/* For the Abit uGuru, we need to keep some data in memory. - The structure is dynamically allocated, at the same time when a new - abituguru device is allocated. */ +/* + * For the Abit uGuru, we need to keep some data in memory. + * The structure is dynamically allocated, at the same time when a new + * abituguru device is allocated. + */ struct abituguru_data { struct device *hwmon_dev; /* hwmon registered device */ struct mutex update_lock; /* protect access to data and uGuru */ unsigned long last_updated; /* In jiffies */ unsigned short addr; /* uguru base address */ char uguru_ready; /* is the uguru in ready state? */ - unsigned char update_timeouts; /* number of update timeouts since last - successful update */ - - /* The sysfs attr and their names are generated automatically, for bank1 - we cannot use a predefined array because we don't know beforehand - of a sensor is a volt or a temp sensor, for bank2 and the pwms its - easier todo things the same way. For in sensors we have 9 (temp 7) - sysfs entries per sensor, for bank2 and pwms 6. */ + unsigned char update_timeouts; /* + * number of update timeouts since last + * successful update + */ + + /* + * The sysfs attr and their names are generated automatically, for bank1 + * we cannot use a predefined array because we don't know beforehand + * of a sensor is a volt or a temp sensor, for bank2 and the pwms its + * easier todo things the same way. For in sensors we have 9 (temp 7) + * sysfs entries per sensor, for bank2 and pwms 6. + */ struct sensor_device_attribute_2 sysfs_attr[ ABIT_UGURU_MAX_BANK1_SENSORS * 9 + ABIT_UGURU_MAX_BANK2_SENSORS * 6 + ABIT_UGURU_MAX_PWMS * 6]; @@ -200,11 +236,15 @@ struct abituguru_data { u8 bank1_sensors[2]; u8 bank1_address[2][ABIT_UGURU_MAX_BANK1_SENSORS]; u8 bank1_value[ABIT_UGURU_MAX_BANK1_SENSORS]; - /* This array holds 3 entries per sensor for the bank 1 sensor settings - (flags, min, max for voltage / flags, warn, shutdown for temp). */ + /* + * This array holds 3 entries per sensor for the bank 1 sensor settings + * (flags, min, max for voltage / flags, warn, shutdown for temp). + */ u8 bank1_settings[ABIT_UGURU_MAX_BANK1_SENSORS][3]; - /* Maximum value for each sensor used for scaling in mV/millidegrees - Celsius. */ + /* + * Maximum value for each sensor used for scaling in mV/millidegrees + * Celsius. + */ int bank1_max_value[ABIT_UGURU_MAX_BANK1_SENSORS]; /* Bank 2 data, ABIT_UGURU_MAX_BANK2_SENSORS entries for bank2 */ @@ -220,6 +260,10 @@ struct abituguru_data { u8 pwm_settings[ABIT_UGURU_MAX_PWMS][5]; }; +static const char *never_happen = "This should never happen."; +static const char *report_this = + "Please report this to the abituguru maintainer (see MAINTAINERS)"; + /* wait till the uguru is in the specified state */ static int abituguru_wait(struct abituguru_data *data, u8 state) { @@ -229,8 +273,10 @@ static int abituguru_wait(struct abituguru_data *data, u8 state) timeout--; if (timeout == 0) return -EBUSY; - /* sleep a bit before our last few tries, see the comment on - this where ABIT_UGURU_WAIT_TIMEOUT_SLEEP is defined. */ + /* + * sleep a bit before our last few tries, see the comment on + * this where ABIT_UGURU_WAIT_TIMEOUT_SLEEP is defined. + */ if (timeout <= ABIT_UGURU_WAIT_TIMEOUT_SLEEP) msleep(0); } @@ -266,8 +312,10 @@ static int abituguru_ready(struct abituguru_data *data) msleep(0); } - /* After this the ABIT_UGURU_DATA port should contain - ABIT_UGURU_STATUS_INPUT */ + /* + * After this the ABIT_UGURU_DATA port should contain + * ABIT_UGURU_STATUS_INPUT + */ timeout = ABIT_UGURU_READY_TIMEOUT; while (inb_p(data->addr + ABIT_UGURU_DATA) != ABIT_UGURU_STATUS_INPUT) { timeout--; @@ -283,27 +331,35 @@ static int abituguru_ready(struct abituguru_data *data) return 0; } -/* Send the bank and then sensor address to the uGuru for the next read/write - cycle. This function gets called as the first part of a read/write by - abituguru_read and abituguru_write. This function should never be - called by any other function. */ +/* + * Send the bank and then sensor address to the uGuru for the next read/write + * cycle. This function gets called as the first part of a read/write by + * abituguru_read and abituguru_write. This function should never be + * called by any other function. + */ static int abituguru_send_address(struct abituguru_data *data, u8 bank_addr, u8 sensor_addr, int retries) { - /* assume the caller does error handling itself if it has not requested - any retries, and thus be quiet. */ + /* + * assume the caller does error handling itself if it has not requested + * any retries, and thus be quiet. + */ int report_errors = retries; for (;;) { - /* Make sure the uguru is ready and then send the bank address, - after this the uguru is no longer "ready". */ + /* + * Make sure the uguru is ready and then send the bank address, + * after this the uguru is no longer "ready". + */ if (abituguru_ready(data) != 0) return -EIO; outb(bank_addr, data->addr + ABIT_UGURU_DATA); data->uguru_ready = 0; - /* Wait till the uguru is ABIT_UGURU_STATUS_INPUT state again - and send the sensor addr */ + /* + * Wait till the uguru is ABIT_UGURU_STATUS_INPUT state again + * and send the sensor addr + */ if (abituguru_wait(data, ABIT_UGURU_STATUS_INPUT)) { if (retries) { ABIT_UGURU_DEBUG(3, "timeout exceeded " @@ -325,8 +381,10 @@ static int abituguru_send_address(struct abituguru_data *data, } } -/* Read count bytes from sensor sensor_addr in bank bank_addr and store the - result in buf, retry the send address part of the read retries times. */ +/* + * Read count bytes from sensor sensor_addr in bank bank_addr and store the + * result in buf, retry the send address part of the read retries times. + */ static int abituguru_read(struct abituguru_data *data, u8 bank_addr, u8 sensor_addr, u8 *buf, int count, int retries) { @@ -355,13 +413,17 @@ static int abituguru_read(struct abituguru_data *data, return i; } -/* Write count bytes from buf to sensor sensor_addr in bank bank_addr, the send - address part of the write is always retried ABIT_UGURU_MAX_RETRIES times. */ +/* + * Write count bytes from buf to sensor sensor_addr in bank bank_addr, the send + * address part of the write is always retried ABIT_UGURU_MAX_RETRIES times. + */ static int abituguru_write(struct abituguru_data *data, u8 bank_addr, u8 sensor_addr, u8 *buf, int count) { - /* We use the ready timeout as we have to wait for 0xAC just like the - ready function */ + /* + * We use the ready timeout as we have to wait for 0xAC just like the + * ready function + */ int i, timeout = ABIT_UGURU_READY_TIMEOUT; /* Send the address */ @@ -381,9 +443,11 @@ static int abituguru_write(struct abituguru_data *data, outb(buf[i], data->addr + ABIT_UGURU_CMD); } - /* Now we need to wait till the chip is ready to be read again, - so that we can read 0xAC as confirmation that our write has - succeeded. */ + /* + * Now we need to wait till the chip is ready to be read again, + * so that we can read 0xAC as confirmation that our write has + * succeeded. + */ if (abituguru_wait(data, ABIT_UGURU_STATUS_READ)) { ABIT_UGURU_DEBUG(1, "timeout exceeded waiting for read state " "after write (bank: %d, sensor: %d)\n", (int)bank_addr, @@ -409,13 +473,15 @@ static int abituguru_write(struct abituguru_data *data, return i; } -/* Detect sensor type. Temp and Volt sensors are enabled with - different masks and will ignore enable masks not meant for them. - This enables us to test what kind of sensor we're dealing with. - By setting the alarm thresholds so that we will always get an - alarm for sensor type X and then enabling the sensor as sensor type - X, if we then get an alarm it is a sensor of type X. */ -static int __devinit +/* + * Detect sensor type. Temp and Volt sensors are enabled with + * different masks and will ignore enable masks not meant for them. + * This enables us to test what kind of sensor we're dealing with. + * By setting the alarm thresholds so that we will always get an + * alarm for sensor type X and then enabling the sensor as sensor type + * X, if we then get an alarm it is a sensor of type X. + */ +static int abituguru_detect_bank1_sensor_type(struct abituguru_data *data, u8 sensor_addr) { @@ -438,20 +504,23 @@ abituguru_detect_bank1_sensor_type(struct abituguru_data *data, /* Test val is sane / usable for sensor type detection. */ if ((val < 10u) || (val > 250u)) { - printk(KERN_WARNING ABIT_UGURU_NAME - ": bank1-sensor: %d reading (%d) too close to limits, " + pr_warn("bank1-sensor: %d reading (%d) too close to limits, " "unable to determine sensor type, skipping sensor\n", (int)sensor_addr, (int)val); - /* assume no sensor is there for sensors for which we can't - determine the sensor type because their reading is too close - to their limits, this usually means no sensor is there. */ + /* + * assume no sensor is there for sensors for which we can't + * determine the sensor type because their reading is too close + * to their limits, this usually means no sensor is there. + */ return ABIT_UGURU_NC; } ABIT_UGURU_DEBUG(2, "testing bank1 sensor %d\n", (int)sensor_addr); - /* Volt sensor test, enable volt low alarm, set min value ridicously - high, or vica versa if the reading is very high. If its a volt - sensor this should always give us an alarm. */ + /* + * Volt sensor test, enable volt low alarm, set min value ridiculously + * high, or vica versa if the reading is very high. If its a volt + * sensor this should always give us an alarm. + */ if (val <= 240u) { buf[0] = ABIT_UGURU_VOLT_LOW_ALARM_ENABLE; buf[1] = 245; @@ -467,8 +536,10 @@ abituguru_detect_bank1_sensor_type(struct abituguru_data *data, if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr, buf, 3) != 3) goto abituguru_detect_bank1_sensor_type_exit; - /* Now we need 20 ms to give the uguru time to read the sensors - and raise a voltage alarm */ + /* + * Now we need 20 ms to give the uguru time to read the sensors + * and raise a voltage alarm + */ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/50); /* Check for alarm and check the alarm is a volt low alarm. */ @@ -491,17 +562,21 @@ abituguru_detect_bank1_sensor_type(struct abituguru_data *data, ABIT_UGURU_DEBUG(2, " alarm not raised during volt sensor " "test\n"); - /* Temp sensor test, enable sensor as a temp sensor, set beep value - ridicously low (but not too low, otherwise uguru ignores it). - If its a temp sensor this should always give us an alarm. */ + /* + * Temp sensor test, enable sensor as a temp sensor, set beep value + * ridiculously low (but not too low, otherwise uguru ignores it). + * If its a temp sensor this should always give us an alarm. + */ buf[0] = ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE; buf[1] = 5; buf[2] = 10; if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr, buf, 3) != 3) goto abituguru_detect_bank1_sensor_type_exit; - /* Now we need 50 ms to give the uguru time to read the sensors - and raise a temp alarm */ + /* + * Now we need 50 ms to give the uguru time to read the sensors + * and raise a temp alarm + */ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/20); /* Check for alarm and check the alarm is a temp high alarm. */ @@ -526,42 +601,44 @@ abituguru_detect_bank1_sensor_type(struct abituguru_data *data, ret = ABIT_UGURU_NC; abituguru_detect_bank1_sensor_type_exit: - /* Restore original settings, failing here is really BAD, it has been - reported that some BIOS-es hang when entering the uGuru menu with - invalid settings present in the uGuru, so we try this 3 times. */ + /* + * Restore original settings, failing here is really BAD, it has been + * reported that some BIOS-es hang when entering the uGuru menu with + * invalid settings present in the uGuru, so we try this 3 times. + */ for (i = 0; i < 3; i++) if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr, data->bank1_settings[sensor_addr], 3) == 3) break; if (i == 3) { - printk(KERN_ERR ABIT_UGURU_NAME - ": Fatal error could not restore original settings. " - "This should never happen please report this to the " - "abituguru maintainer (see MAINTAINERS)\n"); + pr_err("Fatal error could not restore original settings. %s %s\n", + never_happen, report_this); return -ENODEV; } return ret; } -/* These functions try to find out how many sensors there are in bank2 and how - many pwms there are. The purpose of this is to make sure that we don't give - the user the possibility to change settings for non-existent sensors / pwm. - The uGuru will happily read / write whatever memory happens to be after the - memory storing the PWM settings when reading/writing to a PWM which is not - there. Notice even if we detect a PWM which doesn't exist we normally won't - write to it, unless the user tries to change the settings. - - Although the uGuru allows reading (settings) from non existing bank2 - sensors, my version of the uGuru does seem to stop writing to them, the - write function above aborts in this case with: - "CMD reg does not hold 0xAC after write" - - Notice these 2 tests are non destructive iow read-only tests, otherwise - they would defeat their purpose. Although for the bank2_sensors detection a - read/write test would be feasible because of the reaction above, I've - however opted to stay on the safe side. */ -static void __devinit +/* + * These functions try to find out how many sensors there are in bank2 and how + * many pwms there are. The purpose of this is to make sure that we don't give + * the user the possibility to change settings for non-existent sensors / pwm. + * The uGuru will happily read / write whatever memory happens to be after the + * memory storing the PWM settings when reading/writing to a PWM which is not + * there. Notice even if we detect a PWM which doesn't exist we normally won't + * write to it, unless the user tries to change the settings. + * + * Although the uGuru allows reading (settings) from non existing bank2 + * sensors, my version of the uGuru does seem to stop writing to them, the + * write function above aborts in this case with: + * "CMD reg does not hold 0xAC after write" + * + * Notice these 2 tests are non destructive iow read-only tests, otherwise + * they would defeat their purpose. Although for the bank2_sensors detection a + * read/write test would be feasible because of the reaction above, I've + * however opted to stay on the safe side. + */ +static void abituguru_detect_no_bank2_sensors(struct abituguru_data *data) { int i; @@ -576,12 +653,14 @@ abituguru_detect_no_bank2_sensors(struct abituguru_data *data) ABIT_UGURU_DEBUG(2, "detecting number of fan sensors\n"); for (i = 0; i < ABIT_UGURU_MAX_BANK2_SENSORS; i++) { - /* 0x89 are the known used bits: - -0x80 enable shutdown - -0x08 enable beep - -0x01 enable alarm - All other bits should be 0, but on some motherboards - 0x40 (bit 6) is also high for some of the fans?? */ + /* + * 0x89 are the known used bits: + * -0x80 enable shutdown + * -0x08 enable beep + * -0x01 enable alarm + * All other bits should be 0, but on some motherboards + * 0x40 (bit 6) is also high for some of the fans?? + */ if (data->bank2_settings[i][0] & ~0xC9) { ABIT_UGURU_DEBUG(2, " bank2 sensor %d does not seem " "to be a fan sensor: settings[0] = %02X\n", @@ -615,7 +694,7 @@ abituguru_detect_no_bank2_sensors(struct abituguru_data *data) (int)data->bank2_sensors); } -static void __devinit +static void abituguru_detect_no_pwms(struct abituguru_data *data) { int i, j; @@ -629,9 +708,11 @@ abituguru_detect_no_pwms(struct abituguru_data *data) ABIT_UGURU_DEBUG(2, "detecting number of PWM outputs\n"); for (i = 0; i < ABIT_UGURU_MAX_PWMS; i++) { - /* 0x80 is the enable bit and the low - nibble is which temp sensor to use, - the other bits should be 0 */ + /* + * 0x80 is the enable bit and the low + * nibble is which temp sensor to use, + * the other bits should be 0 + */ if (data->pwm_settings[i][0] & ~0x8F) { ABIT_UGURU_DEBUG(2, " pwm channel %d does not seem " "to be a pwm channel: settings[0] = %02X\n", @@ -639,8 +720,10 @@ abituguru_detect_no_pwms(struct abituguru_data *data) break; } - /* the low nibble must correspond to one of the temp sensors - we've found */ + /* + * the low nibble must correspond to one of the temp sensors + * we've found + */ for (j = 0; j < data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]; j++) { if (data->bank1_address[ABIT_UGURU_TEMP_SENSOR][j] == @@ -707,9 +790,11 @@ abituguru_detect_no_pwms_exit: ABIT_UGURU_DEBUG(2, " found: %d PWM outputs\n", (int)data->pwms); } -/* Following are the sysfs callback functions. These functions expect: - sensor_device_attribute_2->index: sensor address/offset in the bank - sensor_device_attribute_2->nr: register offset, bitmask or NA. */ +/* + * Following are the sysfs callback functions. These functions expect: + * sensor_device_attribute_2->index: sensor address/offset in the bank + * sensor_device_attribute_2->nr: register offset, bitmask or NA. + */ static struct abituguru_data *abituguru_update_device(struct device *dev); static ssize_t show_bank1_value(struct device *dev, @@ -759,10 +844,18 @@ static ssize_t store_bank1_setting(struct device *dev, struct device_attribute { struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); struct abituguru_data *data = dev_get_drvdata(dev); - u8 val = (simple_strtoul(buf, NULL, 10) * 255 + - data->bank1_max_value[attr->index]/2) / + unsigned long val; + ssize_t ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + ret = count; + val = (val * 255 + data->bank1_max_value[attr->index] / 2) / data->bank1_max_value[attr->index]; - ssize_t ret = count; + if (val > 255) + return -EINVAL; mutex_lock(&data->update_lock); if (data->bank1_settings[attr->index][attr->nr] != val) { @@ -784,13 +877,19 @@ static ssize_t store_bank2_setting(struct device *dev, struct device_attribute { struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); struct abituguru_data *data = dev_get_drvdata(dev); - u8 val = (simple_strtoul(buf, NULL, 10)*255 + ABIT_UGURU_FAN_MAX/2) / - ABIT_UGURU_FAN_MAX; - ssize_t ret = count; + unsigned long val; + ssize_t ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + ret = count; + val = (val * 255 + ABIT_UGURU_FAN_MAX / 2) / ABIT_UGURU_FAN_MAX; /* this check can be done before taking the lock */ - if ((val < abituguru_bank2_min_threshold) || - (val > abituguru_bank2_max_threshold)) + if (val < abituguru_bank2_min_threshold || + val > abituguru_bank2_max_threshold) return -EINVAL; mutex_lock(&data->update_lock); @@ -815,11 +914,13 @@ static ssize_t show_bank1_alarm(struct device *dev, struct abituguru_data *data = abituguru_update_device(dev); if (!data) return -EIO; - /* See if the alarm bit for this sensor is set, and if the - alarm matches the type of alarm we're looking for (for volt - it can be either low or high). The type is stored in a few - readonly bits in the settings part of the relevant sensor. - The bitmask of the type is passed to us in attr->nr. */ + /* + * See if the alarm bit for this sensor is set, and if the + * alarm matches the type of alarm we're looking for (for volt + * it can be either low or high). The type is stored in a few + * readonly bits in the settings part of the relevant sensor. + * The bitmask of the type is passed to us in attr->nr. + */ if ((data->alarms[attr->index / 8] & (0x01 << (attr->index % 8))) && (data->bank1_settings[attr->index][0] & attr->nr)) return sprintf(buf, "1\n"); @@ -867,10 +968,15 @@ static ssize_t store_bank1_mask(struct device *dev, { struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); struct abituguru_data *data = dev_get_drvdata(dev); - int mask = simple_strtoul(buf, NULL, 10); - ssize_t ret = count; + ssize_t ret; u8 orig_val; + unsigned long mask; + + ret = kstrtoul(buf, 10, &mask); + if (ret) + return ret; + ret = count; mutex_lock(&data->update_lock); orig_val = data->bank1_settings[attr->index][0]; @@ -895,10 +1001,15 @@ static ssize_t store_bank2_mask(struct device *dev, { struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); struct abituguru_data *data = dev_get_drvdata(dev); - int mask = simple_strtoul(buf, NULL, 10); - ssize_t ret = count; + ssize_t ret; u8 orig_val; + unsigned long mask; + ret = kstrtoul(buf, 10, &mask); + if (ret) + return ret; + + ret = count; mutex_lock(&data->update_lock); orig_val = data->bank2_settings[attr->index][0]; @@ -933,10 +1044,17 @@ static ssize_t store_pwm_setting(struct device *dev, struct device_attribute { struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); struct abituguru_data *data = dev_get_drvdata(dev); - u8 min, val = (simple_strtoul(buf, NULL, 10) + - abituguru_pwm_settings_multiplier[attr->nr]/2) / - abituguru_pwm_settings_multiplier[attr->nr]; - ssize_t ret = count; + u8 min; + unsigned long val; + ssize_t ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + ret = count; + val = (val + abituguru_pwm_settings_multiplier[attr->nr] / 2) / + abituguru_pwm_settings_multiplier[attr->nr]; /* special case pwm1 min pwm% */ if ((attr->index == 0) && ((attr->nr == 1) || (attr->nr == 2))) @@ -945,7 +1063,7 @@ static ssize_t store_pwm_setting(struct device *dev, struct device_attribute min = abituguru_pwm_min[attr->nr]; /* this check can be done before taking the lock */ - if ((val < min) || (val > abituguru_pwm_max[attr->nr])) + if (val < min || val > abituguru_pwm_max[attr->nr]) return -EINVAL; mutex_lock(&data->update_lock); @@ -977,8 +1095,10 @@ static ssize_t show_pwm_sensor(struct device *dev, struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); struct abituguru_data *data = dev_get_drvdata(dev); int i; - /* We need to walk to the temp sensor addresses to find what - the userspace id of the configured temp sensor is. */ + /* + * We need to walk to the temp sensor addresses to find what + * the userspace id of the configured temp sensor is. + */ for (i = 0; i < data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]; i++) if (data->bank1_address[ABIT_UGURU_TEMP_SENSOR][i] == (data->pwm_settings[attr->index][0] & 0x0F)) @@ -992,27 +1112,32 @@ static ssize_t store_pwm_sensor(struct device *dev, struct device_attribute { struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); struct abituguru_data *data = dev_get_drvdata(dev); - unsigned long val = simple_strtoul(buf, NULL, 10) - 1; - ssize_t ret = count; + ssize_t ret; + unsigned long val; + u8 orig_val; + u8 address; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + if (val == 0 || val > data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]) + return -EINVAL; + val -= 1; + ret = count; mutex_lock(&data->update_lock); - if (val < data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]) { - u8 orig_val = data->pwm_settings[attr->index][0]; - u8 address = data->bank1_address[ABIT_UGURU_TEMP_SENSOR][val]; - data->pwm_settings[attr->index][0] &= 0xF0; - data->pwm_settings[attr->index][0] |= address; - if (data->pwm_settings[attr->index][0] != orig_val) { - if (abituguru_write(data, ABIT_UGURU_FAN_PWM + 1, - attr->index, - data->pwm_settings[attr->index], - 5) < 1) { - data->pwm_settings[attr->index][0] = orig_val; - ret = -EIO; - } + orig_val = data->pwm_settings[attr->index][0]; + address = data->bank1_address[ABIT_UGURU_TEMP_SENSOR][val]; + data->pwm_settings[attr->index][0] &= 0xF0; + data->pwm_settings[attr->index][0] |= address; + if (data->pwm_settings[attr->index][0] != orig_val) { + if (abituguru_write(data, ABIT_UGURU_FAN_PWM + 1, attr->index, + data->pwm_settings[attr->index], 5) < 1) { + data->pwm_settings[attr->index][0] = orig_val; + ret = -EIO; } } - else - ret = -EINVAL; mutex_unlock(&data->update_lock); return ret; } @@ -1033,22 +1158,27 @@ static ssize_t store_pwm_enable(struct device *dev, struct device_attribute { struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); struct abituguru_data *data = dev_get_drvdata(dev); - u8 orig_val, user_val = simple_strtoul(buf, NULL, 10); - ssize_t ret = count; + u8 orig_val; + ssize_t ret; + unsigned long user_val; + + ret = kstrtoul(buf, 10, &user_val); + if (ret) + return ret; + ret = count; mutex_lock(&data->update_lock); orig_val = data->pwm_settings[attr->index][0]; switch (user_val) { - case 0: - data->pwm_settings[attr->index][0] &= - ~ABIT_UGURU_FAN_PWM_ENABLE; - break; - case 2: - data->pwm_settings[attr->index][0] |= - ABIT_UGURU_FAN_PWM_ENABLE; - break; - default: - ret = -EINVAL; + case 0: + data->pwm_settings[attr->index][0] &= + ~ABIT_UGURU_FAN_PWM_ENABLE; + break; + case 2: + data->pwm_settings[attr->index][0] |= ABIT_UGURU_FAN_PWM_ENABLE; + break; + default: + ret = -EINVAL; } if ((data->pwm_settings[attr->index][0] != orig_val) && (abituguru_write(data, ABIT_UGURU_FAN_PWM + 1, @@ -1137,19 +1267,23 @@ static struct sensor_device_attribute_2 abituguru_sysfs_attr[] = { SENSOR_ATTR_2(name, 0444, show_name, NULL, 0, 0), }; -static int __devinit abituguru_probe(struct platform_device *pdev) +static int abituguru_probe(struct platform_device *pdev) { struct abituguru_data *data; int i, j, used, sysfs_names_free, sysfs_attr_i, res = -ENODEV; char *sysfs_filename; - /* El weirdo probe order, to keep the sysfs order identical to the - BIOS and window-appliction listing order. */ + /* + * El weirdo probe order, to keep the sysfs order identical to the + * BIOS and window-appliction listing order. + */ const u8 probe_order[ABIT_UGURU_MAX_BANK1_SENSORS] = { 0x00, 0x01, 0x03, 0x04, 0x0A, 0x08, 0x0E, 0x02, 0x09, 0x06, 0x05, 0x0B, 0x0F, 0x0D, 0x07, 0x0C }; - if (!(data = kzalloc(sizeof(struct abituguru_data), GFP_KERNEL))) + data = devm_kzalloc(&pdev->dev, sizeof(struct abituguru_data), + GFP_KERNEL); + if (!data) return -ENOMEM; data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start; @@ -1160,9 +1294,11 @@ static int __devinit abituguru_probe(struct platform_device *pdev) if (inb_p(data->addr + ABIT_UGURU_DATA) == ABIT_UGURU_STATUS_INPUT) data->uguru_ready = 1; - /* Completely read the uGuru this has 2 purposes: - - testread / see if one really is there. - - make an in memory copy of all the uguru settings for future use. */ + /* + * Completely read the uGuru this has 2 purposes: + * - testread / see if one really is there. + * - make an in memory copy of all the uguru settings for future use. + */ if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, data->alarms, 3, ABIT_UGURU_MAX_RETRIES) != 3) goto abituguru_probe_error; @@ -1177,11 +1313,13 @@ static int __devinit abituguru_probe(struct platform_device *pdev) ABIT_UGURU_MAX_RETRIES) != 3) goto abituguru_probe_error; } - /* Note: We don't know how many bank2 sensors / pwms there really are, - but in order to "detect" this we need to read the maximum amount - anyways. If we read sensors/pwms not there we'll just read crap - this can't hurt. We need the detection because we don't want - unwanted writes, which will hurt! */ + /* + * Note: We don't know how many bank2 sensors / pwms there really are, + * but in order to "detect" this we need to read the maximum amount + * anyways. If we read sensors/pwms not there we'll just read crap + * this can't hurt. We need the detection because we don't want + * unwanted writes, which will hurt! + */ for (i = 0; i < ABIT_UGURU_MAX_BANK2_SENSORS; i++) { if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK2, i, &data->bank2_value[i], 1, @@ -1268,24 +1406,26 @@ static int __devinit abituguru_probe(struct platform_device *pdev) } /* Fail safe check, this should never happen! */ if (sysfs_names_free < 0) { - printk(KERN_ERR ABIT_UGURU_NAME ": Fatal error ran out of " - "space for sysfs attr names. This should never " - "happen please report to the abituguru maintainer " - "(see MAINTAINERS)\n"); + pr_err("Fatal error ran out of space for sysfs attr names. %s %s", + never_happen, report_this); res = -ENAMETOOLONG; goto abituguru_probe_error; } - printk(KERN_INFO ABIT_UGURU_NAME ": found Abit uGuru\n"); + pr_info("found Abit uGuru\n"); /* Register sysfs hooks */ - for (i = 0; i < sysfs_attr_i; i++) - if (device_create_file(&pdev->dev, - &data->sysfs_attr[i].dev_attr)) + for (i = 0; i < sysfs_attr_i; i++) { + res = device_create_file(&pdev->dev, + &data->sysfs_attr[i].dev_attr); + if (res) goto abituguru_probe_error; - for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++) - if (device_create_file(&pdev->dev, - &abituguru_sysfs_attr[i].dev_attr)) + } + for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++) { + res = device_create_file(&pdev->dev, + &abituguru_sysfs_attr[i].dev_attr); + if (res) goto abituguru_probe_error; + } data->hwmon_dev = hwmon_device_register(&pdev->dev); if (!IS_ERR(data->hwmon_dev)) @@ -1298,12 +1438,10 @@ abituguru_probe_error: for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++) device_remove_file(&pdev->dev, &abituguru_sysfs_attr[i].dev_attr); - platform_set_drvdata(pdev, NULL); - kfree(data); return res; } -static int __devexit abituguru_remove(struct platform_device *pdev) +static int abituguru_remove(struct platform_device *pdev) { int i; struct abituguru_data *data = platform_get_drvdata(pdev); @@ -1314,8 +1452,6 @@ static int __devexit abituguru_remove(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++) device_remove_file(&pdev->dev, &abituguru_sysfs_attr[i].dev_attr); - platform_set_drvdata(pdev, NULL); - kfree(data); return 0; } @@ -1330,24 +1466,26 @@ static struct abituguru_data *abituguru_update_device(struct device *dev) mutex_lock(&data->update_lock); if (time_after(jiffies, data->last_updated + HZ)) { success = 0; - if ((err = abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, - data->alarms, 3, 0)) != 3) + err = abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, + data->alarms, 3, 0); + if (err != 3) goto LEAVE_UPDATE; for (i = 0; i < ABIT_UGURU_MAX_BANK1_SENSORS; i++) { - if ((err = abituguru_read(data, - ABIT_UGURU_SENSOR_BANK1, i, - &data->bank1_value[i], 1, 0)) != 1) + err = abituguru_read(data, ABIT_UGURU_SENSOR_BANK1, + i, &data->bank1_value[i], 1, 0); + if (err != 1) goto LEAVE_UPDATE; - if ((err = abituguru_read(data, - ABIT_UGURU_SENSOR_BANK1 + 1, i, - data->bank1_settings[i], 3, 0)) != 3) + err = abituguru_read(data, ABIT_UGURU_SENSOR_BANK1 + 1, + i, data->bank1_settings[i], 3, 0); + if (err != 3) goto LEAVE_UPDATE; } - for (i = 0; i < data->bank2_sensors; i++) - if ((err = abituguru_read(data, - ABIT_UGURU_SENSOR_BANK2, i, - &data->bank2_value[i], 1, 0)) != 1) + for (i = 0; i < data->bank2_sensors; i++) { + err = abituguru_read(data, ABIT_UGURU_SENSOR_BANK2, i, + &data->bank2_value[i], 1, 0); + if (err != 1) goto LEAVE_UPDATE; + } /* success! */ success = 1; data->update_timeouts = 0; @@ -1379,49 +1517,54 @@ LEAVE_UPDATE: return NULL; } -#ifdef CONFIG_PM -static int abituguru_suspend(struct platform_device *pdev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int abituguru_suspend(struct device *dev) { - struct abituguru_data *data = platform_get_drvdata(pdev); - /* make sure all communications with the uguru are done and no new - ones are started */ + struct abituguru_data *data = dev_get_drvdata(dev); + /* + * make sure all communications with the uguru are done and no new + * ones are started + */ mutex_lock(&data->update_lock); return 0; } -static int abituguru_resume(struct platform_device *pdev) +static int abituguru_resume(struct device *dev) { - struct abituguru_data *data = platform_get_drvdata(pdev); + struct abituguru_data *data = dev_get_drvdata(dev); /* See if the uGuru is still ready */ if (inb_p(data->addr + ABIT_UGURU_DATA) != ABIT_UGURU_STATUS_INPUT) data->uguru_ready = 0; mutex_unlock(&data->update_lock); return 0; } + +static SIMPLE_DEV_PM_OPS(abituguru_pm, abituguru_suspend, abituguru_resume); +#define ABIT_UGURU_PM (&abituguru_pm) #else -#define abituguru_suspend NULL -#define abituguru_resume NULL +#define ABIT_UGURU_PM NULL #endif /* CONFIG_PM */ static struct platform_driver abituguru_driver = { .driver = { .owner = THIS_MODULE, .name = ABIT_UGURU_NAME, + .pm = ABIT_UGURU_PM, }, .probe = abituguru_probe, - .remove = __devexit_p(abituguru_remove), - .suspend = abituguru_suspend, - .resume = abituguru_resume, + .remove = abituguru_remove, }; static int __init abituguru_detect(void) { - /* See if there is an uguru there. After a reboot uGuru will hold 0x00 - at DATA and 0xAC, when this driver has already been loaded once - DATA will hold 0x08. For most uGuru's CMD will hold 0xAC in either - scenario but some will hold 0x00. - Some uGuru's initally hold 0x09 at DATA and will only hold 0x08 - after reading CMD first, so CMD must be read first! */ + /* + * See if there is an uguru there. After a reboot uGuru will hold 0x00 + * at DATA and 0xAC, when this driver has already been loaded once + * DATA will hold 0x08. For most uGuru's CMD will hold 0xAC in either + * scenario but some will hold 0x00. + * Some uGuru's initially hold 0x09 at DATA and will only hold 0x08 + * after reading CMD first, so CMD must be read first! + */ u8 cmd_val = inb_p(ABIT_UGURU_BASE + ABIT_UGURU_CMD); u8 data_val = inb_p(ABIT_UGURU_BASE + ABIT_UGURU_DATA); if (((data_val == 0x00) || (data_val == 0x08)) && @@ -1432,8 +1575,7 @@ static int __init abituguru_detect(void) "0x%02X\n", (unsigned int)data_val, (unsigned int)cmd_val); if (force) { - printk(KERN_INFO ABIT_UGURU_NAME ": Assuming Abit uGuru is " - "present because of \"force\" parameter\n"); + pr_info("Assuming Abit uGuru is present because of \"force\" parameter\n"); return ABIT_UGURU_BASE; } @@ -1447,15 +1589,12 @@ static int __init abituguru_init(void) { int address, err; struct resource res = { .flags = IORESOURCE_IO }; - -#ifdef CONFIG_DMI const char *board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); /* safety check, refuse to load on non Abit motherboards */ if (!force && (!board_vendor || strcmp(board_vendor, "http://www.abit.com.tw/"))) return -ENODEV; -#endif address = abituguru_detect(); if (address < 0) @@ -1467,8 +1606,7 @@ static int __init abituguru_init(void) abituguru_pdev = platform_device_alloc(ABIT_UGURU_NAME, address); if (!abituguru_pdev) { - printk(KERN_ERR ABIT_UGURU_NAME - ": Device allocation failed\n"); + pr_err("Device allocation failed\n"); err = -ENOMEM; goto exit_driver_unregister; } @@ -1479,15 +1617,13 @@ static int __init abituguru_init(void) err = platform_device_add_resources(abituguru_pdev, &res, 1); if (err) { - printk(KERN_ERR ABIT_UGURU_NAME - ": Device resource addition failed (%d)\n", err); + pr_err("Device resource addition failed (%d)\n", err); goto exit_device_put; } err = platform_device_add(abituguru_pdev); if (err) { - printk(KERN_ERR ABIT_UGURU_NAME - ": Device addition failed (%d)\n", err); + pr_err("Device addition failed (%d)\n", err); goto exit_device_put; } @@ -1507,7 +1643,7 @@ static void __exit abituguru_exit(void) platform_driver_unregister(&abituguru_driver); } -MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); MODULE_DESCRIPTION("Abit uGuru Sensor device"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/abituguru3.c b/drivers/hwmon/abituguru3.c index 3cf28af614b..4ae74aa8cdc 100644 --- a/drivers/hwmon/abituguru3.c +++ b/drivers/hwmon/abituguru3.c @@ -1,28 +1,31 @@ /* - abituguru3.c - - Copyright (c) 2006-2008 Hans de Goede <j.w.r.degoede@hhs.nl> - Copyright (c) 2008 Alistair John Strachan <alistair@devzero.co.uk> - - 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. + * abituguru3.c + * + * Copyright (c) 2006-2008 Hans de Goede <hdegoede@redhat.com> + * Copyright (c) 2008 Alistair John Strachan <alistair@devzero.co.uk> + * + * 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. + */ +/* + * This driver supports the sensor part of revision 3 of the custom Abit uGuru + * chip found on newer Abit uGuru motherboards. Note: because of lack of specs + * only reading the sensors and their settings is supported. + */ - 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. +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - 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. -*/ -/* - This driver supports the sensor part of revision 3 of the custom Abit uGuru - chip found on newer Abit uGuru motherboards. Note: because of lack of specs - only reading the sensors and their settings is supported. -*/ #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> @@ -59,48 +62,67 @@ #define ABIT_UGURU3_TEMP_SENSOR 1 #define ABIT_UGURU3_FAN_SENSOR 2 -/* Timeouts / Retries, if these turn out to need a lot of fiddling we could - convert them to params. Determined by trial and error. I assume this is - cpu-speed independent, since the ISA-bus and not the CPU should be the - bottleneck. */ +/* + * Timeouts / Retries, if these turn out to need a lot of fiddling we could + * convert them to params. Determined by trial and error. I assume this is + * cpu-speed independent, since the ISA-bus and not the CPU should be the + * bottleneck. + */ #define ABIT_UGURU3_WAIT_TIMEOUT 250 -/* Normally the 0xAC at the end of synchronize() is reported after the - first read, but sometimes not and we need to poll */ +/* + * Normally the 0xAC at the end of synchronize() is reported after the + * first read, but sometimes not and we need to poll + */ #define ABIT_UGURU3_SYNCHRONIZE_TIMEOUT 5 /* utility macros */ #define ABIT_UGURU3_NAME "abituguru3" -#define ABIT_UGURU3_DEBUG(format, arg...) \ - if (verbose) \ - printk(KERN_DEBUG ABIT_UGURU3_NAME ": " format , ## arg) +#define ABIT_UGURU3_DEBUG(format, arg...) \ + do { \ + if (verbose) \ + pr_debug(format , ## arg); \ + } while (0) /* Macros to help calculate the sysfs_names array length */ #define ABIT_UGURU3_MAX_NO_SENSORS 26 -/* sum of strlen +1 of: in??_input\0, in??_{min,max}\0, in??_{min,max}_alarm\0, - in??_{min,max}_alarm_enable\0, in??_beep\0, in??_shutdown\0, in??_label\0 */ -#define ABIT_UGURU3_IN_NAMES_LENGTH (11 + 2 * 9 + 2 * 15 + 2 * 22 + 10 + 14 + 11) -/* sum of strlen +1 of: temp??_input\0, temp??_max\0, temp??_crit\0, - temp??_alarm\0, temp??_alarm_enable\0, temp??_beep\0, temp??_shutdown\0, - temp??_label\0 */ +/* + * sum of strlen +1 of: in??_input\0, in??_{min,max}\0, in??_{min,max}_alarm\0, + * in??_{min,max}_alarm_enable\0, in??_beep\0, in??_shutdown\0, in??_label\0 + */ +#define ABIT_UGURU3_IN_NAMES_LENGTH \ + (11 + 2 * 9 + 2 * 15 + 2 * 22 + 10 + 14 + 11) +/* + * sum of strlen +1 of: temp??_input\0, temp??_max\0, temp??_crit\0, + * temp??_alarm\0, temp??_alarm_enable\0, temp??_beep\0, temp??_shutdown\0, + * temp??_label\0 + */ #define ABIT_UGURU3_TEMP_NAMES_LENGTH (13 + 11 + 12 + 13 + 20 + 12 + 16 + 13) -/* sum of strlen +1 of: fan??_input\0, fan??_min\0, fan??_alarm\0, - fan??_alarm_enable\0, fan??_beep\0, fan??_shutdown\0, fan??_label\0 */ +/* + * sum of strlen +1 of: fan??_input\0, fan??_min\0, fan??_alarm\0, + * fan??_alarm_enable\0, fan??_beep\0, fan??_shutdown\0, fan??_label\0 + */ #define ABIT_UGURU3_FAN_NAMES_LENGTH (12 + 10 + 12 + 19 + 11 + 15 + 12) -/* Worst case scenario 16 in sensors (longest names_length) and the rest - temp sensors (second longest names_length). */ +/* + * Worst case scenario 16 in sensors (longest names_length) and the rest + * temp sensors (second longest names_length). + */ #define ABIT_UGURU3_SYSFS_NAMES_LENGTH (16 * ABIT_UGURU3_IN_NAMES_LENGTH + \ (ABIT_UGURU3_MAX_NO_SENSORS - 16) * ABIT_UGURU3_TEMP_NAMES_LENGTH) -/* All the macros below are named identical to the openguru2 program - reverse engineered by Louis Kruger, hence the names might not be 100% - logical. I could come up with better names, but I prefer keeping the names - identical so that this driver can be compared with his work more easily. */ +/* + * All the macros below are named identical to the openguru2 program + * reverse engineered by Louis Kruger, hence the names might not be 100% + * logical. I could come up with better names, but I prefer keeping the names + * identical so that this driver can be compared with his work more easily. + */ /* Two i/o-ports are used by uGuru */ #define ABIT_UGURU3_BASE 0x00E0 #define ABIT_UGURU3_CMD 0x00 #define ABIT_UGURU3_DATA 0x04 #define ABIT_UGURU3_REGION_LENGTH 5 -/* The wait_xxx functions return this on success and the last contents - of the DATA register (0-255) on failure. */ +/* + * The wait_xxx functions return this on success and the last contents + * of the DATA register (0-255) on failure. + */ #define ABIT_UGURU3_SUCCESS -1 /* uGuru status flags */ #define ABIT_UGURU3_STATUS_READY_FOR_READ 0x01 @@ -109,7 +131,7 @@ /* Structures */ struct abituguru3_sensor_info { - const char* name; + const char *name; int port; int type; int multiplier; @@ -127,9 +149,11 @@ struct abituguru3_motherboard_info { struct abituguru3_sensor_info sensors[ABIT_UGURU3_MAX_NO_SENSORS + 1]; }; -/* For the Abit uGuru, we need to keep some data in memory. - The structure is dynamically allocated, at the same time when a new - abituguru3 device is allocated. */ +/* + * For the Abit uGuru, we need to keep some data in memory. + * The structure is dynamically allocated, at the same time when a new + * abituguru3 device is allocated. + */ struct abituguru3_data { struct device *hwmon_dev; /* hwmon registered device */ struct mutex update_lock; /* protect access to data and uGuru */ @@ -137,8 +161,10 @@ struct abituguru3_data { char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ - /* For convenience the sysfs attr and their names are generated - automatically. We have max 10 entries per sensor (for in sensors) */ + /* + * For convenience the sysfs attr and their names are generated + * automatically. We have max 10 entries per sensor (for in sensors) + */ struct sensor_device_attribute_2 sysfs_attr[ABIT_UGURU3_MAX_NO_SENSORS * 10]; @@ -148,9 +174,11 @@ struct abituguru3_data { /* Pointer to the sensors info for the detected motherboard */ const struct abituguru3_sensor_info *sensors; - /* The abituguru3 supports upto 48 sensors, and thus has registers - sets for 48 sensors, for convienence reasons / simplicity of the - code we always read and store all registers for all 48 sensors */ + /* + * The abituguru3 supports up to 48 sensors, and thus has registers + * sets for 48 sensors, for convenience reasons / simplicity of the + * code we always read and store all registers for all 48 sensors + */ /* Alarms for all 48 sensors (1 bit per sensor) */ u8 alarms[48/8]; @@ -158,9 +186,11 @@ struct abituguru3_data { /* Value of all 48 sensors */ u8 value[48]; - /* Settings of all 48 sensors, note in and temp sensors (the first 32 - sensors) have 3 bytes of settings, while fans only have 2 bytes, - for convenience we use 3 bytes for all sensors */ + /* + * Settings of all 48 sensors, note in and temp sensors (the first 32 + * sensors) have 3 bytes of settings, while fans only have 2 bytes, + * for convenience we use 3 bytes for all sensors + */ u8 settings[48][3]; }; @@ -600,14 +630,17 @@ static const struct abituguru3_motherboard_info abituguru3_motherboards[] = { /* Insmod parameters */ -static int force; +static bool force; module_param(force, bool, 0); MODULE_PARM_DESC(force, "Set to one to force detection."); /* Default verbose is 1, since this driver is still in the testing phase */ -static int verbose = 1; +static bool verbose = 1; module_param(verbose, bool, 0644); MODULE_PARM_DESC(verbose, "Enable/disable verbose error reporting"); +static const char *never_happen = "This should never happen."; +static const char *report_this = + "Please report this to the abituguru3 maintainer (see MAINTAINERS)"; /* wait while the uguru is busy (usually after a write) */ static int abituguru3_wait_while_busy(struct abituguru3_data *data) @@ -620,8 +653,10 @@ static int abituguru3_wait_while_busy(struct abituguru3_data *data) timeout--; if (timeout == 0) return x; - /* sleep a bit before our last try, to give the uGuru3 one - last chance to respond. */ + /* + * sleep a bit before our last try, to give the uGuru3 one + * last chance to respond. + */ if (timeout == 1) msleep(1); } @@ -639,48 +674,57 @@ static int abituguru3_wait_for_read(struct abituguru3_data *data) timeout--; if (timeout == 0) return x; - /* sleep a bit before our last try, to give the uGuru3 one - last chance to respond. */ + /* + * sleep a bit before our last try, to give the uGuru3 one + * last chance to respond. + */ if (timeout == 1) msleep(1); } return ABIT_UGURU3_SUCCESS; } -/* This synchronizes us with the uGuru3's protocol state machine, this - must be done before each command. */ +/* + * This synchronizes us with the uGuru3's protocol state machine, this + * must be done before each command. + */ static int abituguru3_synchronize(struct abituguru3_data *data) { int x, timeout = ABIT_UGURU3_SYNCHRONIZE_TIMEOUT; - if ((x = abituguru3_wait_while_busy(data)) != ABIT_UGURU3_SUCCESS) { + x = abituguru3_wait_while_busy(data); + if (x != ABIT_UGURU3_SUCCESS) { ABIT_UGURU3_DEBUG("synchronize timeout during initial busy " "wait, status: 0x%02x\n", x); return -EIO; } outb(0x20, data->addr + ABIT_UGURU3_DATA); - if ((x = abituguru3_wait_while_busy(data)) != ABIT_UGURU3_SUCCESS) { + x = abituguru3_wait_while_busy(data); + if (x != ABIT_UGURU3_SUCCESS) { ABIT_UGURU3_DEBUG("synchronize timeout after sending 0x20, " "status: 0x%02x\n", x); return -EIO; } outb(0x10, data->addr + ABIT_UGURU3_CMD); - if ((x = abituguru3_wait_while_busy(data)) != ABIT_UGURU3_SUCCESS) { + x = abituguru3_wait_while_busy(data); + if (x != ABIT_UGURU3_SUCCESS) { ABIT_UGURU3_DEBUG("synchronize timeout after sending 0x10, " "status: 0x%02x\n", x); return -EIO; } outb(0x00, data->addr + ABIT_UGURU3_CMD); - if ((x = abituguru3_wait_while_busy(data)) != ABIT_UGURU3_SUCCESS) { + x = abituguru3_wait_while_busy(data); + if (x != ABIT_UGURU3_SUCCESS) { ABIT_UGURU3_DEBUG("synchronize timeout after sending 0x00, " "status: 0x%02x\n", x); return -EIO; } - if ((x = abituguru3_wait_for_read(data)) != ABIT_UGURU3_SUCCESS) { + x = abituguru3_wait_for_read(data); + if (x != ABIT_UGURU3_SUCCESS) { ABIT_UGURU3_DEBUG("synchronize timeout waiting for read, " "status: 0x%02x\n", x); return -EIO; @@ -699,18 +743,22 @@ static int abituguru3_synchronize(struct abituguru3_data *data) return 0; } -/* Read count bytes from sensor sensor_addr in bank bank_addr and store the - result in buf */ +/* + * Read count bytes from sensor sensor_addr in bank bank_addr and store the + * result in buf + */ static int abituguru3_read(struct abituguru3_data *data, u8 bank, u8 offset, u8 count, u8 *buf) { int i, x; - if ((x = abituguru3_synchronize(data))) + x = abituguru3_synchronize(data); + if (x) return x; outb(0x1A, data->addr + ABIT_UGURU3_DATA); - if ((x = abituguru3_wait_while_busy(data)) != ABIT_UGURU3_SUCCESS) { + x = abituguru3_wait_while_busy(data); + if (x != ABIT_UGURU3_SUCCESS) { ABIT_UGURU3_DEBUG("read from 0x%02x:0x%02x timed out after " "sending 0x1A, status: 0x%02x\n", (unsigned int)bank, (unsigned int)offset, x); @@ -718,7 +766,8 @@ static int abituguru3_read(struct abituguru3_data *data, u8 bank, u8 offset, } outb(bank, data->addr + ABIT_UGURU3_CMD); - if ((x = abituguru3_wait_while_busy(data)) != ABIT_UGURU3_SUCCESS) { + x = abituguru3_wait_while_busy(data); + if (x != ABIT_UGURU3_SUCCESS) { ABIT_UGURU3_DEBUG("read from 0x%02x:0x%02x timed out after " "sending the bank, status: 0x%02x\n", (unsigned int)bank, (unsigned int)offset, x); @@ -726,7 +775,8 @@ static int abituguru3_read(struct abituguru3_data *data, u8 bank, u8 offset, } outb(offset, data->addr + ABIT_UGURU3_CMD); - if ((x = abituguru3_wait_while_busy(data)) != ABIT_UGURU3_SUCCESS) { + x = abituguru3_wait_while_busy(data); + if (x != ABIT_UGURU3_SUCCESS) { ABIT_UGURU3_DEBUG("read from 0x%02x:0x%02x timed out after " "sending the offset, status: 0x%02x\n", (unsigned int)bank, (unsigned int)offset, x); @@ -734,7 +784,8 @@ static int abituguru3_read(struct abituguru3_data *data, u8 bank, u8 offset, } outb(count, data->addr + ABIT_UGURU3_CMD); - if ((x = abituguru3_wait_while_busy(data)) != ABIT_UGURU3_SUCCESS) { + x = abituguru3_wait_while_busy(data); + if (x != ABIT_UGURU3_SUCCESS) { ABIT_UGURU3_DEBUG("read from 0x%02x:0x%02x timed out after " "sending the count, status: 0x%02x\n", (unsigned int)bank, (unsigned int)offset, x); @@ -742,8 +793,8 @@ static int abituguru3_read(struct abituguru3_data *data, u8 bank, u8 offset, } for (i = 0; i < count; i++) { - if ((x = abituguru3_wait_for_read(data)) != - ABIT_UGURU3_SUCCESS) { + x = abituguru3_wait_for_read(data); + if (x != ABIT_UGURU3_SUCCESS) { ABIT_UGURU3_DEBUG("timeout reading byte %d from " "0x%02x:0x%02x, status: 0x%02x\n", i, (unsigned int)bank, (unsigned int)offset, x); @@ -754,28 +805,34 @@ static int abituguru3_read(struct abituguru3_data *data, u8 bank, u8 offset, return i; } -/* Sensor settings are stored 1 byte per offset with the bytes - placed add consecutive offsets. */ +/* + * Sensor settings are stored 1 byte per offset with the bytes + * placed add consecutive offsets. + */ static int abituguru3_read_increment_offset(struct abituguru3_data *data, u8 bank, u8 offset, u8 count, u8 *buf, int offset_count) { int i, x; - for (i = 0; i < offset_count; i++) - if ((x = abituguru3_read(data, bank, offset + i, count, - buf + i * count)) != count) { + for (i = 0; i < offset_count; i++) { + x = abituguru3_read(data, bank, offset + i, count, + buf + i * count); + if (x != count) { if (x < 0) return x; return i * count + x; } + } return i * count; } -/* Following are the sysfs callback functions. These functions expect: - sensor_device_attribute_2->index: index into the data->sensors array - sensor_device_attribute_2->nr: register offset, bitmask or NA. */ +/* + * Following are the sysfs callback functions. These functions expect: + * sensor_device_attribute_2->index: index into the data->sensors array + * sensor_device_attribute_2->nr: register offset, bitmask or NA. + */ static struct abituguru3_data *abituguru3_update_device(struct device *dev); static ssize_t show_value(struct device *dev, @@ -801,8 +858,10 @@ static ssize_t show_value(struct device *dev, value = (value * sensor->multiplier) / sensor->divisor + sensor->offset; - /* alternatively we could update the sensors settings struct for this, - but then its contents would differ from the windows sw ini files */ + /* + * alternatively we could update the sensors settings struct for this, + * but then its contents would differ from the windows sw ini files + */ if (sensor->type == ABIT_UGURU3_TEMP_SENSOR) value *= 1000; @@ -821,10 +880,12 @@ static ssize_t show_alarm(struct device *dev, port = data->sensors[attr->index].port; - /* See if the alarm bit for this sensor is set and if a bitmask is - given in attr->nr also check if the alarm matches the type of alarm - we're looking for (for volt it can be either low or high). The type - is stored in a few readonly bits in the settings of the sensor. */ + /* + * See if the alarm bit for this sensor is set and if a bitmask is + * given in attr->nr also check if the alarm matches the type of alarm + * we're looking for (for volt it can be either low or high). The type + * is stored in a few readonly bits in the settings of the sensor. + */ if ((data->alarms[port / 8] & (0x01 << (port % 8))) && (!attr->nr || (data->settings[port][0] & attr->nr))) return sprintf(buf, "1\n"); @@ -907,7 +968,7 @@ static struct sensor_device_attribute_2 abituguru3_sysfs_attr[] = { SENSOR_ATTR_2(name, 0444, show_name, NULL, 0, 0), }; -static int __devinit abituguru3_probe(struct platform_device *pdev) +static int abituguru3_probe(struct platform_device *pdev) { const int no_sysfs_attr[3] = { 10, 8, 7 }; int sensor_index[3] = { 0, 1, 1 }; @@ -917,7 +978,9 @@ static int __devinit abituguru3_probe(struct platform_device *pdev) u8 buf[2]; u16 id; - if (!(data = kzalloc(sizeof(struct abituguru3_data), GFP_KERNEL))) + data = devm_kzalloc(&pdev->dev, sizeof(struct abituguru3_data), + GFP_KERNEL); + if (!data) return -ENOMEM; data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start; @@ -925,10 +988,10 @@ static int __devinit abituguru3_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); /* Read the motherboard ID */ - if ((i = abituguru3_read(data, ABIT_UGURU3_MISC_BANK, - ABIT_UGURU3_BOARD_ID, 2, buf)) != 2) { + i = abituguru3_read(data, ABIT_UGURU3_MISC_BANK, ABIT_UGURU3_BOARD_ID, + 2, buf); + if (i != 2) goto abituguru3_probe_error; - } /* Completely read the uGuru to see if one really is there */ if (!abituguru3_update_device(&pdev->dev)) @@ -940,15 +1003,13 @@ static int __devinit abituguru3_probe(struct platform_device *pdev) if (abituguru3_motherboards[i].id == id) break; if (!abituguru3_motherboards[i].id) { - printk(KERN_ERR ABIT_UGURU3_NAME ": error unknown motherboard " - "ID: %04X. Please report this to the abituguru3 " - "maintainer (see MAINTAINERS)\n", (unsigned int)id); + pr_err("error unknown motherboard ID: %04X. %s\n", + (unsigned int)id, report_this); goto abituguru3_probe_error; } data->sensors = abituguru3_motherboards[i].sensors; - printk(KERN_INFO ABIT_UGURU3_NAME ": found Abit uGuru3, motherboard " - "ID: %04X\n", (unsigned int)id); + pr_info("found Abit uGuru3, motherboard ID: %04X\n", (unsigned int)id); /* Fill the sysfs attr array */ sysfs_attr_i = 0; @@ -957,11 +1018,8 @@ static int __devinit abituguru3_probe(struct platform_device *pdev) for (i = 0; data->sensors[i].name; i++) { /* Fail safe check, this should never happen! */ if (i >= ABIT_UGURU3_MAX_NO_SENSORS) { - printk(KERN_ERR ABIT_UGURU3_NAME - ": Fatal error motherboard has more sensors " - "then ABIT_UGURU3_MAX_NO_SENSORS. This should " - "never happen please report to the abituguru3 " - "maintainer (see MAINTAINERS)\n"); + pr_err("Fatal error motherboard has more sensors then ABIT_UGURU3_MAX_NO_SENSORS. %s %s\n", + never_happen, report_this); res = -ENAMETOOLONG; goto abituguru3_probe_error; } @@ -983,10 +1041,8 @@ static int __devinit abituguru3_probe(struct platform_device *pdev) } /* Fail safe check, this should never happen! */ if (sysfs_names_free < 0) { - printk(KERN_ERR ABIT_UGURU3_NAME - ": Fatal error ran out of space for sysfs attr names. " - "This should never happen please report to the " - "abituguru3 maintainer (see MAINTAINERS)\n"); + pr_err("Fatal error ran out of space for sysfs attr names. %s %s\n", + never_happen, report_this); res = -ENAMETOOLONG; goto abituguru3_probe_error; } @@ -1015,24 +1071,20 @@ abituguru3_probe_error: for (i = 0; i < ARRAY_SIZE(abituguru3_sysfs_attr); i++) device_remove_file(&pdev->dev, &abituguru3_sysfs_attr[i].dev_attr); - kfree(data); return res; } -static int __devexit abituguru3_remove(struct platform_device *pdev) +static int abituguru3_remove(struct platform_device *pdev) { int i; struct abituguru3_data *data = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); hwmon_device_unregister(data->hwmon_dev); for (i = 0; data->sysfs_attr[i].dev_attr.attr.name; i++) device_remove_file(&pdev->dev, &data->sysfs_attr[i].dev_attr); for (i = 0; i < ARRAY_SIZE(abituguru3_sysfs_attr); i++) device_remove_file(&pdev->dev, &abituguru3_sysfs_attr[i].dev_attr); - kfree(data); - return 0; } @@ -1088,40 +1140,41 @@ LEAVE_UPDATE: return NULL; } -#ifdef CONFIG_PM -static int abituguru3_suspend(struct platform_device *pdev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int abituguru3_suspend(struct device *dev) { - struct abituguru3_data *data = platform_get_drvdata(pdev); - /* make sure all communications with the uguru3 are done and no new - ones are started */ + struct abituguru3_data *data = dev_get_drvdata(dev); + /* + * make sure all communications with the uguru3 are done and no new + * ones are started + */ mutex_lock(&data->update_lock); return 0; } -static int abituguru3_resume(struct platform_device *pdev) +static int abituguru3_resume(struct device *dev) { - struct abituguru3_data *data = platform_get_drvdata(pdev); + struct abituguru3_data *data = dev_get_drvdata(dev); mutex_unlock(&data->update_lock); return 0; } + +static SIMPLE_DEV_PM_OPS(abituguru3_pm, abituguru3_suspend, abituguru3_resume); +#define ABIT_UGURU3_PM (&abituguru3_pm) #else -#define abituguru3_suspend NULL -#define abituguru3_resume NULL +#define ABIT_UGURU3_PM NULL #endif /* CONFIG_PM */ static struct platform_driver abituguru3_driver = { .driver = { .owner = THIS_MODULE, .name = ABIT_UGURU3_NAME, + .pm = ABIT_UGURU3_PM }, .probe = abituguru3_probe, - .remove = __devexit_p(abituguru3_remove), - .suspend = abituguru3_suspend, - .resume = abituguru3_resume + .remove = abituguru3_remove, }; -#ifdef CONFIG_DMI - static int __init abituguru3_dmi_detect(void) { const char *board_vendor, *board_name; @@ -1137,7 +1190,8 @@ static int __init abituguru3_dmi_detect(void) if (!board_name) return err; - /* At the moment, we don't care about the part of the vendor + /* + * At the moment, we don't care about the part of the vendor * DMI string contained in brackets. Truncate the string at * the first occurrence of a bracket. Trim any trailing space * from the substring. @@ -1160,24 +1214,18 @@ static int __init abituguru3_dmi_detect(void) return 1; } -#else /* !CONFIG_DMI */ - -static inline int abituguru3_dmi_detect(void) -{ - return 1; -} - -#endif /* CONFIG_DMI */ - -/* FIXME: Manual detection should die eventually; we need to collect stable +/* + * FIXME: Manual detection should die eventually; we need to collect stable * DMI model names first before we can rely entirely on CONFIG_DMI. */ static int __init abituguru3_detect(void) { - /* See if there is an uguru3 there. An idle uGuru3 will hold 0x00 or - 0x08 at DATA and 0xAC at CMD. Sometimes the uGuru3 will hold 0x05 - or 0x55 at CMD instead, why is unknown. */ + /* + * See if there is an uguru3 there. An idle uGuru3 will hold 0x00 or + * 0x08 at DATA and 0xAC at CMD. Sometimes the uGuru3 will hold 0x05 + * or 0x55 at CMD instead, why is unknown. + */ u8 data_val = inb_p(ABIT_UGURU3_BASE + ABIT_UGURU3_DATA); u8 cmd_val = inb_p(ABIT_UGURU3_BASE + ABIT_UGURU3_CMD); if (((data_val == 0x00) || (data_val == 0x08)) && @@ -1189,8 +1237,7 @@ static int __init abituguru3_detect(void) "0x%02X\n", (unsigned int)data_val, (unsigned int)cmd_val); if (force) { - printk(KERN_INFO ABIT_UGURU3_NAME ": Assuming Abit uGuru3 is " - "present because of \"force\" parameter\n"); + pr_info("Assuming Abit uGuru3 is present because of \"force\" parameter\n"); return 0; } @@ -1210,7 +1257,8 @@ static int __init abituguru3_init(void) if (err < 0) return err; - /* Fall back to manual detection if there was no exact + /* + * Fall back to manual detection if there was no exact * board name match, or force was specified. */ if (err > 0) { @@ -1218,12 +1266,8 @@ static int __init abituguru3_init(void) if (err) return err; -#ifdef CONFIG_DMI - printk(KERN_WARNING ABIT_UGURU3_NAME ": this motherboard was " - "not detected using DMI. Please send the output of " - "\"dmidecode\" to the abituguru3 maintainer " - "(see MAINTAINERS)\n"); -#endif + pr_warn("this motherboard was not detected using DMI. " + "Please send the output of \"dmidecode\" to the abituguru3 maintainer (see MAINTAINERS)\n"); } err = platform_driver_register(&abituguru3_driver); @@ -1233,8 +1277,7 @@ static int __init abituguru3_init(void) abituguru3_pdev = platform_device_alloc(ABIT_UGURU3_NAME, ABIT_UGURU3_BASE); if (!abituguru3_pdev) { - printk(KERN_ERR ABIT_UGURU3_NAME - ": Device allocation failed\n"); + pr_err("Device allocation failed\n"); err = -ENOMEM; goto exit_driver_unregister; } @@ -1245,15 +1288,13 @@ static int __init abituguru3_init(void) err = platform_device_add_resources(abituguru3_pdev, &res, 1); if (err) { - printk(KERN_ERR ABIT_UGURU3_NAME - ": Device resource addition failed (%d)\n", err); + pr_err("Device resource addition failed (%d)\n", err); goto exit_device_put; } err = platform_device_add(abituguru3_pdev); if (err) { - printk(KERN_ERR ABIT_UGURU3_NAME - ": Device addition failed (%d)\n", err); + pr_err("Device addition failed (%d)\n", err); goto exit_device_put; } @@ -1273,7 +1314,7 @@ static void __exit abituguru3_exit(void) platform_driver_unregister(&abituguru3_driver); } -MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); MODULE_DESCRIPTION("Abit uGuru3 Sensor device"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/abx500.c b/drivers/hwmon/abx500.c new file mode 100644 index 00000000000..769fe20ec93 --- /dev/null +++ b/drivers/hwmon/abx500.c @@ -0,0 +1,491 @@ +/* + * Copyright (C) ST-Ericsson 2010 - 2013 + * Author: Martin Persson <martin.persson@stericsson.com> + * Hongbo Zhang <hongbo.zhang@linaro.org> + * License Terms: GNU General Public License v2 + * + * ABX500 does not provide auto ADC, so to monitor the required temperatures, + * a periodic work is used. It is more important to not wake up the CPU than + * to perform this job, hence the use of a deferred delay. + * + * A deferred delay for thermal monitor is considered safe because: + * If the chip gets too hot during a sleep state it's most likely due to + * external factors, such as the surrounding temperature. I.e. no SW decisions + * will make any difference. + */ + +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/workqueue.h> +#include "abx500.h" + +#define DEFAULT_MONITOR_DELAY HZ +#define DEFAULT_MAX_TEMP 130 + +static inline void schedule_monitor(struct abx500_temp *data) +{ + data->work_active = true; + schedule_delayed_work(&data->work, DEFAULT_MONITOR_DELAY); +} + +static void threshold_updated(struct abx500_temp *data) +{ + int i; + for (i = 0; i < data->monitored_sensors; i++) + if (data->max[i] != 0 || data->min[i] != 0) { + schedule_monitor(data); + return; + } + + dev_dbg(&data->pdev->dev, "No active thresholds.\n"); + cancel_delayed_work_sync(&data->work); + data->work_active = false; +} + +static void gpadc_monitor(struct work_struct *work) +{ + int temp, i, ret; + char alarm_node[30]; + bool updated_min_alarm, updated_max_alarm; + struct abx500_temp *data; + + data = container_of(work, struct abx500_temp, work.work); + mutex_lock(&data->lock); + + for (i = 0; i < data->monitored_sensors; i++) { + /* Thresholds are considered inactive if set to 0 */ + if (data->max[i] == 0 && data->min[i] == 0) + continue; + + if (data->max[i] < data->min[i]) + continue; + + ret = data->ops.read_sensor(data, data->gpadc_addr[i], &temp); + if (ret < 0) { + dev_err(&data->pdev->dev, "GPADC read failed\n"); + continue; + } + + updated_min_alarm = false; + updated_max_alarm = false; + + if (data->min[i] != 0) { + if (temp < data->min[i]) { + if (data->min_alarm[i] == false) { + data->min_alarm[i] = true; + updated_min_alarm = true; + } + } else { + if (data->min_alarm[i] == true) { + data->min_alarm[i] = false; + updated_min_alarm = true; + } + } + } + if (data->max[i] != 0) { + if (temp > data->max[i]) { + if (data->max_alarm[i] == false) { + data->max_alarm[i] = true; + updated_max_alarm = true; + } + } else if (temp < data->max[i] - data->max_hyst[i]) { + if (data->max_alarm[i] == true) { + data->max_alarm[i] = false; + updated_max_alarm = true; + } + } + } + + if (updated_min_alarm) { + ret = sprintf(alarm_node, "temp%d_min_alarm", i + 1); + sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node); + } + if (updated_max_alarm) { + ret = sprintf(alarm_node, "temp%d_max_alarm", i + 1); + sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node); + } + } + + schedule_monitor(data); + mutex_unlock(&data->lock); +} + +/* HWMON sysfs interfaces */ +static ssize_t show_name(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct abx500_temp *data = dev_get_drvdata(dev); + /* Show chip name */ + return data->ops.show_name(dev, devattr, buf); +} + +static ssize_t show_label(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct abx500_temp *data = dev_get_drvdata(dev); + /* Show each sensor label */ + return data->ops.show_label(dev, devattr, buf); +} + +static ssize_t show_input(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int ret, temp; + struct abx500_temp *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + u8 gpadc_addr = data->gpadc_addr[attr->index]; + + ret = data->ops.read_sensor(data, gpadc_addr, &temp); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", temp); +} + +/* Set functions (RW nodes) */ +static ssize_t set_min(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned long val; + struct abx500_temp *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int res = kstrtol(buf, 10, &val); + if (res < 0) + return res; + + val = clamp_val(val, 0, DEFAULT_MAX_TEMP); + + mutex_lock(&data->lock); + data->min[attr->index] = val; + threshold_updated(data); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t set_max(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned long val; + struct abx500_temp *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int res = kstrtol(buf, 10, &val); + if (res < 0) + return res; + + val = clamp_val(val, 0, DEFAULT_MAX_TEMP); + + mutex_lock(&data->lock); + data->max[attr->index] = val; + threshold_updated(data); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t set_max_hyst(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + unsigned long val; + struct abx500_temp *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int res = kstrtoul(buf, 10, &val); + if (res < 0) + return res; + + val = clamp_val(val, 0, DEFAULT_MAX_TEMP); + + mutex_lock(&data->lock); + data->max_hyst[attr->index] = val; + threshold_updated(data); + mutex_unlock(&data->lock); + + return count; +} + +/* Show functions (RO nodes) */ +static ssize_t show_min(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct abx500_temp *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + + return sprintf(buf, "%ld\n", data->min[attr->index]); +} + +static ssize_t show_max(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct abx500_temp *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + + return sprintf(buf, "%ld\n", data->max[attr->index]); +} + +static ssize_t show_max_hyst(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct abx500_temp *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + + return sprintf(buf, "%ld\n", data->max_hyst[attr->index]); +} + +static ssize_t show_min_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct abx500_temp *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + + return sprintf(buf, "%d\n", data->min_alarm[attr->index]); +} + +static ssize_t show_max_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct abx500_temp *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + + return sprintf(buf, "%d\n", data->max_alarm[attr->index]); +} + +static umode_t abx500_attrs_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct abx500_temp *data = dev_get_drvdata(dev); + + if (data->ops.is_visible) + return data->ops.is_visible(attr, n); + + return attr->mode; +} + +/* Chip name, required by hwmon */ +static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0); + +/* GPADC - SENSOR1 */ +static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_min, set_min, 0); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_max, set_max, 0); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, + show_max_hyst, set_max_hyst, 0); +static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_min_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_max_alarm, NULL, 0); + +/* GPADC - SENSOR2 */ +static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_min, set_min, 1); +static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_max, set_max, 1); +static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IWUSR | S_IRUGO, + show_max_hyst, set_max_hyst, 1); +static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_min_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_max_alarm, NULL, 1); + +/* GPADC - SENSOR3 */ +static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, show_label, NULL, 2); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_input, NULL, 2); +static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_min, set_min, 2); +static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_max, set_max, 2); +static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IWUSR | S_IRUGO, + show_max_hyst, set_max_hyst, 2); +static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_min_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_max_alarm, NULL, 2); + +/* GPADC - SENSOR4 */ +static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, show_label, NULL, 3); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_input, NULL, 3); +static SENSOR_DEVICE_ATTR(temp4_min, S_IWUSR | S_IRUGO, show_min, set_min, 3); +static SENSOR_DEVICE_ATTR(temp4_max, S_IWUSR | S_IRUGO, show_max, set_max, 3); +static SENSOR_DEVICE_ATTR(temp4_max_hyst, S_IWUSR | S_IRUGO, + show_max_hyst, set_max_hyst, 3); +static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_min_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_max_alarm, NULL, 3); + +static struct attribute *abx500_temp_attributes[] = { + &sensor_dev_attr_name.dev_attr.attr, + + &sensor_dev_attr_temp1_label.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + + &sensor_dev_attr_temp2_label.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + + &sensor_dev_attr_temp3_label.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, + + &sensor_dev_attr_temp4_label.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp4_min.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp4_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp4_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group abx500_temp_group = { + .attrs = abx500_temp_attributes, + .is_visible = abx500_attrs_visible, +}; + +static irqreturn_t abx500_temp_irq_handler(int irq, void *irq_data) +{ + struct platform_device *pdev = irq_data; + struct abx500_temp *data = platform_get_drvdata(pdev); + + data->ops.irq_handler(irq, data); + return IRQ_HANDLED; +} + +static int setup_irqs(struct platform_device *pdev) +{ + int ret; + int irq = platform_get_irq_byname(pdev, "ABX500_TEMP_WARM"); + + if (irq < 0) { + dev_err(&pdev->dev, "Get irq by name failed\n"); + return irq; + } + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + abx500_temp_irq_handler, IRQF_NO_SUSPEND, "abx500-temp", pdev); + if (ret < 0) + dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret); + + return ret; +} + +static int abx500_temp_probe(struct platform_device *pdev) +{ + struct abx500_temp *data; + int err; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->pdev = pdev; + mutex_init(&data->lock); + + /* Chip specific initialization */ + err = abx500_hwmon_init(data); + if (err < 0 || !data->ops.read_sensor || !data->ops.show_name || + !data->ops.show_label) + return err; + + INIT_DEFERRABLE_WORK(&data->work, gpadc_monitor); + + platform_set_drvdata(pdev, data); + + err = sysfs_create_group(&pdev->dev.kobj, &abx500_temp_group); + if (err < 0) { + dev_err(&pdev->dev, "Create sysfs group failed (%d)\n", err); + return err; + } + + data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + dev_err(&pdev->dev, "Class registration failed (%d)\n", err); + goto exit_sysfs_group; + } + + if (data->ops.irq_handler) { + err = setup_irqs(pdev); + if (err < 0) + goto exit_hwmon_reg; + } + return 0; + +exit_hwmon_reg: + hwmon_device_unregister(data->hwmon_dev); +exit_sysfs_group: + sysfs_remove_group(&pdev->dev.kobj, &abx500_temp_group); + return err; +} + +static int abx500_temp_remove(struct platform_device *pdev) +{ + struct abx500_temp *data = platform_get_drvdata(pdev); + + cancel_delayed_work_sync(&data->work); + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&pdev->dev.kobj, &abx500_temp_group); + + return 0; +} + +static int abx500_temp_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct abx500_temp *data = platform_get_drvdata(pdev); + + if (data->work_active) + cancel_delayed_work_sync(&data->work); + + return 0; +} + +static int abx500_temp_resume(struct platform_device *pdev) +{ + struct abx500_temp *data = platform_get_drvdata(pdev); + + if (data->work_active) + schedule_monitor(data); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id abx500_temp_match[] = { + { .compatible = "stericsson,abx500-temp" }, + {}, +}; +#endif + +static struct platform_driver abx500_temp_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "abx500-temp", + .of_match_table = of_match_ptr(abx500_temp_match), + }, + .suspend = abx500_temp_suspend, + .resume = abx500_temp_resume, + .probe = abx500_temp_probe, + .remove = abx500_temp_remove, +}; + +module_platform_driver(abx500_temp_driver); + +MODULE_AUTHOR("Martin Persson <martin.persson@stericsson.com>"); +MODULE_DESCRIPTION("ABX500 temperature driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/abx500.h b/drivers/hwmon/abx500.h new file mode 100644 index 00000000000..9b295e684d0 --- /dev/null +++ b/drivers/hwmon/abx500.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) ST-Ericsson 2010 - 2013 + * License terms: GNU General Public License v2 + * Author: Martin Persson <martin.persson@stericsson.com> + * Hongbo Zhang <hongbo.zhang@linaro.com> + */ + +#ifndef _ABX500_H +#define _ABX500_H + +#define NUM_SENSORS 5 + +struct abx500_temp; + +/* + * struct abx500_temp_ops - abx500 chip specific ops + * @read_sensor: reads gpadc output + * @irq_handler: irq handler + * @show_name: hwmon device name + * @show_label: hwmon attribute label + * @is_visible: is attribute visible + */ +struct abx500_temp_ops { + int (*read_sensor)(struct abx500_temp *, u8, int *); + int (*irq_handler)(int, struct abx500_temp *); + ssize_t (*show_name)(struct device *, + struct device_attribute *, char *); + ssize_t (*show_label) (struct device *, + struct device_attribute *, char *); + int (*is_visible)(struct attribute *, int); +}; + +/* + * struct abx500_temp - representation of temp mon device + * @pdev: platform device + * @hwmon_dev: hwmon device + * @ops: abx500 chip specific ops + * @gpadc_addr: gpadc channel address + * @min: sensor temperature min value + * @max: sensor temperature max value + * @max_hyst: sensor temperature hysteresis value for max limit + * @min_alarm: sensor temperature min alarm + * @max_alarm: sensor temperature max alarm + * @work: delayed work scheduled to monitor temperature periodically + * @work_active: True if work is active + * @lock: mutex + * @monitored_sensors: number of monitored sensors + * @plat_data: private usage, usually points to platform specific data + */ +struct abx500_temp { + struct platform_device *pdev; + struct device *hwmon_dev; + struct abx500_temp_ops ops; + u8 gpadc_addr[NUM_SENSORS]; + unsigned long min[NUM_SENSORS]; + unsigned long max[NUM_SENSORS]; + unsigned long max_hyst[NUM_SENSORS]; + bool min_alarm[NUM_SENSORS]; + bool max_alarm[NUM_SENSORS]; + struct delayed_work work; + bool work_active; + struct mutex lock; + int monitored_sensors; + void *plat_data; +}; + +int abx500_hwmon_init(struct abx500_temp *data); + +#endif /* _ABX500_H */ diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c new file mode 100644 index 00000000000..579bdf93be4 --- /dev/null +++ b/drivers/hwmon/acpi_power_meter.c @@ -0,0 +1,1015 @@ +/* + * A hwmon driver for ACPI 4.0 power meters + * Copyright (C) 2009 IBM + * + * Author: Darrick J. Wong <darrick.wong@oracle.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/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/jiffies.h> +#include <linux/mutex.h> +#include <linux/dmi.h> +#include <linux/slab.h> +#include <linux/kdev_t.h> +#include <linux/sched.h> +#include <linux/time.h> +#include <linux/err.h> +#include <linux/acpi.h> + +#define ACPI_POWER_METER_NAME "power_meter" +ACPI_MODULE_NAME(ACPI_POWER_METER_NAME); +#define ACPI_POWER_METER_DEVICE_NAME "Power Meter" +#define ACPI_POWER_METER_CLASS "pwr_meter_resource" + +#define NUM_SENSORS 17 + +#define POWER_METER_CAN_MEASURE (1 << 0) +#define POWER_METER_CAN_TRIP (1 << 1) +#define POWER_METER_CAN_CAP (1 << 2) +#define POWER_METER_CAN_NOTIFY (1 << 3) +#define POWER_METER_IS_BATTERY (1 << 8) +#define UNKNOWN_HYSTERESIS 0xFFFFFFFF + +#define METER_NOTIFY_CONFIG 0x80 +#define METER_NOTIFY_TRIP 0x81 +#define METER_NOTIFY_CAP 0x82 +#define METER_NOTIFY_CAPPING 0x83 +#define METER_NOTIFY_INTERVAL 0x84 + +#define POWER_AVERAGE_NAME "power1_average" +#define POWER_CAP_NAME "power1_cap" +#define POWER_AVG_INTERVAL_NAME "power1_average_interval" +#define POWER_ALARM_NAME "power1_alarm" + +static int cap_in_hardware; +static bool force_cap_on; + +static int can_cap_in_hardware(void) +{ + return force_cap_on || cap_in_hardware; +} + +static const struct acpi_device_id power_meter_ids[] = { + {"ACPI000D", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, power_meter_ids); + +struct acpi_power_meter_capabilities { + u64 flags; + u64 units; + u64 type; + u64 accuracy; + u64 sampling_time; + u64 min_avg_interval; + u64 max_avg_interval; + u64 hysteresis; + u64 configurable_cap; + u64 min_cap; + u64 max_cap; +}; + +struct acpi_power_meter_resource { + struct acpi_device *acpi_dev; + acpi_bus_id name; + struct mutex lock; + struct device *hwmon_dev; + struct acpi_power_meter_capabilities caps; + acpi_string model_number; + acpi_string serial_number; + acpi_string oem_info; + u64 power; + u64 cap; + u64 avg_interval; + int sensors_valid; + unsigned long sensors_last_updated; + struct sensor_device_attribute sensors[NUM_SENSORS]; + int num_sensors; + s64 trip[2]; + int num_domain_devices; + struct acpi_device **domain_devices; + struct kobject *holders_dir; +}; + +struct sensor_template { + char *label; + ssize_t (*show)(struct device *dev, + struct device_attribute *devattr, + char *buf); + ssize_t (*set)(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count); + int index; +}; + +/* Averaging interval */ +static int update_avg_interval(struct acpi_power_meter_resource *resource) +{ + unsigned long long data; + acpi_status status; + + status = acpi_evaluate_integer(resource->acpi_dev->handle, "_GAI", + NULL, &data); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _GAI")); + return -ENODEV; + } + + resource->avg_interval = data; + return 0; +} + +static ssize_t show_avg_interval(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + + mutex_lock(&resource->lock); + update_avg_interval(resource); + mutex_unlock(&resource->lock); + + return sprintf(buf, "%llu\n", resource->avg_interval); +} + +static ssize_t set_avg_interval(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { 1, &arg0 }; + int res; + unsigned long temp; + unsigned long long data; + acpi_status status; + + res = kstrtoul(buf, 10, &temp); + if (res) + return res; + + if (temp > resource->caps.max_avg_interval || + temp < resource->caps.min_avg_interval) + return -EINVAL; + arg0.integer.value = temp; + + mutex_lock(&resource->lock); + status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PAI", + &args, &data); + if (!ACPI_FAILURE(status)) + resource->avg_interval = temp; + mutex_unlock(&resource->lock); + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PAI")); + return -EINVAL; + } + + /* _PAI returns 0 on success, nonzero otherwise */ + if (data) + return -EINVAL; + + return count; +} + +/* Cap functions */ +static int update_cap(struct acpi_power_meter_resource *resource) +{ + unsigned long long data; + acpi_status status; + + status = acpi_evaluate_integer(resource->acpi_dev->handle, "_GHL", + NULL, &data); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _GHL")); + return -ENODEV; + } + + resource->cap = data; + return 0; +} + +static ssize_t show_cap(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + + mutex_lock(&resource->lock); + update_cap(resource); + mutex_unlock(&resource->lock); + + return sprintf(buf, "%llu\n", resource->cap * 1000); +} + +static ssize_t set_cap(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { 1, &arg0 }; + int res; + unsigned long temp; + unsigned long long data; + acpi_status status; + + res = kstrtoul(buf, 10, &temp); + if (res) + return res; + + temp = DIV_ROUND_CLOSEST(temp, 1000); + if (temp > resource->caps.max_cap || temp < resource->caps.min_cap) + return -EINVAL; + arg0.integer.value = temp; + + mutex_lock(&resource->lock); + status = acpi_evaluate_integer(resource->acpi_dev->handle, "_SHL", + &args, &data); + if (!ACPI_FAILURE(status)) + resource->cap = temp; + mutex_unlock(&resource->lock); + + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _SHL")); + return -EINVAL; + } + + /* _SHL returns 0 on success, nonzero otherwise */ + if (data) + return -EINVAL; + + return count; +} + +/* Power meter trip points */ +static int set_acpi_trip(struct acpi_power_meter_resource *resource) +{ + union acpi_object arg_objs[] = { + {ACPI_TYPE_INTEGER}, + {ACPI_TYPE_INTEGER} + }; + struct acpi_object_list args = { 2, arg_objs }; + unsigned long long data; + acpi_status status; + + /* Both trip levels must be set */ + if (resource->trip[0] < 0 || resource->trip[1] < 0) + return 0; + + /* This driver stores min, max; ACPI wants max, min. */ + arg_objs[0].integer.value = resource->trip[1]; + arg_objs[1].integer.value = resource->trip[0]; + + status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PTP", + &args, &data); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PTP")); + return -EINVAL; + } + + /* _PTP returns 0 on success, nonzero otherwise */ + if (data) + return -EINVAL; + + return 0; +} + +static ssize_t set_trip(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + int res; + unsigned long temp; + + res = kstrtoul(buf, 10, &temp); + if (res) + return res; + + temp = DIV_ROUND_CLOSEST(temp, 1000); + + mutex_lock(&resource->lock); + resource->trip[attr->index - 7] = temp; + res = set_acpi_trip(resource); + mutex_unlock(&resource->lock); + + if (res) + return res; + + return count; +} + +/* Power meter */ +static int update_meter(struct acpi_power_meter_resource *resource) +{ + unsigned long long data; + acpi_status status; + unsigned long local_jiffies = jiffies; + + if (time_before(local_jiffies, resource->sensors_last_updated + + msecs_to_jiffies(resource->caps.sampling_time)) && + resource->sensors_valid) + return 0; + + status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PMM", + NULL, &data); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PMM")); + return -ENODEV; + } + + resource->power = data; + resource->sensors_valid = 1; + resource->sensors_last_updated = jiffies; + return 0; +} + +static ssize_t show_power(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + + mutex_lock(&resource->lock); + update_meter(resource); + mutex_unlock(&resource->lock); + + return sprintf(buf, "%llu\n", resource->power * 1000); +} + +/* Miscellaneous */ +static ssize_t show_str(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + acpi_string val; + + switch (attr->index) { + case 0: + val = resource->model_number; + break; + case 1: + val = resource->serial_number; + break; + case 2: + val = resource->oem_info; + break; + default: + WARN(1, "Implementation error: unexpected attribute index %d\n", + attr->index); + val = ""; + break; + } + + return sprintf(buf, "%s\n", val); +} + +static ssize_t show_val(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + u64 val = 0; + + switch (attr->index) { + case 0: + val = resource->caps.min_avg_interval; + break; + case 1: + val = resource->caps.max_avg_interval; + break; + case 2: + val = resource->caps.min_cap * 1000; + break; + case 3: + val = resource->caps.max_cap * 1000; + break; + case 4: + if (resource->caps.hysteresis == UNKNOWN_HYSTERESIS) + return sprintf(buf, "unknown\n"); + + val = resource->caps.hysteresis * 1000; + break; + case 5: + if (resource->caps.flags & POWER_METER_IS_BATTERY) + val = 1; + else + val = 0; + break; + case 6: + if (resource->power > resource->cap) + val = 1; + else + val = 0; + break; + case 7: + case 8: + if (resource->trip[attr->index - 7] < 0) + return sprintf(buf, "unknown\n"); + + val = resource->trip[attr->index - 7] * 1000; + break; + default: + WARN(1, "Implementation error: unexpected attribute index %d\n", + attr->index); + break; + } + + return sprintf(buf, "%llu\n", val); +} + +static ssize_t show_accuracy(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + unsigned int acc = resource->caps.accuracy; + + return sprintf(buf, "%u.%u%%\n", acc / 1000, acc % 1000); +} + +static ssize_t show_name(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + return sprintf(buf, "%s\n", ACPI_POWER_METER_NAME); +} + +#define RO_SENSOR_TEMPLATE(_label, _show, _index) \ + { \ + .label = _label, \ + .show = _show, \ + .index = _index, \ + } + +#define RW_SENSOR_TEMPLATE(_label, _show, _set, _index) \ + { \ + .label = _label, \ + .show = _show, \ + .set = _set, \ + .index = _index, \ + } + +/* Sensor descriptions. If you add a sensor, update NUM_SENSORS above! */ +static struct sensor_template meter_attrs[] = { + RO_SENSOR_TEMPLATE(POWER_AVERAGE_NAME, show_power, 0), + RO_SENSOR_TEMPLATE("power1_accuracy", show_accuracy, 0), + RO_SENSOR_TEMPLATE("power1_average_interval_min", show_val, 0), + RO_SENSOR_TEMPLATE("power1_average_interval_max", show_val, 1), + RO_SENSOR_TEMPLATE("power1_is_battery", show_val, 5), + RW_SENSOR_TEMPLATE(POWER_AVG_INTERVAL_NAME, show_avg_interval, + set_avg_interval, 0), + {}, +}; + +static struct sensor_template misc_cap_attrs[] = { + RO_SENSOR_TEMPLATE("power1_cap_min", show_val, 2), + RO_SENSOR_TEMPLATE("power1_cap_max", show_val, 3), + RO_SENSOR_TEMPLATE("power1_cap_hyst", show_val, 4), + RO_SENSOR_TEMPLATE(POWER_ALARM_NAME, show_val, 6), + {}, +}; + +static struct sensor_template ro_cap_attrs[] = { + RO_SENSOR_TEMPLATE(POWER_CAP_NAME, show_cap, 0), + {}, +}; + +static struct sensor_template rw_cap_attrs[] = { + RW_SENSOR_TEMPLATE(POWER_CAP_NAME, show_cap, set_cap, 0), + {}, +}; + +static struct sensor_template trip_attrs[] = { + RW_SENSOR_TEMPLATE("power1_average_min", show_val, set_trip, 7), + RW_SENSOR_TEMPLATE("power1_average_max", show_val, set_trip, 8), + {}, +}; + +static struct sensor_template misc_attrs[] = { + RO_SENSOR_TEMPLATE("name", show_name, 0), + RO_SENSOR_TEMPLATE("power1_model_number", show_str, 0), + RO_SENSOR_TEMPLATE("power1_oem_info", show_str, 2), + RO_SENSOR_TEMPLATE("power1_serial_number", show_str, 1), + {}, +}; + +#undef RO_SENSOR_TEMPLATE +#undef RW_SENSOR_TEMPLATE + +/* Read power domain data */ +static void remove_domain_devices(struct acpi_power_meter_resource *resource) +{ + int i; + + if (!resource->num_domain_devices) + return; + + for (i = 0; i < resource->num_domain_devices; i++) { + struct acpi_device *obj = resource->domain_devices[i]; + if (!obj) + continue; + + sysfs_remove_link(resource->holders_dir, + kobject_name(&obj->dev.kobj)); + put_device(&obj->dev); + } + + kfree(resource->domain_devices); + kobject_put(resource->holders_dir); + resource->num_domain_devices = 0; +} + +static int read_domain_devices(struct acpi_power_meter_resource *resource) +{ + int res = 0; + int i; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *pss; + acpi_status status; + + status = acpi_evaluate_object(resource->acpi_dev->handle, "_PMD", NULL, + &buffer); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PMD")); + return -ENODEV; + } + + pss = buffer.pointer; + if (!pss || + pss->type != ACPI_TYPE_PACKAGE) { + dev_err(&resource->acpi_dev->dev, ACPI_POWER_METER_NAME + "Invalid _PMD data\n"); + res = -EFAULT; + goto end; + } + + if (!pss->package.count) + goto end; + + resource->domain_devices = kzalloc(sizeof(struct acpi_device *) * + pss->package.count, GFP_KERNEL); + if (!resource->domain_devices) { + res = -ENOMEM; + goto end; + } + + resource->holders_dir = kobject_create_and_add("measures", + &resource->acpi_dev->dev.kobj); + if (!resource->holders_dir) { + res = -ENOMEM; + goto exit_free; + } + + resource->num_domain_devices = pss->package.count; + + for (i = 0; i < pss->package.count; i++) { + struct acpi_device *obj; + union acpi_object *element = &(pss->package.elements[i]); + + /* Refuse non-references */ + if (element->type != ACPI_TYPE_LOCAL_REFERENCE) + continue; + + /* Create a symlink to domain objects */ + resource->domain_devices[i] = NULL; + if (acpi_bus_get_device(element->reference.handle, + &resource->domain_devices[i])) + continue; + + obj = resource->domain_devices[i]; + get_device(&obj->dev); + + res = sysfs_create_link(resource->holders_dir, &obj->dev.kobj, + kobject_name(&obj->dev.kobj)); + if (res) { + put_device(&obj->dev); + resource->domain_devices[i] = NULL; + } + } + + res = 0; + goto end; + +exit_free: + kfree(resource->domain_devices); +end: + kfree(buffer.pointer); + return res; +} + +/* Registration and deregistration */ +static int register_attrs(struct acpi_power_meter_resource *resource, + struct sensor_template *attrs) +{ + struct device *dev = &resource->acpi_dev->dev; + struct sensor_device_attribute *sensors = + &resource->sensors[resource->num_sensors]; + int res = 0; + + while (attrs->label) { + sensors->dev_attr.attr.name = attrs->label; + sensors->dev_attr.attr.mode = S_IRUGO; + sensors->dev_attr.show = attrs->show; + sensors->index = attrs->index; + + if (attrs->set) { + sensors->dev_attr.attr.mode |= S_IWUSR; + sensors->dev_attr.store = attrs->set; + } + + sysfs_attr_init(&sensors->dev_attr.attr); + res = device_create_file(dev, &sensors->dev_attr); + if (res) { + sensors->dev_attr.attr.name = NULL; + goto error; + } + sensors++; + resource->num_sensors++; + attrs++; + } + +error: + return res; +} + +static void remove_attrs(struct acpi_power_meter_resource *resource) +{ + int i; + + for (i = 0; i < resource->num_sensors; i++) { + if (!resource->sensors[i].dev_attr.attr.name) + continue; + device_remove_file(&resource->acpi_dev->dev, + &resource->sensors[i].dev_attr); + } + + remove_domain_devices(resource); + + resource->num_sensors = 0; +} + +static int setup_attrs(struct acpi_power_meter_resource *resource) +{ + int res = 0; + + res = read_domain_devices(resource); + if (res) + return res; + + if (resource->caps.flags & POWER_METER_CAN_MEASURE) { + res = register_attrs(resource, meter_attrs); + if (res) + goto error; + } + + if (resource->caps.flags & POWER_METER_CAN_CAP) { + if (!can_cap_in_hardware()) { + dev_err(&resource->acpi_dev->dev, + "Ignoring unsafe software power cap!\n"); + goto skip_unsafe_cap; + } + + if (resource->caps.configurable_cap) + res = register_attrs(resource, rw_cap_attrs); + else + res = register_attrs(resource, ro_cap_attrs); + + if (res) + goto error; + + res = register_attrs(resource, misc_cap_attrs); + if (res) + goto error; + } + +skip_unsafe_cap: + if (resource->caps.flags & POWER_METER_CAN_TRIP) { + res = register_attrs(resource, trip_attrs); + if (res) + goto error; + } + + res = register_attrs(resource, misc_attrs); + if (res) + goto error; + + return res; +error: + remove_attrs(resource); + return res; +} + +static void free_capabilities(struct acpi_power_meter_resource *resource) +{ + acpi_string *str; + int i; + + str = &resource->model_number; + for (i = 0; i < 3; i++, str++) + kfree(*str); +} + +static int read_capabilities(struct acpi_power_meter_resource *resource) +{ + int res = 0; + int i; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer state = { 0, NULL }; + struct acpi_buffer format = { sizeof("NNNNNNNNNNN"), "NNNNNNNNNNN" }; + union acpi_object *pss; + acpi_string *str; + acpi_status status; + + status = acpi_evaluate_object(resource->acpi_dev->handle, "_PMC", NULL, + &buffer); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PMC")); + return -ENODEV; + } + + pss = buffer.pointer; + if (!pss || + pss->type != ACPI_TYPE_PACKAGE || + pss->package.count != 14) { + dev_err(&resource->acpi_dev->dev, ACPI_POWER_METER_NAME + "Invalid _PMC data\n"); + res = -EFAULT; + goto end; + } + + /* Grab all the integer data at once */ + state.length = sizeof(struct acpi_power_meter_capabilities); + state.pointer = &resource->caps; + + status = acpi_extract_package(pss, &format, &state); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Invalid data")); + res = -EFAULT; + goto end; + } + + if (resource->caps.units) { + dev_err(&resource->acpi_dev->dev, ACPI_POWER_METER_NAME + "Unknown units %llu.\n", + resource->caps.units); + res = -EINVAL; + goto end; + } + + /* Grab the string data */ + str = &resource->model_number; + + for (i = 11; i < 14; i++) { + union acpi_object *element = &(pss->package.elements[i]); + + if (element->type != ACPI_TYPE_STRING) { + res = -EINVAL; + goto error; + } + + *str = kzalloc(sizeof(u8) * (element->string.length + 1), + GFP_KERNEL); + if (!*str) { + res = -ENOMEM; + goto error; + } + + strncpy(*str, element->string.pointer, element->string.length); + str++; + } + + dev_info(&resource->acpi_dev->dev, "Found ACPI power meter.\n"); + goto end; +error: + str = &resource->model_number; + for (i = 0; i < 3; i++, str++) + kfree(*str); +end: + kfree(buffer.pointer); + return res; +} + +/* Handle ACPI event notifications */ +static void acpi_power_meter_notify(struct acpi_device *device, u32 event) +{ + struct acpi_power_meter_resource *resource; + int res; + + if (!device || !acpi_driver_data(device)) + return; + + resource = acpi_driver_data(device); + + mutex_lock(&resource->lock); + switch (event) { + case METER_NOTIFY_CONFIG: + free_capabilities(resource); + res = read_capabilities(resource); + if (res) + break; + + remove_attrs(resource); + setup_attrs(resource); + break; + case METER_NOTIFY_TRIP: + sysfs_notify(&device->dev.kobj, NULL, POWER_AVERAGE_NAME); + update_meter(resource); + break; + case METER_NOTIFY_CAP: + sysfs_notify(&device->dev.kobj, NULL, POWER_CAP_NAME); + update_cap(resource); + break; + case METER_NOTIFY_INTERVAL: + sysfs_notify(&device->dev.kobj, NULL, POWER_AVG_INTERVAL_NAME); + update_avg_interval(resource); + break; + case METER_NOTIFY_CAPPING: + sysfs_notify(&device->dev.kobj, NULL, POWER_ALARM_NAME); + dev_info(&device->dev, "Capping in progress.\n"); + break; + default: + WARN(1, "Unexpected event %d\n", event); + break; + } + mutex_unlock(&resource->lock); + + acpi_bus_generate_netlink_event(ACPI_POWER_METER_CLASS, + dev_name(&device->dev), event, 0); +} + +static int acpi_power_meter_add(struct acpi_device *device) +{ + int res; + struct acpi_power_meter_resource *resource; + + if (!device) + return -EINVAL; + + resource = kzalloc(sizeof(struct acpi_power_meter_resource), + GFP_KERNEL); + if (!resource) + return -ENOMEM; + + resource->sensors_valid = 0; + resource->acpi_dev = device; + mutex_init(&resource->lock); + strcpy(acpi_device_name(device), ACPI_POWER_METER_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_POWER_METER_CLASS); + device->driver_data = resource; + + free_capabilities(resource); + res = read_capabilities(resource); + if (res) + goto exit_free; + + resource->trip[0] = resource->trip[1] = -1; + + res = setup_attrs(resource); + if (res) + goto exit_free; + + resource->hwmon_dev = hwmon_device_register(&device->dev); + if (IS_ERR(resource->hwmon_dev)) { + res = PTR_ERR(resource->hwmon_dev); + goto exit_remove; + } + + res = 0; + goto exit; + +exit_remove: + remove_attrs(resource); +exit_free: + kfree(resource); +exit: + return res; +} + +static int acpi_power_meter_remove(struct acpi_device *device) +{ + struct acpi_power_meter_resource *resource; + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + + resource = acpi_driver_data(device); + hwmon_device_unregister(resource->hwmon_dev); + + free_capabilities(resource); + remove_attrs(resource); + + kfree(resource); + return 0; +} + +#ifdef CONFIG_PM_SLEEP + +static int acpi_power_meter_resume(struct device *dev) +{ + struct acpi_power_meter_resource *resource; + + if (!dev) + return -EINVAL; + + resource = acpi_driver_data(to_acpi_device(dev)); + if (!resource) + return -EINVAL; + + free_capabilities(resource); + read_capabilities(resource); + + return 0; +} + +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(acpi_power_meter_pm, NULL, acpi_power_meter_resume); + +static struct acpi_driver acpi_power_meter_driver = { + .name = "power_meter", + .class = ACPI_POWER_METER_CLASS, + .ids = power_meter_ids, + .ops = { + .add = acpi_power_meter_add, + .remove = acpi_power_meter_remove, + .notify = acpi_power_meter_notify, + }, + .drv.pm = &acpi_power_meter_pm, +}; + +/* Module init/exit routines */ +static int __init enable_cap_knobs(const struct dmi_system_id *d) +{ + cap_in_hardware = 1; + return 0; +} + +static struct dmi_system_id __initdata pm_dmi_table[] = { + { + enable_cap_knobs, "IBM Active Energy Manager", + { + DMI_MATCH(DMI_SYS_VENDOR, "IBM") + }, + }, + {} +}; + +static int __init acpi_power_meter_init(void) +{ + int result; + + if (acpi_disabled) + return -ENODEV; + + dmi_check_system(pm_dmi_table); + + result = acpi_bus_register_driver(&acpi_power_meter_driver); + if (result < 0) + return result; + + return 0; +} + +static void __exit acpi_power_meter_exit(void) +{ + acpi_bus_unregister_driver(&acpi_power_meter_driver); +} + +MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>"); +MODULE_DESCRIPTION("ACPI 4.0 power meter driver"); +MODULE_LICENSE("GPL"); + +module_param(force_cap_on, bool, 0644); +MODULE_PARM_DESC(force_cap_on, "Enable power cap even it is unsafe to do so."); + +module_init(acpi_power_meter_init); +module_exit(acpi_power_meter_exit); diff --git a/drivers/hwmon/ad7314.c b/drivers/hwmon/ad7314.c new file mode 100644 index 00000000000..f4f9b219bf1 --- /dev/null +++ b/drivers/hwmon/ad7314.c @@ -0,0 +1,170 @@ +/* + * AD7314 digital temperature sensor driver for AD7314, ADT7301 and ADT7302 + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + * + * Conversion to hwmon from IIO done by Jonathan Cameron <jic23@cam.ac.uk> + */ +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/spi/spi.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> + +/* + * AD7314 temperature masks + */ +#define AD7314_TEMP_MASK 0x7FE0 +#define AD7314_TEMP_SHIFT 5 + +/* + * ADT7301 and ADT7302 temperature masks + */ +#define ADT7301_TEMP_MASK 0x3FFF + +enum ad7314_variant { + adt7301, + adt7302, + ad7314, +}; + +struct ad7314_data { + struct spi_device *spi_dev; + struct device *hwmon_dev; + u16 rx ____cacheline_aligned; +}; + +static int ad7314_spi_read(struct ad7314_data *chip) +{ + int ret; + + ret = spi_read(chip->spi_dev, (u8 *)&chip->rx, sizeof(chip->rx)); + if (ret < 0) { + dev_err(&chip->spi_dev->dev, "SPI read error\n"); + return ret; + } + + return be16_to_cpu(chip->rx); +} + +static ssize_t ad7314_show_temperature(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ad7314_data *chip = dev_get_drvdata(dev); + s16 data; + int ret; + + ret = ad7314_spi_read(chip); + if (ret < 0) + return ret; + switch (spi_get_device_id(chip->spi_dev)->driver_data) { + case ad7314: + data = (ret & AD7314_TEMP_MASK) >> AD7314_TEMP_SHIFT; + data = (data << 6) >> 6; + + return sprintf(buf, "%d\n", 250 * data); + case adt7301: + case adt7302: + /* + * Documented as a 13 bit twos complement register + * with a sign bit - which is a 14 bit 2's complement + * register. 1lsb - 31.25 milli degrees centigrade + */ + data = ret & ADT7301_TEMP_MASK; + data = (data << 2) >> 2; + + return sprintf(buf, "%d\n", + DIV_ROUND_CLOSEST(data * 3125, 100)); + default: + return -EINVAL; + } +} + +static ssize_t ad7314_show_name(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + return sprintf(buf, "%s\n", to_spi_device(dev)->modalias); +} + +static DEVICE_ATTR(name, S_IRUGO, ad7314_show_name, NULL); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, + ad7314_show_temperature, NULL, 0); + +static struct attribute *ad7314_attributes[] = { + &dev_attr_name.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad7314_group = { + .attrs = ad7314_attributes, +}; + +static int ad7314_probe(struct spi_device *spi_dev) +{ + int ret; + struct ad7314_data *chip; + + chip = devm_kzalloc(&spi_dev->dev, sizeof(*chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + spi_set_drvdata(spi_dev, chip); + + ret = sysfs_create_group(&spi_dev->dev.kobj, &ad7314_group); + if (ret < 0) + return ret; + + chip->hwmon_dev = hwmon_device_register(&spi_dev->dev); + if (IS_ERR(chip->hwmon_dev)) { + ret = PTR_ERR(chip->hwmon_dev); + goto error_remove_group; + } + chip->spi_dev = spi_dev; + + return 0; +error_remove_group: + sysfs_remove_group(&spi_dev->dev.kobj, &ad7314_group); + return ret; +} + +static int ad7314_remove(struct spi_device *spi_dev) +{ + struct ad7314_data *chip = spi_get_drvdata(spi_dev); + + hwmon_device_unregister(chip->hwmon_dev); + sysfs_remove_group(&spi_dev->dev.kobj, &ad7314_group); + + return 0; +} + +static const struct spi_device_id ad7314_id[] = { + { "adt7301", adt7301 }, + { "adt7302", adt7302 }, + { "ad7314", ad7314 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7314_id); + +static struct spi_driver ad7314_driver = { + .driver = { + .name = "ad7314", + .owner = THIS_MODULE, + }, + .probe = ad7314_probe, + .remove = ad7314_remove, + .id_table = ad7314_id, +}; + +module_spi_driver(ad7314_driver); + +MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7314, ADT7301 and ADT7302 digital temperature sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/ad7414.c b/drivers/hwmon/ad7414.c index bfda8c80ef2..5d501adc3e5 100644 --- a/drivers/hwmon/ad7414.c +++ b/drivers/hwmon/ad7414.c @@ -27,6 +27,7 @@ #include <linux/err.h> #include <linux/mutex.h> #include <linux/sysfs.h> +#include <linux/slab.h> /* AD7414 registers */ @@ -49,7 +50,8 @@ struct ad7414_data { /* REG: (0.25C/bit, two's complement) << 6 */ static inline int ad7414_temp_from_reg(s16 reg) { - /* use integer division instead of equivalent right shift to + /* + * use integer division instead of equivalent right shift to * guarantee arithmetic shift and preserve the sign */ return ((int)reg / 64) * 250; @@ -57,10 +59,9 @@ static inline int ad7414_temp_from_reg(s16 reg) static inline int ad7414_read(struct i2c_client *client, u8 reg) { - if (reg == AD7414_REG_TEMP) { - int value = i2c_smbus_read_word_data(client, reg); - return (value < 0) ? value : swab16(value); - } else + if (reg == AD7414_REG_TEMP) + return i2c_smbus_read_word_swapped(client, reg); + else return i2c_smbus_read_byte_data(client, reg); } @@ -130,9 +131,13 @@ static ssize_t set_max_min(struct device *dev, struct ad7414_data *data = i2c_get_clientdata(client); int index = to_sensor_dev_attr(attr)->index; u8 reg = AD7414_REG_LIMIT[index]; - long temp = simple_strtol(buf, NULL, 10); + long temp; + int ret = kstrtol(buf, 10, &temp); - temp = SENSORS_LIMIT(temp, -40000, 85000); + if (ret < 0) + return ret; + + temp = clamp_val(temp, -40000, 85000); temp = (temp + (temp < 0 ? -500 : 500)) / 1000; mutex_lock(&data->lock); @@ -177,17 +182,16 @@ static int ad7414_probe(struct i2c_client *client, { struct ad7414_data *data; int conf; - int err = 0; + int err; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_READ_WORD_DATA)) - goto exit; + return -EOPNOTSUPP; - data = kzalloc(sizeof(struct ad7414_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&client->dev, sizeof(struct ad7414_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); mutex_init(&data->lock); @@ -207,7 +211,7 @@ static int ad7414_probe(struct i2c_client *client, /* Register sysfs hooks */ err = sysfs_create_group(&client->dev.kobj, &ad7414_group); if (err) - goto exit_free; + return err; data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { @@ -219,19 +223,15 @@ static int ad7414_probe(struct i2c_client *client, exit_remove: sysfs_remove_group(&client->dev.kobj, &ad7414_group); -exit_free: - kfree(data); -exit: return err; } -static int __devexit ad7414_remove(struct i2c_client *client) +static int ad7414_remove(struct i2c_client *client) { struct ad7414_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &ad7414_group); - kfree(data); return 0; } @@ -239,27 +239,18 @@ static const struct i2c_device_id ad7414_id[] = { { "ad7414", 0 }, {} }; +MODULE_DEVICE_TABLE(i2c, ad7414_id); static struct i2c_driver ad7414_driver = { .driver = { .name = "ad7414", }, .probe = ad7414_probe, - .remove = __devexit_p(ad7414_remove), + .remove = ad7414_remove, .id_table = ad7414_id, }; -static int __init ad7414_init(void) -{ - return i2c_add_driver(&ad7414_driver); -} -module_init(ad7414_init); - -static void __exit ad7414_exit(void) -{ - i2c_del_driver(&ad7414_driver); -} -module_exit(ad7414_exit); +module_i2c_driver(ad7414_driver); MODULE_AUTHOR("Stefan Roese <sr at denx.de>, " "Frank Edelhaeuser <frank.edelhaeuser at spansion.com>"); diff --git a/drivers/hwmon/ad7418.c b/drivers/hwmon/ad7418.c index f97b5b35687..57d4a629567 100644 --- a/drivers/hwmon/ad7418.c +++ b/drivers/hwmon/ad7418.c @@ -20,6 +20,7 @@ #include <linux/err.h> #include <linux/mutex.h> #include <linux/delay.h> +#include <linux/slab.h> #include "lm75.h" @@ -75,20 +76,6 @@ static struct i2c_driver ad7418_driver = { .id_table = ad7418_id, }; -/* All registers are word-sized, except for the configuration registers. - * AD7418 uses a high-byte first convention. Do NOT use those functions to - * access the configuration registers CONF and CONF2, as they are byte-sized. - */ -static inline int ad7418_read(struct i2c_client *client, u8 reg) -{ - return swab16(i2c_smbus_read_word_data(client, reg)); -} - -static inline int ad7418_write(struct i2c_client *client, u8 reg, u16 value) -{ - return i2c_smbus_write_word_data(client, reg, swab16(value)); -} - static void ad7418_init_client(struct i2c_client *client) { struct ad7418_data *data = i2c_get_clientdata(client); @@ -127,7 +114,9 @@ static struct ad7418_data *ad7418_update_device(struct device *dev) udelay(30); for (i = 0; i < 3; i++) { - data->temp[i] = ad7418_read(client, AD7418_REG_TEMP[i]); + data->temp[i] = + i2c_smbus_read_word_swapped(client, + AD7418_REG_TEMP[i]); } for (i = 0, ch = 4; i < data->adc_max; i++, ch--) { @@ -137,11 +126,12 @@ static struct ad7418_data *ad7418_update_device(struct device *dev) udelay(15); data->in[data->adc_max - 1 - i] = - ad7418_read(client, AD7418_REG_ADC); + i2c_smbus_read_word_swapped(client, + AD7418_REG_ADC); } /* restore old configuration value */ - ad7418_write(client, AD7418_REG_CONF, cfg); + i2c_smbus_write_word_swapped(client, AD7418_REG_CONF, cfg); data->last_updated = jiffies; data->valid = 1; @@ -177,11 +167,17 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *devattr, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct ad7418_data *data = i2c_get_clientdata(client); - long temp = simple_strtol(buf, NULL, 10); + long temp; + int ret = kstrtol(buf, 10, &temp); + + if (ret < 0) + return ret; mutex_lock(&data->lock); data->temp[attr->index] = LM75_TEMP_TO_REG(temp); - ad7418_write(client, AD7418_REG_TEMP[attr->index], data->temp[attr->index]); + i2c_smbus_write_word_swapped(client, + AD7418_REG_TEMP[attr->index], + data->temp[attr->index]); mutex_unlock(&data->lock); return count; } @@ -231,15 +227,13 @@ static int ad7418_probe(struct i2c_client *client, int err; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_SMBUS_WORD_DATA)) { - err = -EOPNOTSUPP; - goto exit; - } + I2C_FUNC_SMBUS_WORD_DATA)) + return -EOPNOTSUPP; - if (!(data = kzalloc(sizeof(struct ad7418_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&client->dev, sizeof(struct ad7418_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); @@ -269,8 +263,9 @@ static int ad7418_probe(struct i2c_client *client, ad7418_init_client(client); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&client->dev.kobj, &data->attrs))) - goto exit_free; + err = sysfs_create_group(&client->dev.kobj, &data->attrs); + if (err) + return err; data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { @@ -282,9 +277,6 @@ static int ad7418_probe(struct i2c_client *client, exit_remove: sysfs_remove_group(&client->dev.kobj, &data->attrs); -exit_free: - kfree(data); -exit: return err; } @@ -293,24 +285,12 @@ static int ad7418_remove(struct i2c_client *client) struct ad7418_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &data->attrs); - kfree(data); return 0; } -static int __init ad7418_init(void) -{ - return i2c_add_driver(&ad7418_driver); -} - -static void __exit ad7418_exit(void) -{ - i2c_del_driver(&ad7418_driver); -} +module_i2c_driver(ad7418_driver); MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); MODULE_DESCRIPTION("AD7416/17/18 driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); - -module_init(ad7418_init); -module_exit(ad7418_exit); diff --git a/drivers/hwmon/adc128d818.c b/drivers/hwmon/adc128d818.c new file mode 100644 index 00000000000..0625e50d7a6 --- /dev/null +++ b/drivers/hwmon/adc128d818.c @@ -0,0 +1,491 @@ +/* + * Driver for TI ADC128D818 System Monitor with Temperature Sensor + * + * Copyright (c) 2014 Guenter Roeck + * + * Derived from lm80.c + * Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> + * and Philip Edelbrock <phil@netroedge.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/regulator/consumer.h> +#include <linux/mutex.h> + +/* Addresses to scan + * The chip also supports addresses 0x35..0x37. Don't scan those addresses + * since they are also used by some EEPROMs, which may result in false + * positives. + */ +static const unsigned short normal_i2c[] = { + 0x1d, 0x1e, 0x1f, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; + +/* registers */ +#define ADC128_REG_IN_MAX(nr) (0x2a + (nr) * 2) +#define ADC128_REG_IN_MIN(nr) (0x2b + (nr) * 2) +#define ADC128_REG_IN(nr) (0x20 + (nr)) + +#define ADC128_REG_TEMP 0x27 +#define ADC128_REG_TEMP_MAX 0x38 +#define ADC128_REG_TEMP_HYST 0x39 + +#define ADC128_REG_CONFIG 0x00 +#define ADC128_REG_ALARM 0x01 +#define ADC128_REG_MASK 0x03 +#define ADC128_REG_CONV_RATE 0x07 +#define ADC128_REG_ONESHOT 0x09 +#define ADC128_REG_SHUTDOWN 0x0a +#define ADC128_REG_CONFIG_ADV 0x0b +#define ADC128_REG_BUSY_STATUS 0x0c + +#define ADC128_REG_MAN_ID 0x3e +#define ADC128_REG_DEV_ID 0x3f + +struct adc128_data { + struct i2c_client *client; + struct regulator *regulator; + int vref; /* Reference voltage in mV */ + struct mutex update_lock; + bool valid; /* true if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u16 in[3][7]; /* Register value, normalized to 12 bit + * 0: input voltage + * 1: min limit + * 2: max limit + */ + s16 temp[3]; /* Register value, normalized to 9 bit + * 0: sensor 1: limit 2: hyst + */ + u8 alarms; /* alarm register value */ +}; + +static struct adc128_data *adc128_update_device(struct device *dev) +{ + struct adc128_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + struct adc128_data *ret = data; + int i, rv; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + for (i = 0; i < 7; i++) { + rv = i2c_smbus_read_word_swapped(client, + ADC128_REG_IN(i)); + if (rv < 0) + goto abort; + data->in[0][i] = rv >> 4; + + rv = i2c_smbus_read_byte_data(client, + ADC128_REG_IN_MIN(i)); + if (rv < 0) + goto abort; + data->in[1][i] = rv << 4; + + rv = i2c_smbus_read_byte_data(client, + ADC128_REG_IN_MAX(i)); + if (rv < 0) + goto abort; + data->in[2][i] = rv << 4; + } + + rv = i2c_smbus_read_word_swapped(client, ADC128_REG_TEMP); + if (rv < 0) + goto abort; + data->temp[0] = rv >> 7; + + rv = i2c_smbus_read_byte_data(client, ADC128_REG_TEMP_MAX); + if (rv < 0) + goto abort; + data->temp[1] = rv << 1; + + rv = i2c_smbus_read_byte_data(client, ADC128_REG_TEMP_HYST); + if (rv < 0) + goto abort; + data->temp[2] = rv << 1; + + rv = i2c_smbus_read_byte_data(client, ADC128_REG_ALARM); + if (rv < 0) + goto abort; + data->alarms |= rv; + + data->last_updated = jiffies; + data->valid = true; + } + goto done; + +abort: + ret = ERR_PTR(rv); + data->valid = false; +done: + mutex_unlock(&data->update_lock); + return ret; +} + +static ssize_t adc128_show_in(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adc128_data *data = adc128_update_device(dev); + int index = to_sensor_dev_attr_2(attr)->index; + int nr = to_sensor_dev_attr_2(attr)->nr; + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = DIV_ROUND_CLOSEST(data->in[index][nr] * data->vref, 4095); + return sprintf(buf, "%d\n", val); +} + +static ssize_t adc128_set_in(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adc128_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr_2(attr)->index; + int nr = to_sensor_dev_attr_2(attr)->nr; + u8 reg, regval; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + mutex_lock(&data->update_lock); + /* 10 mV LSB on limit registers */ + regval = clamp_val(DIV_ROUND_CLOSEST(val, 10), 0, 255); + data->in[index][nr] = regval << 4; + reg = index == 1 ? ADC128_REG_IN_MIN(nr) : ADC128_REG_IN_MAX(nr); + i2c_smbus_write_byte_data(data->client, reg, regval); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t adc128_show_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adc128_data *data = adc128_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + int temp; + + if (IS_ERR(data)) + return PTR_ERR(data); + + temp = (data->temp[index] << 7) >> 7; /* sign extend */ + return sprintf(buf, "%d\n", temp * 500);/* 0.5 degrees C resolution */ +} + +static ssize_t adc128_set_temp(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adc128_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(attr)->index; + long val; + int err; + s8 regval; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + mutex_lock(&data->update_lock); + regval = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127); + data->temp[index] = regval << 1; + i2c_smbus_write_byte_data(data->client, + index == 1 ? ADC128_REG_TEMP_MAX + : ADC128_REG_TEMP_HYST, + regval); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t adc128_show_alarm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adc128_data *data = adc128_update_device(dev); + int mask = 1 << to_sensor_dev_attr(attr)->index; + u8 alarms; + + if (IS_ERR(data)) + return PTR_ERR(data); + + /* + * Clear an alarm after reporting it to user space. If it is still + * active, the next update sequence will set the alarm bit again. + */ + alarms = data->alarms; + data->alarms &= ~mask; + + return sprintf(buf, "%u\n", !!(alarms & mask)); +} + +static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, + adc128_show_in, NULL, 0, 0); +static SENSOR_DEVICE_ATTR_2(in0_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 0, 1); +static SENSOR_DEVICE_ATTR_2(in0_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 0, 2); + +static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, + adc128_show_in, NULL, 1, 0); +static SENSOR_DEVICE_ATTR_2(in1_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 1, 1); +static SENSOR_DEVICE_ATTR_2(in1_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 1, 2); + +static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, + adc128_show_in, NULL, 2, 0); +static SENSOR_DEVICE_ATTR_2(in2_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 2, 1); +static SENSOR_DEVICE_ATTR_2(in2_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 2, 2); + +static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, + adc128_show_in, NULL, 3, 0); +static SENSOR_DEVICE_ATTR_2(in3_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 3, 1); +static SENSOR_DEVICE_ATTR_2(in3_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 3, 2); + +static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, + adc128_show_in, NULL, 4, 0); +static SENSOR_DEVICE_ATTR_2(in4_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 4, 1); +static SENSOR_DEVICE_ATTR_2(in4_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 4, 2); + +static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, + adc128_show_in, NULL, 5, 0); +static SENSOR_DEVICE_ATTR_2(in5_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 5, 1); +static SENSOR_DEVICE_ATTR_2(in5_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 5, 2); + +static SENSOR_DEVICE_ATTR_2(in6_input, S_IRUGO, + adc128_show_in, NULL, 6, 0); +static SENSOR_DEVICE_ATTR_2(in6_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 6, 1); +static SENSOR_DEVICE_ATTR_2(in6_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 6, 2); + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adc128_show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, + adc128_show_temp, adc128_set_temp, 1); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, + adc128_show_temp, adc128_set_temp, 2); + +static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, adc128_show_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, adc128_show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, adc128_show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, adc128_show_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, adc128_show_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, adc128_show_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, adc128_show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adc128_show_alarm, NULL, 7); + +static struct attribute *adc128_attrs[] = { + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in6_min.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, + &sensor_dev_attr_in6_max.dev_attr.attr, + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_in0_alarm.dev_attr.attr, + &sensor_dev_attr_in1_alarm.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + &sensor_dev_attr_in3_alarm.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + &sensor_dev_attr_in5_alarm.dev_attr.attr, + &sensor_dev_attr_in6_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(adc128); + +static int adc128_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + int man_id, dev_id; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + man_id = i2c_smbus_read_byte_data(client, ADC128_REG_MAN_ID); + dev_id = i2c_smbus_read_byte_data(client, ADC128_REG_DEV_ID); + if (man_id != 0x01 || dev_id != 0x09) + return -ENODEV; + + /* Check unused bits for confirmation */ + if (i2c_smbus_read_byte_data(client, ADC128_REG_CONFIG) & 0xf4) + return -ENODEV; + if (i2c_smbus_read_byte_data(client, ADC128_REG_CONV_RATE) & 0xfe) + return -ENODEV; + if (i2c_smbus_read_byte_data(client, ADC128_REG_ONESHOT) & 0xfe) + return -ENODEV; + if (i2c_smbus_read_byte_data(client, ADC128_REG_SHUTDOWN) & 0xfe) + return -ENODEV; + if (i2c_smbus_read_byte_data(client, ADC128_REG_CONFIG_ADV) & 0xf8) + return -ENODEV; + if (i2c_smbus_read_byte_data(client, ADC128_REG_BUSY_STATUS) & 0xfc) + return -ENODEV; + + strlcpy(info->type, "adc128d818", I2C_NAME_SIZE); + + return 0; +} + +static int adc128_init_client(struct adc128_data *data) +{ + struct i2c_client *client = data->client; + int err; + + /* + * Reset chip to defaults. + * This makes most other initializations unnecessary. + */ + err = i2c_smbus_write_byte_data(client, ADC128_REG_CONFIG, 0x80); + if (err) + return err; + + /* Start monitoring */ + err = i2c_smbus_write_byte_data(client, ADC128_REG_CONFIG, 0x01); + if (err) + return err; + + /* If external vref is selected, configure the chip to use it */ + if (data->regulator) { + err = i2c_smbus_write_byte_data(client, + ADC128_REG_CONFIG_ADV, 0x01); + if (err) + return err; + } + + return 0; +} + +static int adc128_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct regulator *regulator; + struct device *hwmon_dev; + struct adc128_data *data; + int err, vref; + + data = devm_kzalloc(dev, sizeof(struct adc128_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* vref is optional. If specified, is used as chip reference voltage */ + regulator = devm_regulator_get_optional(dev, "vref"); + if (!IS_ERR(regulator)) { + data->regulator = regulator; + err = regulator_enable(regulator); + if (err < 0) + return err; + vref = regulator_get_voltage(regulator); + if (vref < 0) { + err = vref; + goto error; + } + data->vref = DIV_ROUND_CLOSEST(vref, 1000); + } else { + data->vref = 2560; /* 2.56V, in mV */ + } + + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* Initialize the chip */ + err = adc128_init_client(data); + if (err < 0) + goto error; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, adc128_groups); + if (IS_ERR(hwmon_dev)) { + err = PTR_ERR(hwmon_dev); + goto error; + } + + return 0; + +error: + if (data->regulator) + regulator_disable(data->regulator); + return err; +} + +static int adc128_remove(struct i2c_client *client) +{ + struct adc128_data *data = i2c_get_clientdata(client); + + if (data->regulator) + regulator_disable(data->regulator); + + return 0; +} + +static const struct i2c_device_id adc128_id[] = { + { "adc128d818", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adc128_id); + +static struct i2c_driver adc128_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "adc128d818", + }, + .probe = adc128_probe, + .remove = adc128_remove, + .id_table = adc128_id, + .detect = adc128_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(adc128_driver); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("Driver for ADC128D818"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/adcxx.c b/drivers/hwmon/adcxx.c index 5e9e095f113..04c08c2f79b 100644 --- a/drivers/hwmon/adcxx.c +++ b/drivers/hwmon/adcxx.c @@ -37,6 +37,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> +#include <linux/slab.h> #include <linux/device.h> #include <linux/err.h> #include <linux/sysfs.h> @@ -61,19 +62,24 @@ static ssize_t adcxx_read(struct device *dev, { struct spi_device *spi = to_spi_device(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adcxx *adc = dev_get_drvdata(&spi->dev); - u8 tx_buf[2] = { attr->index << 3 }; /* other bits are don't care */ + struct adcxx *adc = spi_get_drvdata(spi); + u8 tx_buf[2]; u8 rx_buf[2]; int status; - int value; + u32 value; if (mutex_lock_interruptible(&adc->lock)) return -ERESTARTSYS; - status = spi_write_then_read(spi, tx_buf, sizeof(tx_buf), - rx_buf, sizeof(rx_buf)); + if (adc->channels == 1) { + status = spi_read(spi, rx_buf, sizeof(rx_buf)); + } else { + tx_buf[0] = attr->index << 3; /* other bits are don't care */ + status = spi_write_then_read(spi, tx_buf, sizeof(tx_buf), + rx_buf, sizeof(rx_buf)); + } if (status < 0) { - dev_warn(dev, "spi_write_then_read failed with status %d\n", + dev_warn(dev, "SPI synch. transfer failed with status %d\n", status); goto out; } @@ -99,7 +105,7 @@ static ssize_t adcxx_show_max(struct device *dev, struct device_attribute *devattr, char *buf) { struct spi_device *spi = to_spi_device(dev); - struct adcxx *adc = dev_get_drvdata(&spi->dev); + struct adcxx *adc = spi_get_drvdata(spi); u32 reference; if (mutex_lock_interruptible(&adc->lock)) @@ -116,10 +122,10 @@ static ssize_t adcxx_set_max(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct spi_device *spi = to_spi_device(dev); - struct adcxx *adc = dev_get_drvdata(&spi->dev); + struct adcxx *adc = spi_get_drvdata(spi); unsigned long value; - if (strict_strtoul(buf, 10, &value)) + if (kstrtoul(buf, 10, &value)) return -EINVAL; if (mutex_lock_interruptible(&adc->lock)) @@ -135,10 +141,7 @@ static ssize_t adcxx_set_max(struct device *dev, static ssize_t adcxx_show_name(struct device *dev, struct device_attribute *devattr, char *buf) { - struct spi_device *spi = to_spi_device(dev); - struct adcxx *adc = dev_get_drvdata(&spi->dev); - - return sprintf(buf, "adcxx%ds\n", adc->channels); + return sprintf(buf, "%s\n", to_spi_device(dev)->modalias); } static struct sensor_device_attribute ad_input[] = { @@ -158,14 +161,14 @@ static struct sensor_device_attribute ad_input[] = { /*----------------------------------------------------------------------*/ -static int __devinit adcxx_probe(struct spi_device *spi) +static int adcxx_probe(struct spi_device *spi) { int channels = spi_get_device_id(spi)->driver_data; struct adcxx *adc; int status; int i; - adc = kzalloc(sizeof *adc, GFP_KERNEL); + adc = devm_kzalloc(&spi->dev, sizeof(*adc), GFP_KERNEL); if (!adc) return -ENOMEM; @@ -176,7 +179,7 @@ static int __devinit adcxx_probe(struct spi_device *spi) mutex_lock(&adc->lock); - dev_set_drvdata(&spi->dev, adc); + spi_set_drvdata(spi, adc); for (i = 0; i < 3 + adc->channels; i++) { status = device_create_file(&spi->dev, &ad_input[i].dev_attr); @@ -200,15 +203,13 @@ out_err: for (i--; i >= 0; i--) device_remove_file(&spi->dev, &ad_input[i].dev_attr); - dev_set_drvdata(&spi->dev, NULL); mutex_unlock(&adc->lock); - kfree(adc); return status; } -static int __devexit adcxx_remove(struct spi_device *spi) +static int adcxx_remove(struct spi_device *spi) { - struct adcxx *adc = dev_get_drvdata(&spi->dev); + struct adcxx *adc = spi_get_drvdata(spi); int i; mutex_lock(&adc->lock); @@ -216,9 +217,7 @@ static int __devexit adcxx_remove(struct spi_device *spi) for (i = 0; i < 3 + adc->channels; i++) device_remove_file(&spi->dev, &ad_input[i].dev_attr); - dev_set_drvdata(&spi->dev, NULL); mutex_unlock(&adc->lock); - kfree(adc); return 0; } @@ -239,21 +238,10 @@ static struct spi_driver adcxx_driver = { }, .id_table = adcxx_ids, .probe = adcxx_probe, - .remove = __devexit_p(adcxx_remove), + .remove = adcxx_remove, }; -static int __init init_adcxx(void) -{ - return spi_register_driver(&adcxx_driver); -} - -static void __exit exit_adcxx(void) -{ - spi_unregister_driver(&adcxx_driver); -} - -module_init(init_adcxx); -module_exit(exit_adcxx); +module_spi_driver(adcxx_driver); MODULE_AUTHOR("Marc Pignat"); MODULE_DESCRIPTION("National Semiconductor adcxx8sxxx Linux driver"); diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c index 1ad0a885c5a..d74241bb278 100644 --- a/drivers/hwmon/adm1021.c +++ b/drivers/hwmon/adm1021.c @@ -1,23 +1,23 @@ /* - adm1021.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and - Philip Edelbrock <phil@netroedge.com> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ + * adm1021.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and + * Philip Edelbrock <phil@netroedge.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ #include <linux/module.h> #include <linux/init.h> @@ -70,16 +70,20 @@ enum chips { /* Initial values */ -/* Note: Even though I left the low and high limits named os and hyst, -they don't quite work like a thermostat the way the LM75 does. I.e., -a lower temp than THYST actually triggers an alarm instead of -clearing it. Weird, ey? --Phil */ +/* + * Note: Even though I left the low and high limits named os and hyst, + * they don't quite work like a thermostat the way the LM75 does. I.e., + * a lower temp than THYST actually triggers an alarm instead of + * clearing it. Weird, ey? --Phil + */ /* Each client has this additional data */ struct adm1021_data { - struct device *hwmon_dev; + struct i2c_client *client; enum chips type; + const struct attribute_group *groups[3]; + struct mutex update_lock; char valid; /* !=0 if following fields are valid */ char low_power; /* !=0 if device in low power mode */ @@ -99,11 +103,10 @@ static int adm1021_probe(struct i2c_client *client, static int adm1021_detect(struct i2c_client *client, struct i2c_board_info *info); static void adm1021_init_client(struct i2c_client *client); -static int adm1021_remove(struct i2c_client *client); static struct adm1021_data *adm1021_update_device(struct device *dev); /* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */ -static int read_only; +static bool read_only; static const struct i2c_device_id adm1021_id[] = { @@ -126,7 +129,6 @@ static struct i2c_driver adm1021_driver = { .name = "adm1021", }, .probe = adm1021_probe, - .remove = adm1021_remove, .id_table = adm1021_id, .detect = adm1021_detect, .address_list = normal_i2c, @@ -180,15 +182,22 @@ static ssize_t set_temp_max(struct device *dev, const char *buf, size_t count) { int index = to_sensor_dev_attr(devattr)->index; - struct i2c_client *client = to_i2c_client(dev); - struct adm1021_data *data = i2c_get_clientdata(client); - long temp = simple_strtol(buf, NULL, 10) / 1000; + struct adm1021_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + long temp; + int reg_val, err; + + err = kstrtol(buf, 10, &temp); + if (err) + return err; + temp /= 1000; mutex_lock(&data->update_lock); - data->temp_max[index] = SENSORS_LIMIT(temp, -128, 127); + reg_val = clamp_val(temp, -128, 127); + data->temp_max[index] = reg_val * 1000; if (!read_only) i2c_smbus_write_byte_data(client, ADM1021_REG_TOS_W(index), - data->temp_max[index]); + reg_val); mutex_unlock(&data->update_lock); return count; @@ -199,15 +208,22 @@ static ssize_t set_temp_min(struct device *dev, const char *buf, size_t count) { int index = to_sensor_dev_attr(devattr)->index; - struct i2c_client *client = to_i2c_client(dev); - struct adm1021_data *data = i2c_get_clientdata(client); - long temp = simple_strtol(buf, NULL, 10) / 1000; + struct adm1021_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + long temp; + int reg_val, err; + + err = kstrtol(buf, 10, &temp); + if (err) + return err; + temp /= 1000; mutex_lock(&data->update_lock); - data->temp_min[index] = SENSORS_LIMIT(temp, -128, 127); + reg_val = clamp_val(temp, -128, 127); + data->temp_min[index] = reg_val * 1000; if (!read_only) i2c_smbus_write_byte_data(client, ADM1021_REG_THYST_W(index), - data->temp_min[index]); + reg_val); mutex_unlock(&data->update_lock); return count; @@ -224,9 +240,16 @@ static ssize_t set_low_power(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct adm1021_data *data = i2c_get_clientdata(client); - int low_power = simple_strtol(buf, NULL, 10) != 0; + struct adm1021_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + char low_power; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + low_power = val != 0; mutex_lock(&data->update_lock); if (low_power != data->low_power) { @@ -263,15 +286,11 @@ static DEVICE_ATTR(low_power, S_IWUSR | S_IRUGO, show_low_power, set_low_power); static struct attribute *adm1021_attributes[] = { &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_min.dev_attr.attr, &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp2_min.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, &sensor_dev_attr_temp2_fault.dev_attr.attr, &dev_attr_alarms.attr, &dev_attr_low_power.attr, @@ -282,6 +301,18 @@ static const struct attribute_group adm1021_group = { .attrs = adm1021_attributes, }; +static struct attribute *adm1021_min_attributes[] = { + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group adm1021_min_group = { + .attrs = adm1021_min_attributes, +}; + /* Return 0 if detection is successful, -ENODEV otherwise */ static int adm1021_detect(struct i2c_client *client, struct i2c_board_info *info) @@ -291,8 +322,7 @@ static int adm1021_detect(struct i2c_client *client, int conv_rate, status, config, man_id, dev_id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - pr_debug("adm1021: detect failed, " - "smbus byte data not supported!\n"); + pr_debug("detect failed, smbus byte data not supported!\n"); return -ENODEV; } @@ -303,7 +333,7 @@ static int adm1021_detect(struct i2c_client *client, /* Check unused bits */ if ((status & 0x03) || (config & 0x3F) || (conv_rate & 0xF8)) { - pr_debug("adm1021: detect failed, chip not detected!\n"); + pr_debug("detect failed, chip not detected!\n"); return -ENODEV; } @@ -311,28 +341,70 @@ static int adm1021_detect(struct i2c_client *client, man_id = i2c_smbus_read_byte_data(client, ADM1021_REG_MAN_ID); dev_id = i2c_smbus_read_byte_data(client, ADM1021_REG_DEV_ID); + if (man_id < 0 || dev_id < 0) + return -ENODEV; + if (man_id == 0x4d && dev_id == 0x01) type_name = "max1617a"; else if (man_id == 0x41) { if ((dev_id & 0xF0) == 0x30) type_name = "adm1023"; - else + else if ((dev_id & 0xF0) == 0x00) type_name = "adm1021"; + else + return -ENODEV; } else if (man_id == 0x49) type_name = "thmc10"; else if (man_id == 0x23) type_name = "gl523sm"; else if (man_id == 0x54) type_name = "mc1066"; - /* LM84 Mfr ID in a different place, and it has more unused bits */ - else if (conv_rate == 0x00 - && (config & 0x7F) == 0x00 - && (status & 0xAB) == 0x00) - type_name = "lm84"; - else - type_name = "max1617"; - - pr_debug("adm1021: Detected chip %s at adapter %d, address 0x%02x.\n", + else { + int lte, rte, lhi, rhi, llo, rlo; + + /* extra checks for LM84 and MAX1617 to avoid misdetections */ + + llo = i2c_smbus_read_byte_data(client, ADM1021_REG_THYST_R(0)); + rlo = i2c_smbus_read_byte_data(client, ADM1021_REG_THYST_R(1)); + + /* fail if any of the additional register reads failed */ + if (llo < 0 || rlo < 0) + return -ENODEV; + + lte = i2c_smbus_read_byte_data(client, ADM1021_REG_TEMP(0)); + rte = i2c_smbus_read_byte_data(client, ADM1021_REG_TEMP(1)); + lhi = i2c_smbus_read_byte_data(client, ADM1021_REG_TOS_R(0)); + rhi = i2c_smbus_read_byte_data(client, ADM1021_REG_TOS_R(1)); + + /* + * Fail for negative temperatures and negative high limits. + * This check also catches read errors on the tested registers. + */ + if ((s8)lte < 0 || (s8)rte < 0 || (s8)lhi < 0 || (s8)rhi < 0) + return -ENODEV; + + /* fail if all registers hold the same value */ + if (lte == rte && lte == lhi && lte == rhi && lte == llo + && lte == rlo) + return -ENODEV; + + /* + * LM84 Mfr ID is in a different place, + * and it has more unused bits. + */ + if (conv_rate == 0x00 + && (config & 0x7F) == 0x00 + && (status & 0xAB) == 0x00) { + type_name = "lm84"; + } else { + /* fail if low limits are larger than high limits */ + if ((s8)llo > lhi || (s8)rlo > rhi) + return -ENODEV; + type_name = "max1617"; + } + } + + pr_debug("Detected chip %s at adapter %d, address 0x%02x.\n", type_name, i2c_adapter_id(adapter), client->addr); strlcpy(info->type, type_name, I2C_NAME_SIZE); @@ -342,17 +414,15 @@ static int adm1021_detect(struct i2c_client *client, static int adm1021_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; struct adm1021_data *data; - int err; + struct device *hwmon_dev; - data = kzalloc(sizeof(struct adm1021_data), GFP_KERNEL); - if (!data) { - pr_debug("adm1021: detect failed, kzalloc failed!\n"); - err = -ENOMEM; - goto error0; - } + data = devm_kzalloc(dev, sizeof(struct adm1021_data), GFP_KERNEL); + if (!data) + return -ENOMEM; - i2c_set_clientdata(client, data); + data->client = client; data->type = id->driver_data; mutex_init(&data->update_lock); @@ -360,24 +430,14 @@ static int adm1021_probe(struct i2c_client *client, if (data->type != lm84 && !read_only) adm1021_init_client(client); - /* Register sysfs hooks */ - if ((err = sysfs_create_group(&client->dev.kobj, &adm1021_group))) - goto error1; - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto error3; - } + data->groups[0] = &adm1021_group; + if (data->type != lm84) + data->groups[1] = &adm1021_min_group; - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, data->groups); -error3: - sysfs_remove_group(&client->dev.kobj, &adm1021_group); -error1: - kfree(data); -error0: - return err; + return PTR_ERR_OR_ZERO(hwmon_dev); } static void adm1021_init_client(struct i2c_client *client) @@ -389,21 +449,10 @@ static void adm1021_init_client(struct i2c_client *client) i2c_smbus_write_byte_data(client, ADM1021_REG_CONV_RATE_W, 0x04); } -static int adm1021_remove(struct i2c_client *client) -{ - struct adm1021_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &adm1021_group); - - kfree(data); - return 0; -} - static struct adm1021_data *adm1021_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct adm1021_data *data = i2c_get_clientdata(client); + struct adm1021_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; mutex_lock(&data->update_lock); @@ -411,7 +460,7 @@ static struct adm1021_data *adm1021_update_device(struct device *dev) || !data->valid) { int i; - dev_dbg(&client->dev, "Starting adm1021 update\n"); + dev_dbg(dev, "Starting adm1021 update\n"); for (i = 0; i < 2; i++) { data->temp[i] = 1000 * @@ -420,15 +469,19 @@ static struct adm1021_data *adm1021_update_device(struct device *dev) data->temp_max[i] = 1000 * (s8) i2c_smbus_read_byte_data( client, ADM1021_REG_TOS_R(i)); - data->temp_min[i] = 1000 * - (s8) i2c_smbus_read_byte_data( - client, ADM1021_REG_THYST_R(i)); + if (data->type != lm84) { + data->temp_min[i] = 1000 * + (s8) i2c_smbus_read_byte_data(client, + ADM1021_REG_THYST_R(i)); + } } data->alarms = i2c_smbus_read_byte_data(client, ADM1021_REG_STATUS) & 0x7c; if (data->type == adm1023) { - /* The ADM1023 provides 3 extra bits of precision for - * the remote sensor in extra registers. */ + /* + * The ADM1023 provides 3 extra bits of precision for + * the remote sensor in extra registers. + */ data->temp[1] += 125 * (i2c_smbus_read_byte_data( client, ADM1023_REG_REM_TEMP_PREC) >> 5); data->temp_max[1] += 125 * (i2c_smbus_read_byte_data( @@ -451,23 +504,12 @@ static struct adm1021_data *adm1021_update_device(struct device *dev) return data; } -static int __init sensors_adm1021_init(void) -{ - return i2c_add_driver(&adm1021_driver); -} +module_i2c_driver(adm1021_driver); -static void __exit sensors_adm1021_exit(void) -{ - i2c_del_driver(&adm1021_driver); -} - -MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl> and " +MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and " "Philip Edelbrock <phil@netroedge.com>"); MODULE_DESCRIPTION("adm1021 driver"); MODULE_LICENSE("GPL"); module_param(read_only, bool, 0); MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); - -module_init(sensors_adm1021_init) -module_exit(sensors_adm1021_exit) diff --git a/drivers/hwmon/adm1025.c b/drivers/hwmon/adm1025.c index 251b63165e2..9ffc4c8ca8b 100644 --- a/drivers/hwmon/adm1025.c +++ b/drivers/hwmon/adm1025.c @@ -2,7 +2,7 @@ * adm1025.c * * Copyright (C) 2000 Chen-Yuan Wu <gwu@esoft.com> - * Copyright (C) 2003-2009 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2003-2009 Jean Delvare <jdelvare@suse.de> * * The ADM1025 is a sensor chip made by Analog Devices. It reports up to 6 * voltages (including its own power source) and up to two temperatures @@ -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 @@ -91,15 +91,16 @@ enum chips { adm1025, ne1619 }; static const int in_scale[6] = { 2500, 2250, 3300, 5000, 12000, 3300 }; -#define IN_FROM_REG(reg,scale) (((reg) * (scale) + 96) / 192) -#define IN_TO_REG(val,scale) ((val) <= 0 ? 0 : \ +#define IN_FROM_REG(reg, scale) (((reg) * (scale) + 96) / 192) +#define IN_TO_REG(val, scale) ((val) <= 0 ? 0 : \ (val) * 192 >= (scale) * 255 ? 255 : \ - ((val) * 192 + (scale)/2) / (scale)) + ((val) * 192 + (scale) / 2) / (scale)) #define TEMP_FROM_REG(reg) ((reg) * 1000) #define TEMP_TO_REG(val) ((val) <= -127500 ? -128 : \ (val) >= 126500 ? 127 : \ - (((val) < 0 ? (val)-500 : (val)+500) / 1000)) + (((val) < 0 ? (val) - 500 : \ + (val) + 500) / 1000)) /* * Functions declaration @@ -218,7 +219,12 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, int index = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct adm1025_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_min[index] = IN_TO_REG(val, in_scale[index]); @@ -234,7 +240,12 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, int index = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct adm1025_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_max[index] = IN_TO_REG(val, in_scale[index]); @@ -264,7 +275,12 @@ static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr, int index = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct adm1025_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_min[index] = TEMP_TO_REG(val); @@ -280,7 +296,12 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, int index = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct adm1025_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_max[index] = TEMP_TO_REG(val); @@ -343,7 +364,14 @@ static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct adm1025_data *data = dev_get_drvdata(dev); - data->vrm = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + data->vrm = val; return count; } static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); @@ -449,11 +477,10 @@ static int adm1025_probe(struct i2c_client *client, int err; u8 config; - data = kzalloc(sizeof(struct adm1025_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&client->dev, sizeof(struct adm1025_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); mutex_init(&data->update_lock); @@ -462,14 +489,15 @@ static int adm1025_probe(struct i2c_client *client, adm1025_init_client(client); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&client->dev.kobj, &adm1025_group))) - goto exit_free; + err = sysfs_create_group(&client->dev.kobj, &adm1025_group); + if (err) + return err; /* Pin 11 is either in4 (+12V) or VID4 */ config = i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG); if (!(config & 0x20)) { - if ((err = sysfs_create_group(&client->dev.kobj, - &adm1025_group_in4))) + err = sysfs_create_group(&client->dev.kobj, &adm1025_group_in4); + if (err) goto exit_remove; } @@ -484,9 +512,6 @@ static int adm1025_probe(struct i2c_client *client, exit_remove: sysfs_remove_group(&client->dev.kobj, &adm1025_group); sysfs_remove_group(&client->dev.kobj, &adm1025_group_in4); -exit_free: - kfree(data); -exit: return err; } @@ -506,7 +531,7 @@ static void adm1025_init_client(struct i2c_client *client) * setting yet, we better set the high limits to the max so that * no alarm triggers. */ - for (i=0; i<6; i++) { + for (i = 0; i < 6; i++) { reg = i2c_smbus_read_byte_data(client, ADM1025_REG_IN_MAX(i)); if (reg == 0) @@ -514,7 +539,7 @@ static void adm1025_init_client(struct i2c_client *client) ADM1025_REG_IN_MAX(i), 0xFF); } - for (i=0; i<2; i++) { + for (i = 0; i < 2; i++) { reg = i2c_smbus_read_byte_data(client, ADM1025_REG_TEMP_HIGH(i)); if (reg == 0) @@ -540,7 +565,6 @@ static int adm1025_remove(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &adm1025_group); sysfs_remove_group(&client->dev.kobj, &adm1025_group_in4); - kfree(data); return 0; } @@ -555,7 +579,7 @@ static struct adm1025_data *adm1025_update_device(struct device *dev) int i; dev_dbg(&client->dev, "Updating data.\n"); - for (i=0; i<6; i++) { + for (i = 0; i < 6; i++) { data->in[i] = i2c_smbus_read_byte_data(client, ADM1025_REG_IN(i)); data->in_min[i] = i2c_smbus_read_byte_data(client, @@ -563,7 +587,7 @@ static struct adm1025_data *adm1025_update_device(struct device *dev) data->in_max[i] = i2c_smbus_read_byte_data(client, ADM1025_REG_IN_MAX(i)); } - for (i=0; i<2; i++) { + for (i = 0; i < 2; i++) { data->temp[i] = i2c_smbus_read_byte_data(client, ADM1025_REG_TEMP(i)); data->temp_min[i] = i2c_smbus_read_byte_data(client, @@ -589,19 +613,8 @@ static struct adm1025_data *adm1025_update_device(struct device *dev) return data; } -static int __init sensors_adm1025_init(void) -{ - return i2c_add_driver(&adm1025_driver); -} +module_i2c_driver(adm1025_driver); -static void __exit sensors_adm1025_exit(void) -{ - i2c_del_driver(&adm1025_driver); -} - -MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); +MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); MODULE_DESCRIPTION("ADM1025 driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_adm1025_init); -module_exit(sensors_adm1025_exit); diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index 65335b268fa..b3498acb9ab 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -1,27 +1,27 @@ /* - adm1026.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (C) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com> - Copyright (C) 2004 Justin Thiessen <jthiessen@penguincomputing.com> - - Chip details at: - - <http://www.analog.com/UploadedFiles/Data_Sheets/779263102ADM1026_a.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. -*/ + * adm1026.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (C) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com> + * Copyright (C) 2004 Justin Thiessen <jthiessen@penguincomputing.com> + * + * Chip details at: + * + * <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 + * 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> @@ -49,14 +49,14 @@ static int gpio_fan[8] = { -1, -1, -1, -1, -1, -1, -1, -1 }; module_param_array(gpio_input, int, NULL, 0); MODULE_PARM_DESC(gpio_input, "List of GPIO pins (0-16) to program as inputs"); module_param_array(gpio_output, int, NULL, 0); -MODULE_PARM_DESC(gpio_output, "List of GPIO pins (0-16) to program as " - "outputs"); +MODULE_PARM_DESC(gpio_output, + "List of GPIO pins (0-16) to program as outputs"); module_param_array(gpio_inverted, int, NULL, 0); -MODULE_PARM_DESC(gpio_inverted, "List of GPIO pins (0-16) to program as " - "inverted"); +MODULE_PARM_DESC(gpio_inverted, + "List of GPIO pins (0-16) to program as inverted"); module_param_array(gpio_normal, int, NULL, 0); -MODULE_PARM_DESC(gpio_normal, "List of GPIO pins (0-16) to program as " - "normal/non-inverted"); +MODULE_PARM_DESC(gpio_normal, + "List of GPIO pins (0-16) to program as normal/non-inverted"); module_param_array(gpio_fan, int, NULL, 0); MODULE_PARM_DESC(gpio_fan, "List of GPIO pins (0-7) to program as fan tachs"); @@ -90,7 +90,8 @@ MODULE_PARM_DESC(gpio_fan, "List of GPIO pins (0-7) to program as fan tachs"); #define E2CFG_ROM 0x08 #define E2CFG_CLK_EXT 0x80 -/* There are 10 general analog inputs and 7 dedicated inputs +/* + * There are 10 general analog inputs and 7 dedicated inputs * They are: * 0 - 9 = AIN0 - AIN9 * 10 = Vbat @@ -117,7 +118,8 @@ static u16 ADM1026_REG_IN_MAX[] = { 0x43, 0x44, 0x45, 0x46, 0x47 }; -/* Temperatures are: +/* + * Temperatures are: * 0 - Internal * 1 - External 1 * 2 - External 2 @@ -170,12 +172,14 @@ static u16 ADM1026_REG_TEMP_OFFSET[] = { 0x1e, 0x6e, 0x6f }; #define ADM1026_FAN_CONTROL_TEMP_RANGE 20 #define ADM1026_PWM_MAX 255 -/* Conversions. Rounding and limit checking is only done on the TO_REG +/* + * Conversions. Rounding and limit checking is only done on the TO_REG * variants. Note that you should be a bit careful with which arguments * these macros are called: arguments may be evaluated more than once. */ -/* IN are scaled acording to built-in resistors. These are the +/* + * IN are scaled according to built-in resistors. These are the * voltages corresponding to 3/4 of full scale (192 or 0xc0) * NOTE: The -12V input needs an additional factor to account * for the Vref pullup resistor. @@ -193,43 +197,47 @@ static int adm1026_scaling[] = { /* .001 Volts */ }; #define NEG12_OFFSET 16000 #define SCALE(val, from, to) (((val)*(to) + ((from)/2))/(from)) -#define INS_TO_REG(n, val) (SENSORS_LIMIT(SCALE(val, adm1026_scaling[n], 192),\ +#define INS_TO_REG(n, val) (clamp_val(SCALE(val, adm1026_scaling[n], 192),\ 0, 255)) #define INS_FROM_REG(n, val) (SCALE(val, 192, adm1026_scaling[n])) -/* FAN speed is measured using 22.5kHz clock and counts for 2 pulses +/* + * FAN speed is measured using 22.5kHz clock and counts for 2 pulses * and we assume a 2 pulse-per-rev fan tach signal * 22500 kHz * 60 (sec/min) * 2 (pulse) / 2 (pulse/rev) == 1350000 */ #define FAN_TO_REG(val, div) ((val) <= 0 ? 0xff : \ - SENSORS_LIMIT(1350000/((val)*(div)), 1, 254)) -#define FAN_FROM_REG(val, div) ((val) == 0 ? -1:(val) == 0xff ? 0 : \ - 1350000/((val)*(div))) -#define DIV_FROM_REG(val) (1<<(val)) + clamp_val(1350000 / ((val) * (div)), \ + 1, 254)) +#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : (val) == 0xff ? 0 : \ + 1350000 / ((val) * (div))) +#define DIV_FROM_REG(val) (1 << (val)) #define DIV_TO_REG(val) ((val) >= 8 ? 3 : (val) >= 4 ? 2 : (val) >= 2 ? 1 : 0) /* Temperature is reported in 1 degC increments */ -#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)+((val)<0 ? -500 : 500))/1000,\ - -127, 127)) +#define TEMP_TO_REG(val) (clamp_val(((val) + ((val) < 0 ? -500 : 500)) \ + / 1000, -127, 127)) #define TEMP_FROM_REG(val) ((val) * 1000) -#define OFFSET_TO_REG(val) (SENSORS_LIMIT(((val)+((val)<0 ? -500 : 500))/1000,\ - -127, 127)) +#define OFFSET_TO_REG(val) (clamp_val(((val) + ((val) < 0 ? -500 : 500)) \ + / 1000, -127, 127)) #define OFFSET_FROM_REG(val) ((val) * 1000) -#define PWM_TO_REG(val) (SENSORS_LIMIT(val, 0, 255)) +#define PWM_TO_REG(val) (clamp_val(val, 0, 255)) #define PWM_FROM_REG(val) (val) #define PWM_MIN_TO_REG(val) ((val) & 0xf0) #define PWM_MIN_FROM_REG(val) (((val) & 0xf0) + ((val) >> 4)) -/* Analog output is a voltage, and scaled to millivolts. The datasheet +/* + * Analog output is a voltage, and scaled to millivolts. The datasheet * indicates that the DAC could be used to drive the fans, but in our * example board (Arima HDAMA) it isn't connected to the fans at all. */ -#define DAC_TO_REG(val) (SENSORS_LIMIT(((((val)*255)+500)/2500), 0, 255)) -#define DAC_FROM_REG(val) (((val)*2500)/255) +#define DAC_TO_REG(val) (clamp_val(((((val) * 255) + 500) / 2500), 0, 255)) +#define DAC_FROM_REG(val) (((val) * 2500) / 255) -/* Chip sampling rates +/* + * Chip sampling rates * * Some sensors are not updated more frequently than once per second * so it doesn't make sense to read them more often than that. @@ -243,11 +251,13 @@ static int adm1026_scaling[] = { /* .001 Volts */ #define ADM1026_DATA_INTERVAL (1 * HZ) #define ADM1026_CONFIG_INTERVAL (5 * 60 * HZ) -/* We allow for multiple chips in a single system. +/* + * We allow for multiple chips in a single system. * * For each registered ADM1026, we need to keep state information * at client->data. The adm1026_data structure is dynamically - * allocated, when a new client structure is allocated. */ + * allocated, when a new client structure is allocated. + */ struct pwm_data { u8 pwm; @@ -362,43 +372,42 @@ static void adm1026_init_client(struct i2c_client *client) dev_dbg(&client->dev, "ADM1026_REG_CONFIG1 is: 0x%02x\n", data->config1); if ((data->config1 & CFG1_MONITOR) == 0) { - dev_dbg(&client->dev, "Monitoring not currently " - "enabled.\n"); + dev_dbg(&client->dev, + "Monitoring not currently enabled.\n"); } if (data->config1 & CFG1_INT_ENABLE) { - dev_dbg(&client->dev, "SMBALERT interrupts are " - "enabled.\n"); + dev_dbg(&client->dev, + "SMBALERT interrupts are enabled.\n"); } if (data->config1 & CFG1_AIN8_9) { - dev_dbg(&client->dev, "in8 and in9 enabled. " - "temp3 disabled.\n"); + dev_dbg(&client->dev, + "in8 and in9 enabled. temp3 disabled.\n"); } else { - dev_dbg(&client->dev, "temp3 enabled. in8 and " - "in9 disabled.\n"); + dev_dbg(&client->dev, + "temp3 enabled. in8 and in9 disabled.\n"); } if (data->config1 & CFG1_THERM_HOT) { - dev_dbg(&client->dev, "Automatic THERM, PWM, " - "and temp limits enabled.\n"); + dev_dbg(&client->dev, + "Automatic THERM, PWM, and temp limits enabled.\n"); } if (data->config3 & CFG3_GPIO16_ENABLE) { - dev_dbg(&client->dev, "GPIO16 enabled. THERM " - "pin disabled.\n"); + dev_dbg(&client->dev, + "GPIO16 enabled. THERM pin disabled.\n"); } else { - dev_dbg(&client->dev, "THERM pin enabled. " - "GPIO16 disabled.\n"); + dev_dbg(&client->dev, + "THERM pin enabled. GPIO16 disabled.\n"); } - if (data->config3 & CFG3_VREF_250) { + if (data->config3 & CFG3_VREF_250) dev_dbg(&client->dev, "Vref is 2.50 Volts.\n"); - } else { + else dev_dbg(&client->dev, "Vref is 1.82 Volts.\n"); - } /* Read and pick apart the existing GPIO configuration */ value = 0; - for (i = 0;i <= 15;++i) { + for (i = 0; i <= 15; ++i) { if ((i & 0x03) == 0) { value = adm1026_read_value(client, - ADM1026_REG_GPIO_CFG_0_3 + i/4); + ADM1026_REG_GPIO_CFG_0_3 + i / 4); } data->gpio_config[i] = value & 0x03; value >>= 2; @@ -408,7 +417,8 @@ static void adm1026_init_client(struct i2c_client *client) /* ... and then print it */ adm1026_print_gpio(client); - /* If the user asks us to reprogram the GPIO config, then + /* + * If the user asks us to reprogram the GPIO config, then * do it now. */ if (gpio_input[0] != -1 || gpio_output[0] != -1 @@ -417,7 +427,8 @@ static void adm1026_init_client(struct i2c_client *client) adm1026_fixup_gpio(client); } - /* WE INTENTIONALLY make no changes to the limits, + /* + * WE INTENTIONALLY make no changes to the limits, * offsets, pwms, fans and zones. If they were * configured, we don't want to mess with them. * If they weren't, the default is 100% PWM, no @@ -428,7 +439,7 @@ static void adm1026_init_client(struct i2c_client *client) * without first setting a value for pwm1.auto_pwm_min * will not result in potentially dangerous fan speed decrease. */ - data->pwm1.auto_pwm_min=255; + data->pwm1.auto_pwm_min = 255; /* Start monitoring */ value = adm1026_read_value(client, ADM1026_REG_CONFIG1); /* Set MONITOR, clear interrupt acknowledge and s/w reset */ @@ -440,7 +451,7 @@ static void adm1026_init_client(struct i2c_client *client) /* initialize fan_div[] to hardware defaults */ value = adm1026_read_value(client, ADM1026_REG_FAN_DIV_0_3) | (adm1026_read_value(client, ADM1026_REG_FAN_DIV_4_7) << 8); - for (i = 0;i <= 7;++i) { + for (i = 0; i <= 7; ++i) { data->fan_div[i] = DIV_FROM_REG(value & 0x03); value >>= 2; } @@ -452,7 +463,7 @@ static void adm1026_print_gpio(struct i2c_client *client) int i; dev_dbg(&client->dev, "GPIO config is:\n"); - for (i = 0;i <= 7;++i) { + for (i = 0; i <= 7; ++i) { if (data->config2 & (1 << i)) { dev_dbg(&client->dev, "\t%sGP%s%d\n", data->gpio_config[i] & 0x02 ? "" : "!", @@ -462,7 +473,7 @@ static void adm1026_print_gpio(struct i2c_client *client) dev_dbg(&client->dev, "\tFAN%d\n", i); } } - for (i = 8;i <= 15;++i) { + for (i = 8; i <= 15; ++i) { dev_dbg(&client->dev, "\t%sGP%s%d\n", data->gpio_config[i] & 0x02 ? "" : "!", data->gpio_config[i] & 0x01 ? "OUT" : "IN", @@ -485,52 +496,46 @@ static void adm1026_fixup_gpio(struct i2c_client *client) int value; /* Make the changes requested. */ - /* We may need to unlock/stop monitoring or soft-reset the + /* + * We may need to unlock/stop monitoring or soft-reset the * chip before we can make changes. This hasn't been * tested much. FIXME */ /* Make outputs */ - for (i = 0;i <= 16;++i) { - if (gpio_output[i] >= 0 && gpio_output[i] <= 16) { + for (i = 0; i <= 16; ++i) { + if (gpio_output[i] >= 0 && gpio_output[i] <= 16) data->gpio_config[gpio_output[i]] |= 0x01; - } /* if GPIO0-7 is output, it isn't a FAN tach */ - if (gpio_output[i] >= 0 && gpio_output[i] <= 7) { + if (gpio_output[i] >= 0 && gpio_output[i] <= 7) data->config2 |= 1 << gpio_output[i]; - } } /* Input overrides output */ - for (i = 0;i <= 16;++i) { - if (gpio_input[i] >= 0 && gpio_input[i] <= 16) { - data->gpio_config[gpio_input[i]] &= ~ 0x01; - } + for (i = 0; i <= 16; ++i) { + if (gpio_input[i] >= 0 && gpio_input[i] <= 16) + data->gpio_config[gpio_input[i]] &= ~0x01; /* if GPIO0-7 is input, it isn't a FAN tach */ - if (gpio_input[i] >= 0 && gpio_input[i] <= 7) { + if (gpio_input[i] >= 0 && gpio_input[i] <= 7) data->config2 |= 1 << gpio_input[i]; - } } /* Inverted */ - for (i = 0;i <= 16;++i) { - if (gpio_inverted[i] >= 0 && gpio_inverted[i] <= 16) { - data->gpio_config[gpio_inverted[i]] &= ~ 0x02; - } + for (i = 0; i <= 16; ++i) { + if (gpio_inverted[i] >= 0 && gpio_inverted[i] <= 16) + data->gpio_config[gpio_inverted[i]] &= ~0x02; } /* Normal overrides inverted */ - for (i = 0;i <= 16;++i) { - if (gpio_normal[i] >= 0 && gpio_normal[i] <= 16) { + for (i = 0; i <= 16; ++i) { + if (gpio_normal[i] >= 0 && gpio_normal[i] <= 16) data->gpio_config[gpio_normal[i]] |= 0x02; - } } /* Fan overrides input and output */ - for (i = 0;i <= 7;++i) { - if (gpio_fan[i] >= 0 && gpio_fan[i] <= 7) { + for (i = 0; i <= 7; ++i) { + if (gpio_fan[i] >= 0 && gpio_fan[i] <= 7) data->config2 &= ~(1 << gpio_fan[i]); - } } /* Write new configs to registers */ @@ -538,7 +543,7 @@ static void adm1026_fixup_gpio(struct i2c_client *client) data->config3 = (data->config3 & 0x3f) | ((data->gpio_config[16] & 0x03) << 6); adm1026_write_value(client, ADM1026_REG_CONFIG3, data->config3); - for (i = 15, value = 0;i >= 0;--i) { + for (i = 15, value = 0; i >= 0; --i) { value <<= 2; value |= data->gpio_config[i] & 0x03; if ((i & 0x03) == 0) { @@ -563,22 +568,25 @@ static struct adm1026_data *adm1026_update_device(struct device *dev) mutex_lock(&data->update_lock); if (!data->valid - || time_after(jiffies, data->last_reading + ADM1026_DATA_INTERVAL)) { + || time_after(jiffies, + data->last_reading + ADM1026_DATA_INTERVAL)) { /* Things that change quickly */ dev_dbg(&client->dev, "Reading sensor values\n"); - for (i = 0;i <= 16;++i) { + for (i = 0; i <= 16; ++i) { data->in[i] = adm1026_read_value(client, ADM1026_REG_IN[i]); } - for (i = 0;i <= 7;++i) { + for (i = 0; i <= 7; ++i) { data->fan[i] = adm1026_read_value(client, ADM1026_REG_FAN(i)); } - for (i = 0;i <= 2;++i) { - /* NOTE: temp[] is s8 and we assume 2's complement - * "conversion" in the assignment */ + for (i = 0; i <= 2; ++i) { + /* + * NOTE: temp[] is s8 and we assume 2's complement + * "conversion" in the assignment + */ data->temp[i] = adm1026_read_value(client, ADM1026_REG_TEMP[i]); } @@ -608,13 +616,13 @@ static struct adm1026_data *adm1026_update_device(struct device *dev) data->gpio = gpio; data->last_reading = jiffies; - }; /* last_reading */ + } /* last_reading */ if (!data->valid || time_after(jiffies, data->last_config + ADM1026_CONFIG_INTERVAL)) { /* Things that don't change often */ dev_dbg(&client->dev, "Reading config values\n"); - for (i = 0;i <= 16;++i) { + for (i = 0; i <= 16; ++i) { data->in_min[i] = adm1026_read_value(client, ADM1026_REG_IN_MIN[i]); data->in_max[i] = adm1026_read_value(client, @@ -624,7 +632,7 @@ static struct adm1026_data *adm1026_update_device(struct device *dev) value = adm1026_read_value(client, ADM1026_REG_FAN_DIV_0_3) | (adm1026_read_value(client, ADM1026_REG_FAN_DIV_4_7) << 8); - for (i = 0;i <= 7;++i) { + for (i = 0; i <= 7; ++i) { data->fan_min[i] = adm1026_read_value(client, ADM1026_REG_FAN_MIN(i)); data->fan_div[i] = DIV_FROM_REG(value & 0x03); @@ -632,7 +640,8 @@ static struct adm1026_data *adm1026_update_device(struct device *dev) } for (i = 0; i <= 2; ++i) { - /* NOTE: temp_xxx[] are s8 and we assume 2's + /* + * NOTE: temp_xxx[] are s8 and we assume 2's * complement "conversion" in the assignment */ data->temp_min[i] = adm1026_read_value(client, @@ -681,7 +690,7 @@ static struct adm1026_data *adm1026_update_device(struct device *dev) data->gpio_config[16] = (data->config3 >> 6) & 0x03; value = 0; - for (i = 0;i <= 15;++i) { + for (i = 0; i <= 15; ++i) { if ((i & 0x03) == 0) { value = adm1026_read_value(client, ADM1026_REG_GPIO_CFG_0_3 + i/4); @@ -691,7 +700,7 @@ static struct adm1026_data *adm1026_update_device(struct device *dev) } data->last_config = jiffies; - }; /* last_config */ + } /* last_config */ data->valid = 1; mutex_unlock(&data->update_lock); @@ -721,7 +730,12 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_min[nr] = INS_TO_REG(nr, val); @@ -744,7 +758,12 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_max[nr] = INS_TO_REG(nr, val); @@ -779,23 +798,31 @@ in_reg(13); in_reg(14); in_reg(15); -static ssize_t show_in16(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_in16(struct device *dev, struct device_attribute *attr, + char *buf) { struct adm1026_data *data = adm1026_update_device(dev); return sprintf(buf, "%d\n", INS_FROM_REG(16, data->in[16]) - NEG12_OFFSET); } -static ssize_t show_in16_min(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_in16_min(struct device *dev, struct device_attribute *attr, + char *buf) { struct adm1026_data *data = adm1026_update_device(dev); return sprintf(buf, "%d\n", INS_FROM_REG(16, data->in_min[16]) - NEG12_OFFSET); } -static ssize_t set_in16_min(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t set_in16_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_min[16] = INS_TO_REG(16, val + NEG12_OFFSET); @@ -803,17 +830,24 @@ static ssize_t set_in16_min(struct device *dev, struct device_attribute *attr, c mutex_unlock(&data->update_lock); return count; } -static ssize_t show_in16_max(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_in16_max(struct device *dev, struct device_attribute *attr, + char *buf) { struct adm1026_data *data = adm1026_update_device(dev); return sprintf(buf, "%d\n", INS_FROM_REG(16, data->in_max[16]) - NEG12_OFFSET); } -static ssize_t set_in16_max(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t set_in16_max(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_max[16] = INS_TO_REG(16, val+NEG12_OFFSET); @@ -823,10 +857,10 @@ static ssize_t set_in16_max(struct device *dev, struct device_attribute *attr, c } static SENSOR_DEVICE_ATTR(in16_input, S_IRUGO, show_in16, NULL, 16); -static SENSOR_DEVICE_ATTR(in16_min, S_IRUGO | S_IWUSR, show_in16_min, set_in16_min, 16); -static SENSOR_DEVICE_ATTR(in16_max, S_IRUGO | S_IWUSR, show_in16_max, set_in16_max, 16); - - +static SENSOR_DEVICE_ATTR(in16_min, S_IRUGO | S_IWUSR, show_in16_min, + set_in16_min, 16); +static SENSOR_DEVICE_ATTR(in16_max, S_IRUGO | S_IWUSR, show_in16_max, + set_in16_max, 16); /* Now add fan read/write functions */ @@ -856,7 +890,12 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->fan_min[nr] = FAN_TO_REG(val, data->fan_div[nr]); @@ -890,12 +929,11 @@ static void fixup_fan_min(struct device *dev, int fan, int old_div) int new_div = data->fan_div[fan]; /* 0 and 0xff are special. Don't adjust them */ - if (data->fan_min[fan] == 0 || data->fan_min[fan] == 0xff) { + if (data->fan_min[fan] == 0 || data->fan_min[fan] == 0xff) return; - } new_min = data->fan_min[fan] * old_div / new_div; - new_min = SENSORS_LIMIT(new_min, 1, 254); + new_min = clamp_val(new_min, 1, 254); data->fan_min[fan] = new_min; adm1026_write_value(client, ADM1026_REG_FAN_MIN(fan), new_min); } @@ -916,32 +954,37 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct adm1026_data *data = i2c_get_clientdata(client); - int val, orig_div, new_div, shift; + long val; + int orig_div, new_div; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; - val = simple_strtol(buf, NULL, 10); new_div = DIV_TO_REG(val); - if (new_div == 0) { - return -EINVAL; - } + mutex_lock(&data->update_lock); orig_div = data->fan_div[nr]; data->fan_div[nr] = DIV_FROM_REG(new_div); if (nr < 4) { /* 0 <= nr < 4 */ - shift = 2 * nr; adm1026_write_value(client, ADM1026_REG_FAN_DIV_0_3, - ((DIV_TO_REG(orig_div) & (~(0x03 << shift))) | - (new_div << shift))); + (DIV_TO_REG(data->fan_div[0]) << 0) | + (DIV_TO_REG(data->fan_div[1]) << 2) | + (DIV_TO_REG(data->fan_div[2]) << 4) | + (DIV_TO_REG(data->fan_div[3]) << 6)); } else { /* 3 < nr < 8 */ - shift = 2 * (nr - 4); adm1026_write_value(client, ADM1026_REG_FAN_DIV_4_7, - ((DIV_TO_REG(orig_div) & (~(0x03 << (2 * shift)))) | - (new_div << shift))); + (DIV_TO_REG(data->fan_div[4]) << 0) | + (DIV_TO_REG(data->fan_div[5]) << 2) | + (DIV_TO_REG(data->fan_div[6]) << 4) | + (DIV_TO_REG(data->fan_div[7]) << 6)); } - if (data->fan_div[nr] != orig_div) { + if (data->fan_div[nr] != orig_div) fixup_fan_min(dev, nr, orig_div); - } + mutex_unlock(&data->update_lock); return count; } @@ -983,7 +1026,12 @@ static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_min[nr] = TEMP_TO_REG(val); @@ -1007,7 +1055,12 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_max[nr] = TEMP_TO_REG(val); @@ -1046,7 +1099,12 @@ static ssize_t set_temp_offset(struct device *dev, int nr = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_offset[nr] = TEMP_TO_REG(val); @@ -1056,8 +1114,8 @@ static ssize_t set_temp_offset(struct device *dev, return count; } -#define temp_offset_reg(offset) \ -static SENSOR_DEVICE_ATTR(temp##offset##_offset, S_IRUGO | S_IWUSR, \ +#define temp_offset_reg(offset) \ +static SENSOR_DEVICE_ATTR(temp##offset##_offset, S_IRUGO | S_IWUSR, \ show_temp_offset, set_temp_offset, offset - 1); temp_offset_reg(1); @@ -1097,7 +1155,12 @@ static ssize_t set_temp_auto_point1_temp(struct device *dev, int nr = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_tmin[nr] = TEMP_TO_REG(val); @@ -1131,15 +1194,21 @@ static ssize_t set_temp_crit_enable(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + if (val > 1) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->config1 = (data->config1 & ~CFG1_THERM_HOT) | (val << 4); + adm1026_write_value(client, ADM1026_REG_CONFIG1, data->config1); + mutex_unlock(&data->update_lock); - if ((val == 1) || (val==0)) { - mutex_lock(&data->update_lock); - data->config1 = (data->config1 & ~CFG1_THERM_HOT) | (val << 4); - adm1026_write_value(client, ADM1026_REG_CONFIG1, - data->config1); - mutex_unlock(&data->update_lock); - } return count; } @@ -1166,7 +1235,12 @@ static ssize_t set_temp_crit(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_crit[nr] = TEMP_TO_REG(val); @@ -1184,17 +1258,24 @@ temp_crit_reg(1); temp_crit_reg(2); temp_crit_reg(3); -static ssize_t show_analog_out_reg(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_analog_out_reg(struct device *dev, + struct device_attribute *attr, char *buf) { struct adm1026_data *data = adm1026_update_device(dev); return sprintf(buf, "%d\n", DAC_FROM_REG(data->analog_out)); } -static ssize_t set_analog_out_reg(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) +static ssize_t set_analog_out_reg(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->analog_out = DAC_TO_REG(val); @@ -1206,7 +1287,8 @@ static ssize_t set_analog_out_reg(struct device *dev, struct device_attribute *a static DEVICE_ATTR(analog_out, S_IRUGO | S_IWUSR, show_analog_out_reg, set_analog_out_reg); -static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, + char *buf) { struct adm1026_data *data = adm1026_update_device(dev); int vid = (data->gpio >> 11) & 0x1f; @@ -1214,25 +1296,35 @@ static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, c dev_dbg(dev, "Setting VID from GPIO11-15.\n"); return sprintf(buf, "%d\n", vid_from_reg(vid, data->vrm)); } + static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); -static ssize_t show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_vrm_reg(struct device *dev, struct device_attribute *attr, + char *buf) { struct adm1026_data *data = dev_get_drvdata(dev); return sprintf(buf, "%d\n", data->vrm); } -static ssize_t store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) + +static ssize_t store_vrm_reg(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct adm1026_data *data = dev_get_drvdata(dev); + unsigned long val; + int err; - data->vrm = simple_strtol(buf, NULL, 10); + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + data->vrm = val; return count; } static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); -static ssize_t show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_alarms_reg(struct device *dev, + struct device_attribute *attr, char *buf) { struct adm1026_data *data = adm1026_update_device(dev); return sprintf(buf, "%ld\n", data->alarms); @@ -1277,18 +1369,24 @@ static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 24); static SENSOR_DEVICE_ATTR(in10_alarm, S_IRUGO, show_alarm, NULL, 25); static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 26); -static ssize_t show_alarm_mask(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_alarm_mask(struct device *dev, + struct device_attribute *attr, char *buf) { struct adm1026_data *data = adm1026_update_device(dev); return sprintf(buf, "%ld\n", data->alarm_mask); } -static ssize_t set_alarm_mask(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) +static ssize_t set_alarm_mask(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); unsigned long mask; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->alarm_mask = val & 0x7fffffff; @@ -1313,18 +1411,24 @@ static DEVICE_ATTR(alarm_mask, S_IRUGO | S_IWUSR, show_alarm_mask, set_alarm_mask); -static ssize_t show_gpio(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_gpio(struct device *dev, struct device_attribute *attr, + char *buf) { struct adm1026_data *data = adm1026_update_device(dev); return sprintf(buf, "%ld\n", data->gpio); } -static ssize_t set_gpio(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) +static ssize_t set_gpio(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); long gpio; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->gpio = val & 0x1ffff; @@ -1340,19 +1444,24 @@ static ssize_t set_gpio(struct device *dev, struct device_attribute *attr, const static DEVICE_ATTR(gpio, S_IRUGO | S_IWUSR, show_gpio, set_gpio); - -static ssize_t show_gpio_mask(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_gpio_mask(struct device *dev, struct device_attribute *attr, + char *buf) { struct adm1026_data *data = adm1026_update_device(dev); return sprintf(buf, "%ld\n", data->gpio_mask); } -static ssize_t set_gpio_mask(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) +static ssize_t set_gpio_mask(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); long mask; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->gpio_mask = val & 0x1ffff; @@ -1368,19 +1477,26 @@ static ssize_t set_gpio_mask(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(gpio_mask, S_IRUGO | S_IWUSR, show_gpio_mask, set_gpio_mask); -static ssize_t show_pwm_reg(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_pwm_reg(struct device *dev, struct device_attribute *attr, + char *buf) { struct adm1026_data *data = adm1026_update_device(dev); return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm1.pwm)); } -static ssize_t set_pwm_reg(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) + +static ssize_t set_pwm_reg(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct adm1026_data *data = i2c_get_clientdata(client); if (data->pwm1.enable == 1) { - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->pwm1.pwm = PWM_TO_REG(val); @@ -1389,20 +1505,29 @@ static ssize_t set_pwm_reg(struct device *dev, struct device_attribute *attr, co } return count; } -static ssize_t show_auto_pwm_min(struct device *dev, struct device_attribute *attr, char *buf) + +static ssize_t show_auto_pwm_min(struct device *dev, + struct device_attribute *attr, char *buf) { struct adm1026_data *data = adm1026_update_device(dev); return sprintf(buf, "%d\n", data->pwm1.auto_pwm_min); } -static ssize_t set_auto_pwm_min(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) + +static ssize_t set_auto_pwm_min(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) { struct i2c_client *client = to_i2c_client(dev); struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - data->pwm1.auto_pwm_min = SENSORS_LIMIT(val, 0, 255); + data->pwm1.auto_pwm_min = clamp_val(val, 0, 255); if (data->pwm1.enable == 2) { /* apply immediately */ data->pwm1.pwm = PWM_TO_REG((data->pwm1.pwm & 0x0f) | PWM_MIN_TO_REG(data->pwm1.auto_pwm_min)); @@ -1411,44 +1536,53 @@ static ssize_t set_auto_pwm_min(struct device *dev, struct device_attribute *att mutex_unlock(&data->update_lock); return count; } -static ssize_t show_auto_pwm_max(struct device *dev, struct device_attribute *attr, char *buf) + +static ssize_t show_auto_pwm_max(struct device *dev, + struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", ADM1026_PWM_MAX); } -static ssize_t show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf) + +static ssize_t show_pwm_enable(struct device *dev, + struct device_attribute *attr, char *buf) { struct adm1026_data *data = adm1026_update_device(dev); return sprintf(buf, "%d\n", data->pwm1.enable); } -static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) + +static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); int old_enable; + unsigned long val; + int err; - if ((val >= 0) && (val < 3)) { - mutex_lock(&data->update_lock); - old_enable = data->pwm1.enable; - data->pwm1.enable = val; - data->config1 = (data->config1 & ~CFG1_PWM_AFC) - | ((val == 2) ? CFG1_PWM_AFC : 0); - adm1026_write_value(client, ADM1026_REG_CONFIG1, - data->config1); - if (val == 2) { /* apply pwm1_auto_pwm_min to pwm1 */ - data->pwm1.pwm = PWM_TO_REG((data->pwm1.pwm & 0x0f) | - PWM_MIN_TO_REG(data->pwm1.auto_pwm_min)); - adm1026_write_value(client, ADM1026_REG_PWM, - data->pwm1.pwm); - } else if (!((old_enable == 1) && (val == 1))) { - /* set pwm to safe value */ - data->pwm1.pwm = 255; - adm1026_write_value(client, ADM1026_REG_PWM, - data->pwm1.pwm); - } - mutex_unlock(&data->update_lock); + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + if (val >= 3) + return -EINVAL; + + mutex_lock(&data->update_lock); + old_enable = data->pwm1.enable; + data->pwm1.enable = val; + data->config1 = (data->config1 & ~CFG1_PWM_AFC) + | ((val == 2) ? CFG1_PWM_AFC : 0); + adm1026_write_value(client, ADM1026_REG_CONFIG1, data->config1); + if (val == 2) { /* apply pwm1_auto_pwm_min to pwm1 */ + data->pwm1.pwm = PWM_TO_REG((data->pwm1.pwm & 0x0f) | + PWM_MIN_TO_REG(data->pwm1.auto_pwm_min)); + adm1026_write_value(client, ADM1026_REG_PWM, data->pwm1.pwm); + } else if (!((old_enable == 1) && (val == 1))) { + /* set pwm to safe value */ + data->pwm1.pwm = 255; + adm1026_write_value(client, ADM1026_REG_PWM, data->pwm1.pwm); } + mutex_unlock(&data->update_lock); + return count; } @@ -1657,15 +1791,15 @@ static int adm1026_detect(struct i2c_client *client, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { /* We need to be able to do byte I/O */ return -ENODEV; - }; + } /* Now, we do the remaining detection. */ company = adm1026_read_value(client, ADM1026_REG_COMPANY); verstep = adm1026_read_value(client, ADM1026_REG_VERSTEP); - dev_dbg(&adapter->dev, "Detecting device at %d,0x%02x with" - " COMPANY: 0x%02x and VERSTEP: 0x%02x\n", + dev_dbg(&adapter->dev, + "Detecting device at %d,0x%02x with COMPANY: 0x%02x and VERSTEP: 0x%02x\n", i2c_adapter_id(client->adapter), client->addr, company, verstep); @@ -1677,11 +1811,12 @@ static int adm1026_detect(struct i2c_client *client, /* Analog Devices ADM1026 */ } else if (company == ADM1026_COMPANY_ANALOG_DEV && (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "Unrecognized stepping " - "0x%02x. Defaulting to ADM1026.\n", verstep); + dev_err(&adapter->dev, + "Unrecognized stepping 0x%02x. Defaulting to ADM1026.\n", + verstep); } else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "Found version/stepping " - "0x%02x. Assuming generic ADM1026.\n", + dev_err(&adapter->dev, + "Found version/stepping 0x%02x. Assuming generic ADM1026.\n", verstep); } else { dev_dbg(&adapter->dev, "Autodetection failed\n"); @@ -1700,11 +1835,10 @@ static int adm1026_probe(struct i2c_client *client, struct adm1026_data *data; int err; - data = kzalloc(sizeof(struct adm1026_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&client->dev, sizeof(struct adm1026_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); mutex_init(&data->update_lock); @@ -1716,8 +1850,9 @@ static int adm1026_probe(struct i2c_client *client, adm1026_init_client(client); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&client->dev.kobj, &adm1026_group))) - goto exitfree; + err = sysfs_create_group(&client->dev.kobj, &adm1026_group); + if (err) + return err; if (data->config1 & CFG1_AIN8_9) err = sysfs_create_group(&client->dev.kobj, &adm1026_group_in8_9); @@ -1742,9 +1877,6 @@ exitremove: sysfs_remove_group(&client->dev.kobj, &adm1026_group_in8_9); else sysfs_remove_group(&client->dev.kobj, &adm1026_group_temp3); -exitfree: - kfree(data); -exit: return err; } @@ -1757,24 +1889,12 @@ static int adm1026_remove(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &adm1026_group_in8_9); else sysfs_remove_group(&client->dev.kobj, &adm1026_group_temp3); - kfree(data); return 0; } -static int __init sm_adm1026_init(void) -{ - return i2c_add_driver(&adm1026_driver); -} - -static void __exit sm_adm1026_exit(void) -{ - i2c_del_driver(&adm1026_driver); -} +module_i2c_driver(adm1026_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Philip Pokorny <ppokorny@penguincomputing.com>, " "Justin Thiessen <jthiessen@penguincomputing.com>"); MODULE_DESCRIPTION("ADM1026 driver"); - -module_init(sm_adm1026_init); -module_exit(sm_adm1026_exit); diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c index 0b8a3b145bd..2804571b269 100644 --- a/drivers/hwmon/adm1029.c +++ b/drivers/hwmon/adm1029.c @@ -1,9 +1,9 @@ /* * adm1029.c - Part of lm_sensors, Linux kernel modules for hardware monitoring * - * Copyright (C) 2006 Corentin LABBE <corentin.labbe@geomatys.fr> + * Copyright (C) 2006 Corentin LABBE <clabbe.montjoie@gmail.com> * - * Based on LM83 Driver by Jean Delvare <khali@linux-fr.org> + * Based on LM83 Driver by Jean Delvare <jdelvare@suse.de> * * Give only processor, motherboard temperatures and fan tachs * Very rare chip please let me know if you use it @@ -78,7 +78,7 @@ static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, #define TEMP_FROM_REG(val) ((val) * 1000) -#define DIV_FROM_REG(val) ( 1 << (((val) >> 6) - 1)) +#define DIV_FROM_REG(val) (1 << (((val) >> 6) - 1)) /* Registers to be checked by adm1029_update_device() */ static const u8 ADM1029_REG_TEMP[] = { @@ -200,8 +200,11 @@ static ssize_t set_fan_div(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct adm1029_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - long val = simple_strtol(buf, NULL, 10); u8 reg; + long val; + int ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; mutex_lock(&data->update_lock); @@ -221,13 +224,17 @@ static ssize_t set_fan_div(struct device *dev, break; default: mutex_unlock(&data->update_lock); - dev_err(&client->dev, "fan_div value %ld not " - "supported. Choose one of 1, 2 or 4!\n", val); + dev_err(&client->dev, + "fan_div value %ld not supported. Choose one of 1, 2 or 4!\n", + val); return -EINVAL; } /* Update the value */ reg = (reg & 0x3F) | (val << 6); + /* Update the cache */ + data->fan_div[attr->index] = reg; + /* Write value */ i2c_smbus_write_byte_data(client, ADM1029_REG_FAN_DIV[attr->index], reg); @@ -237,9 +244,9 @@ static ssize_t set_fan_div(struct device *dev, } /* -Access rights on sysfs, S_IRUGO stand for Is Readable by User, Group and Others - S_IWUSR stand for Is Writable by User -*/ + * Access rights on sysfs. S_IRUGO: Is Readable by User, Group and Others + * S_IWUSR: Is Writable by User. + */ static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); @@ -300,7 +307,8 @@ static int adm1029_detect(struct i2c_client *client, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* ADM1029 doesn't have CHIP ID, check just MAN ID + /* + * ADM1029 doesn't have CHIP ID, check just MAN ID * For better detection we check also ADM1029_TEMP_DEVICES_INSTALLED, * ADM1029_REG_NB_FAN_SUPPORT and compare it with possible values * documented @@ -318,10 +326,12 @@ static int adm1029_detect(struct i2c_client *client, return -ENODEV; if ((chip_id & 0xF0) != 0x00) { - /* There are no "official" CHIP ID, so actually - * we use Major/Minor revision for that */ - pr_info("adm1029: Unknown major revision %x, " - "please let us know\n", chip_id); + /* + * There are no "official" CHIP ID, so actually + * we use Major/Minor revision for that + */ + pr_info("Unknown major revision %x, please let us know\n", + chip_id); return -ENODEV; } @@ -336,11 +346,10 @@ static int adm1029_probe(struct i2c_client *client, struct adm1029_data *data; int err; - data = kzalloc(sizeof(struct adm1029_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&client->dev, sizeof(struct adm1029_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); mutex_init(&data->update_lock); @@ -349,14 +358,13 @@ static int adm1029_probe(struct i2c_client *client, * Initialize the ADM1029 chip * Check config register */ - if (adm1029_init_client(client) == 0) { - err = -ENODEV; - goto exit_free; - } + if (adm1029_init_client(client) == 0) + return -ENODEV; /* Register sysfs hooks */ - if ((err = sysfs_create_group(&client->dev.kobj, &adm1029_group))) - goto exit_free; + err = sysfs_create_group(&client->dev.kobj, &adm1029_group); + if (err) + return err; data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { @@ -368,9 +376,6 @@ static int adm1029_probe(struct i2c_client *client, exit_remove_files: sysfs_remove_group(&client->dev.kobj, &adm1029_group); - exit_free: - kfree(data); - exit: return err; } @@ -398,13 +403,12 @@ static int adm1029_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &adm1029_group); - kfree(data); return 0; } /* -function that update the status of the chips (temperature for example) -*/ + * function that update the status of the chips (temperature for example) + */ static struct adm1029_data *adm1029_update_device(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -446,24 +450,8 @@ static struct adm1029_data *adm1029_update_device(struct device *dev) return data; } -/* - Common module stuff -*/ -static int __init sensors_adm1029_init(void) -{ - - return i2c_add_driver(&adm1029_driver); -} - -static void __exit sensors_adm1029_exit(void) -{ - - i2c_del_driver(&adm1029_driver); -} +module_i2c_driver(adm1029_driver); -MODULE_AUTHOR("Corentin LABBE <corentin.labbe@geomatys.fr>"); +MODULE_AUTHOR("Corentin LABBE <clabbe.montjoie@gmail.com>"); MODULE_DESCRIPTION("adm1029 driver"); MODULE_LICENSE("GPL v2"); - -module_init(sensors_adm1029_init); -module_exit(sensors_adm1029_exit); diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c index 1644b92e7cc..51c1a5a165a 100644 --- a/drivers/hwmon/adm1031.c +++ b/drivers/hwmon/adm1031.c @@ -1,25 +1,25 @@ /* - adm1031.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Based on lm75.c and lm85.c - Supports adm1030 / adm1031 - Copyright (C) 2004 Alexandre d'Alton <alex@alexdalton.org> - Reworked by 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; 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. -*/ + * adm1031.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Based on lm75.c and lm85.c + * Supports adm1030 / adm1031 + * Copyright (C) 2004 Alexandre d'Alton <alex@alexdalton.org> + * Reworked by Jean Delvare <jdelvare@suse.de> + * + * 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> @@ -36,6 +36,7 @@ #define ADM1031_REG_FAN_DIV(nr) (0x20 + (nr)) #define ADM1031_REG_PWM (0x22) #define ADM1031_REG_FAN_MIN(nr) (0x10 + (nr)) +#define ADM1031_REG_FAN_FILTER (0x23) #define ADM1031_REG_TEMP_OFFSET(nr) (0x0d + (nr)) #define ADM1031_REG_TEMP_MAX(nr) (0x14 + 4 * (nr)) @@ -61,6 +62,9 @@ #define ADM1031_CONF2_TACH2_ENABLE 0x08 #define ADM1031_CONF2_TEMP_ENABLE(chan) (0x10 << (chan)) +#define ADM1031_UPDATE_RATE_MASK 0x1c +#define ADM1031_UPDATE_RATE_SHIFT 2 + /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; @@ -75,7 +79,9 @@ struct adm1031_data { int chip_type; char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ - /* The chan_select_table contains the possible configurations for + unsigned int update_interval; /* In milliseconds */ + /* + * The chan_select_table contains the possible configurations for * auto fan control. */ const auto_chan_table_t *chan_select_table; @@ -150,18 +156,19 @@ adm1031_write_value(struct i2c_client *client, u8 reg, unsigned int value) #define TEMP_OFFSET_FROM_REG(val) TEMP_FROM_REG((val) < 0 ? \ (val) | 0x70 : (val)) -#define FAN_FROM_REG(reg, div) ((reg) ? (11250 * 60) / ((reg) * (div)) : 0) +#define FAN_FROM_REG(reg, div) ((reg) ? \ + (11250 * 60) / ((reg) * (div)) : 0) static int FAN_TO_REG(int reg, int div) { int tmp; - tmp = FAN_FROM_REG(SENSORS_LIMIT(reg, 0, 65535), div); + tmp = FAN_FROM_REG(clamp_val(reg, 0, 65535), div); return tmp > 255 ? 255 : tmp; } #define FAN_DIV_FROM_REG(reg) (1<<(((reg)&0xc0)>>6)) -#define PWM_TO_REG(val) (SENSORS_LIMIT((val), 0, 255) >> 4) +#define PWM_TO_REG(val) (clamp_val((val), 0, 255) >> 4) #define PWM_FROM_REG(val) ((val) << 4) #define FAN_CHAN_FROM_REG(reg) (((reg) >> 5) & 7) @@ -169,8 +176,8 @@ static int FAN_TO_REG(int reg, int div) (((reg) & 0x1F) | (((val) << 5) & 0xe0)) #define AUTO_TEMP_MIN_TO_REG(val, reg) \ - ((((val)/500) & 0xf8)|((reg) & 0x7)) -#define AUTO_TEMP_RANGE_FROM_REG(reg) (5000 * (1<< ((reg)&0x7))) + ((((val) / 500) & 0xf8) | ((reg) & 0x7)) +#define AUTO_TEMP_RANGE_FROM_REG(reg) (5000 * (1 << ((reg) & 0x7))) #define AUTO_TEMP_MIN_FROM_REG(reg) (1000 * ((((reg) >> 3) & 0x1f) << 2)) #define AUTO_TEMP_MIN_FROM_REG_DEG(reg) ((((reg) >> 3) & 0x1f) << 2) @@ -197,9 +204,10 @@ static int AUTO_TEMP_MAX_TO_REG(int val, int reg, int pwm) /* FAN auto control */ #define GET_FAN_AUTO_BITFIELD(data, idx) \ - (*(data)->chan_select_table)[FAN_CHAN_FROM_REG((data)->conf1)][idx%2] + (*(data)->chan_select_table)[FAN_CHAN_FROM_REG((data)->conf1)][idx % 2] -/* The tables below contains the possible values for the auto fan +/* + * The tables below contains the possible values for the auto fan * control bitfields. the index in the table is the register value. * MSb is the auto fan control enable bit, so the four first entries * in the table disables auto fan control when both bitfields are zero. @@ -220,22 +228,20 @@ static const auto_chan_table_t auto_channel_select_table_adm1030 = { { 3 /* 0b11 */ , 0 }, }; -/* That function checks if a bitfield is valid and returns the other bitfield +/* + * That function checks if a bitfield is valid and returns the other bitfield * nearest match if no exact match where found. */ static int -get_fan_auto_nearest(struct adm1031_data *data, - int chan, u8 val, u8 reg, u8 * new_reg) +get_fan_auto_nearest(struct adm1031_data *data, int chan, u8 val, u8 reg) { int i; int first_match = -1, exact_match = -1; u8 other_reg_val = (*data->chan_select_table)[FAN_CHAN_FROM_REG(reg)][chan ? 0 : 1]; - if (val == 0) { - *new_reg = 0; + if (val == 0) return 0; - } for (i = 0; i < 8; i++) { if ((val == (*data->chan_select_table)[i][chan]) && @@ -246,21 +252,20 @@ get_fan_auto_nearest(struct adm1031_data *data, break; } else if (val == (*data->chan_select_table)[i][chan] && first_match == -1) { - /* Save the first match in case of an exact match has + /* + * Save the first match in case of an exact match has * not been found */ first_match = i; } } - if (exact_match >= 0) { - *new_reg = exact_match; - } else if (first_match >= 0) { - *new_reg = first_match; - } else { - return -EINVAL; - } - return 0; + if (exact_match >= 0) + return exact_match; + else if (first_match >= 0) + return first_match; + + return -EINVAL; } static ssize_t show_fan_auto_channel(struct device *dev, @@ -278,26 +283,34 @@ set_fan_auto_channel(struct device *dev, struct device_attribute *attr, struct i2c_client *client = to_i2c_client(dev); struct adm1031_data *data = i2c_get_clientdata(client); int nr = to_sensor_dev_attr(attr)->index; - int val = simple_strtol(buf, NULL, 10); + long val; u8 reg; int ret; u8 old_fan_mode; + ret = kstrtol(buf, 10, &val); + if (ret) + return ret; + old_fan_mode = data->conf1; mutex_lock(&data->update_lock); - if ((ret = get_fan_auto_nearest(data, nr, val, data->conf1, ®))) { + ret = get_fan_auto_nearest(data, nr, val, data->conf1); + if (ret < 0) { mutex_unlock(&data->update_lock); return ret; } + reg = ret; data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1); if ((data->conf1 & ADM1031_CONF1_AUTO_MODE) ^ (old_fan_mode & ADM1031_CONF1_AUTO_MODE)) { - if (data->conf1 & ADM1031_CONF1_AUTO_MODE){ - /* Switch to Auto Fan Mode + if (data->conf1 & ADM1031_CONF1_AUTO_MODE) { + /* + * Switch to Auto Fan Mode * Save PWM registers - * Set PWM registers to 33% Both */ + * Set PWM registers to 33% Both + */ data->old_pwm[0] = data->pwm[0]; data->old_pwm[1] = data->pwm[1]; adm1031_write_value(client, ADM1031_REG_PWM, 0x55); @@ -345,8 +358,14 @@ set_auto_temp_min(struct device *dev, struct device_attribute *attr, struct i2c_client *client = to_i2c_client(dev); struct adm1031_data *data = i2c_get_clientdata(client); int nr = to_sensor_dev_attr(attr)->index; - int val = simple_strtol(buf, NULL, 10); + long val; + int ret; + + ret = kstrtol(buf, 10, &val); + if (ret) + return ret; + val = clamp_val(val, 0, 127000); mutex_lock(&data->update_lock); data->auto_temp[nr] = AUTO_TEMP_MIN_TO_REG(val, data->auto_temp[nr]); adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr), @@ -369,10 +388,17 @@ set_auto_temp_max(struct device *dev, struct device_attribute *attr, struct i2c_client *client = to_i2c_client(dev); struct adm1031_data *data = i2c_get_clientdata(client); int nr = to_sensor_dev_attr(attr)->index; - int val = simple_strtol(buf, NULL, 10); + long val; + int ret; + ret = kstrtol(buf, 10, &val); + if (ret) + return ret; + + val = clamp_val(val, 0, 127000); mutex_lock(&data->update_lock); - data->temp_max[nr] = AUTO_TEMP_MAX_TO_REG(val, data->auto_temp[nr], data->pwm[nr]); + data->temp_max[nr] = AUTO_TEMP_MAX_TO_REG(val, data->auto_temp[nr], + data->pwm[nr]); adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr), data->temp_max[nr]); mutex_unlock(&data->update_lock); @@ -405,8 +431,12 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, struct i2c_client *client = to_i2c_client(dev); struct adm1031_data *data = i2c_get_clientdata(client); int nr = to_sensor_dev_attr(attr)->index; - int val = simple_strtol(buf, NULL, 10); - int reg; + long val; + int ret, reg; + + ret = kstrtol(buf, 10, &val); + if (ret) + return ret; mutex_lock(&data->update_lock); if ((data->conf1 & ADM1031_CONF1_AUTO_MODE) && @@ -444,9 +474,13 @@ static int trust_fan_readings(struct adm1031_data *data, int chan) if (data->conf1 & ADM1031_CONF1_AUTO_MODE) { switch (data->conf1 & 0x60) { - case 0x00: /* remote temp1 controls fan1 remote temp2 controls fan2 */ + case 0x00: + /* + * remote temp1 controls fan1, + * remote temp2 controls fan2 + */ res = data->temp[chan+1] >= - AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[chan+1]); + AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[chan+1]); break; case 0x20: /* remote temp1 controls both fans */ res = @@ -510,7 +544,12 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, struct i2c_client *client = to_i2c_client(dev); struct adm1031_data *data = i2c_get_clientdata(client); int nr = to_sensor_dev_attr(attr)->index; - int val = simple_strtol(buf, NULL, 10); + long val; + int ret; + + ret = kstrtol(buf, 10, &val); + if (ret) + return ret; mutex_lock(&data->update_lock); if (val) { @@ -529,10 +568,15 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, struct i2c_client *client = to_i2c_client(dev); struct adm1031_data *data = i2c_get_clientdata(client); int nr = to_sensor_dev_attr(attr)->index; - int val = simple_strtol(buf, NULL, 10); + long val; u8 tmp; int old_div; int new_min; + int ret; + + ret = kstrtol(buf, 10, &val); + if (ret) + return ret; tmp = val == 8 ? 0xc0 : val == 4 ? 0x80 : @@ -626,10 +670,14 @@ static ssize_t set_temp_offset(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct adm1031_data *data = i2c_get_clientdata(client); int nr = to_sensor_dev_attr(attr)->index; - int val; + long val; + int ret; + + ret = kstrtol(buf, 10, &val); + if (ret) + return ret; - val = simple_strtol(buf, NULL, 10); - val = SENSORS_LIMIT(val, -15000, 15000); + val = clamp_val(val, -15000, 15000); mutex_lock(&data->update_lock); data->temp_offset[nr] = TEMP_OFFSET_TO_REG(val); adm1031_write_value(client, ADM1031_REG_TEMP_OFFSET(nr), @@ -643,10 +691,14 @@ static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr, struct i2c_client *client = to_i2c_client(dev); struct adm1031_data *data = i2c_get_clientdata(client); int nr = to_sensor_dev_attr(attr)->index; - int val; + long val; + int ret; - val = simple_strtol(buf, NULL, 10); - val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875); + ret = kstrtol(buf, 10, &val); + if (ret) + return ret; + + val = clamp_val(val, -55000, 127000); mutex_lock(&data->update_lock); data->temp_min[nr] = TEMP_TO_REG(val); adm1031_write_value(client, ADM1031_REG_TEMP_MIN(nr), @@ -660,10 +712,14 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, struct i2c_client *client = to_i2c_client(dev); struct adm1031_data *data = i2c_get_clientdata(client); int nr = to_sensor_dev_attr(attr)->index; - int val; + long val; + int ret; - val = simple_strtol(buf, NULL, 10); - val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875); + ret = kstrtol(buf, 10, &val); + if (ret) + return ret; + + val = clamp_val(val, -55000, 127000); mutex_lock(&data->update_lock); data->temp_max[nr] = TEMP_TO_REG(val); adm1031_write_value(client, ADM1031_REG_TEMP_MAX(nr), @@ -677,10 +733,14 @@ static ssize_t set_temp_crit(struct device *dev, struct device_attribute *attr, struct i2c_client *client = to_i2c_client(dev); struct adm1031_data *data = i2c_get_clientdata(client); int nr = to_sensor_dev_attr(attr)->index; - int val; + long val; + int ret; + + ret = kstrtol(buf, 10, &val); + if (ret) + return ret; - val = simple_strtol(buf, NULL, 10); - val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875); + val = clamp_val(val, -55000, 127000); mutex_lock(&data->update_lock); data->temp_crit[nr] = TEMP_TO_REG(val); adm1031_write_value(client, ADM1031_REG_TEMP_CRIT(nr), @@ -706,7 +766,8 @@ temp_reg(2); temp_reg(3); /* Alarms */ -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, + char *buf) { struct adm1031_data *data = adm1031_update_device(dev); return sprintf(buf, "%d\n", data->alarm); @@ -738,6 +799,60 @@ static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, 12); static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 13); static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 14); +/* Update Interval */ +static const unsigned int update_intervals[] = { + 16000, 8000, 4000, 2000, 1000, 500, 250, 125, +}; + +static ssize_t show_update_interval(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1031_data *data = i2c_get_clientdata(client); + + return sprintf(buf, "%u\n", data->update_interval); +} + +static ssize_t set_update_interval(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1031_data *data = i2c_get_clientdata(client); + unsigned long val; + int i, err; + u8 reg; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + /* + * Find the nearest update interval from the table. + * Use it to determine the matching update rate. + */ + for (i = 0; i < ARRAY_SIZE(update_intervals) - 1; i++) { + if (val >= update_intervals[i]) + break; + } + /* if not found, we point to the last entry (lowest update interval) */ + + /* set the new update rate while preserving other settings */ + reg = adm1031_read_value(client, ADM1031_REG_FAN_FILTER); + reg &= ~ADM1031_UPDATE_RATE_MASK; + reg |= i << ADM1031_UPDATE_RATE_SHIFT; + adm1031_write_value(client, ADM1031_REG_FAN_FILTER, reg); + + mutex_lock(&data->update_lock); + data->update_interval = update_intervals[i]; + mutex_unlock(&data->update_lock); + + return count; +} + +static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval, + set_update_interval); + static struct attribute *adm1031_attributes[] = { &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan1_div.dev_attr.attr, @@ -774,6 +889,7 @@ static struct attribute *adm1031_attributes[] = { &sensor_dev_attr_auto_fan1_min_pwm.dev_attr.attr, + &dev_attr_update_interval.attr, &dev_attr_alarms.attr, NULL @@ -840,11 +956,10 @@ static int adm1031_probe(struct i2c_client *client, struct adm1031_data *data; int err; - data = kzalloc(sizeof(struct adm1031_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&client->dev, sizeof(struct adm1031_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); data->chip_type = id->driver_data; @@ -859,12 +974,13 @@ static int adm1031_probe(struct i2c_client *client, adm1031_init_client(client); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&client->dev.kobj, &adm1031_group))) - goto exit_free; + err = sysfs_create_group(&client->dev.kobj, &adm1031_group); + if (err) + return err; if (data->chip_type == adm1031) { - if ((err = sysfs_create_group(&client->dev.kobj, - &adm1031_group_opt))) + err = sysfs_create_group(&client->dev.kobj, &adm1031_group_opt); + if (err) goto exit_remove; } @@ -879,9 +995,6 @@ static int adm1031_probe(struct i2c_client *client, exit_remove: sysfs_remove_group(&client->dev.kobj, &adm1031_group); sysfs_remove_group(&client->dev.kobj, &adm1031_group_opt); -exit_free: - kfree(data); -exit: return err; } @@ -892,7 +1005,6 @@ static int adm1031_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &adm1031_group); sysfs_remove_group(&client->dev.kobj, &adm1031_group_opt); - kfree(data); return 0; } @@ -900,6 +1012,7 @@ static void adm1031_init_client(struct i2c_client *client) { unsigned int read_val; unsigned int mask; + int i; struct adm1031_data *data = i2c_get_clientdata(client); mask = (ADM1031_CONF2_PWM1_ENABLE | ADM1031_CONF2_TACH1_ENABLE); @@ -909,28 +1022,35 @@ static void adm1031_init_client(struct i2c_client *client) } /* Initialize the ADM1031 chip (enables fan speed reading ) */ read_val = adm1031_read_value(client, ADM1031_REG_CONF2); - if ((read_val | mask) != read_val) { - adm1031_write_value(client, ADM1031_REG_CONF2, read_val | mask); - } + if ((read_val | mask) != read_val) + adm1031_write_value(client, ADM1031_REG_CONF2, read_val | mask); read_val = adm1031_read_value(client, ADM1031_REG_CONF1); if ((read_val | ADM1031_CONF1_MONITOR_ENABLE) != read_val) { - adm1031_write_value(client, ADM1031_REG_CONF1, read_val | - ADM1031_CONF1_MONITOR_ENABLE); + adm1031_write_value(client, ADM1031_REG_CONF1, + read_val | ADM1031_CONF1_MONITOR_ENABLE); } + /* Read the chip's update rate */ + mask = ADM1031_UPDATE_RATE_MASK; + read_val = adm1031_read_value(client, ADM1031_REG_FAN_FILTER); + i = (read_val & mask) >> ADM1031_UPDATE_RATE_SHIFT; + /* Save it as update interval */ + data->update_interval = update_intervals[i]; } static struct adm1031_data *adm1031_update_device(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct adm1031_data *data = i2c_get_clientdata(client); + unsigned long next_update; int chan; mutex_lock(&data->update_lock); - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { + next_update = data->last_updated + + msecs_to_jiffies(data->update_interval); + if (time_after(jiffies, next_update) || !data->valid) { dev_dbg(&client->dev, "Starting adm1031 update\n"); for (chan = 0; @@ -955,8 +1075,7 @@ static struct adm1031_data *adm1031_update_device(struct device *dev) /* oldh is actually newer */ if (newh != oldh) dev_warn(&client->dev, - "Remote temperature may be " - "wrong.\n"); + "Remote temperature may be wrong.\n"); #endif } data->temp[chan] = newh; @@ -983,22 +1102,24 @@ static struct adm1031_data *adm1031_update_device(struct device *dev) data->conf2 = adm1031_read_value(client, ADM1031_REG_CONF2); data->alarm = adm1031_read_value(client, ADM1031_REG_STATUS(0)) - | (adm1031_read_value(client, ADM1031_REG_STATUS(1)) - << 8); - if (data->chip_type == adm1030) { + | (adm1031_read_value(client, ADM1031_REG_STATUS(1)) << 8); + if (data->chip_type == adm1030) data->alarm &= 0xc0ff; - } - for (chan=0; chan<(data->chip_type == adm1030 ? 1 : 2); chan++) { + for (chan = 0; chan < (data->chip_type == adm1030 ? 1 : 2); + chan++) { data->fan_div[chan] = - adm1031_read_value(client, ADM1031_REG_FAN_DIV(chan)); + adm1031_read_value(client, + ADM1031_REG_FAN_DIV(chan)); data->fan_min[chan] = - adm1031_read_value(client, ADM1031_REG_FAN_MIN(chan)); + adm1031_read_value(client, + ADM1031_REG_FAN_MIN(chan)); data->fan[chan] = - adm1031_read_value(client, ADM1031_REG_FAN_SPEED(chan)); + adm1031_read_value(client, + ADM1031_REG_FAN_SPEED(chan)); data->pwm[chan] = - 0xf & (adm1031_read_value(client, ADM1031_REG_PWM) >> - (4*chan)); + (adm1031_read_value(client, + ADM1031_REG_PWM) >> (4 * chan)) & 0x0f; } data->last_updated = jiffies; data->valid = 1; @@ -1009,19 +1130,8 @@ static struct adm1031_data *adm1031_update_device(struct device *dev) return data; } -static int __init sensors_adm1031_init(void) -{ - return i2c_add_driver(&adm1031_driver); -} - -static void __exit sensors_adm1031_exit(void) -{ - i2c_del_driver(&adm1031_driver); -} +module_i2c_driver(adm1031_driver); MODULE_AUTHOR("Alexandre d'Alton <alex@alexdalton.org>"); MODULE_DESCRIPTION("ADM1031/ADM1030 driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_adm1031_init); -module_exit(sensors_adm1031_exit); diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c index 0727ad25079..086d02a9ecd 100644 --- a/drivers/hwmon/adm9240.c +++ b/drivers/hwmon/adm9240.c @@ -1,12 +1,12 @@ /* * adm9240.c Part of lm_sensors, Linux kernel modules for hardware - * monitoring + * monitoring * * Copyright (C) 1999 Frodo Looijaard <frodol@dds.nl> * Philip Edelbrock <phil@netroedge.com> * Copyright (C) 2003 Michiel Rook <michiel@grendelproject.nl> * Copyright (C) 2005 Grant Coady <gcoady.lk@gmail.com> with valuable - * guidance from Jean Delvare + * guidance from Jean Delvare * * Driver supports Analog Devices ADM9240 * Dallas Semiconductor DS1780 @@ -20,7 +20,7 @@ * Alarms 16-bit map of active alarms * Analog Out 0..1250 mV output * - * Chassis Intrusion: clear CI latch with 'echo 1 > chassis_clear' + * Chassis Intrusion: clear CI latch with 'echo 0 > intrusion0_alarm' * * Test hardware: Intel SE440BX-2 desktop motherboard --Grant * @@ -50,6 +50,7 @@ #include <linux/hwmon-vid.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/jiffies.h> /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, @@ -97,13 +98,13 @@ static inline unsigned int IN_FROM_REG(u8 reg, int n) static inline u8 IN_TO_REG(unsigned long val, int n) { - return SENSORS_LIMIT(SCALE(val, 192, nom_mv[n]), 0, 255); + return clamp_val(SCALE(val, 192, nom_mv[n]), 0, 255); } /* temperature range: -40..125, 127 disables temperature alarm */ static inline s8 TEMP_TO_REG(long val) { - return SENSORS_LIMIT(SCALE(val, 1, 1000), -40, 127); + return clamp_val(SCALE(val, 1, 1000), -40, 127); } /* two fans, each with low fan speed limit */ @@ -121,7 +122,7 @@ static inline unsigned int FAN_FROM_REG(u8 reg, u8 div) /* analog out 0..1250mV */ static inline u8 AOUT_TO_REG(unsigned long val) { - return SENSORS_LIMIT(SCALE(val, 255, 1250), 0, 255); + return clamp_val(SCALE(val, 255, 1250), 0, 255); } static inline unsigned int AOUT_FROM_REG(u8 reg) @@ -204,7 +205,12 @@ static ssize_t set_max(struct device *dev, struct device_attribute *devattr, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adm9240_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_max[attr->index] = TEMP_TO_REG(val); @@ -255,7 +261,12 @@ static ssize_t set_in_min(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adm9240_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_min[attr->index] = IN_TO_REG(val, attr->index); @@ -272,7 +283,12 @@ static ssize_t set_in_max(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adm9240_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_max[attr->index] = IN_TO_REG(val, attr->index); @@ -283,7 +299,7 @@ static ssize_t set_in_max(struct device *dev, } #define vin(nr) \ -static SENSOR_DEVICE_ATTR(in##nr##_input, S_IRUGO, \ +static SENSOR_DEVICE_ATTR(in##nr##_input, S_IRUGO, \ show_in, NULL, nr); \ static SENSOR_DEVICE_ATTR(in##nr##_min, S_IRUGO | S_IWUSR, \ show_in_min, set_in_min, nr); \ @@ -335,8 +351,9 @@ static void adm9240_write_fan_div(struct i2c_client *client, int nr, reg &= ~(3 << shift); reg |= (fan_div << shift); i2c_smbus_write_byte_data(client, ADM9240_REG_VID_FAN_DIV, reg); - dev_dbg(&client->dev, "fan%d clock divider changed from %u " - "to %u\n", nr + 1, 1 << old, 1 << fan_div); + dev_dbg(&client->dev, + "fan%d clock divider changed from %u to %u\n", + nr + 1, 1 << old, 1 << fan_div); } /* @@ -357,9 +374,14 @@ static ssize_t set_fan_min(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adm9240_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); int nr = attr->index; u8 new_div; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); @@ -465,7 +487,12 @@ static ssize_t set_aout(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct adm9240_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->aout = AOUT_TO_REG(val); @@ -475,22 +502,27 @@ static ssize_t set_aout(struct device *dev, } static DEVICE_ATTR(aout_output, S_IRUGO | S_IWUSR, show_aout, set_aout); -/* chassis_clear */ static ssize_t chassis_clear(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); - unsigned long val = simple_strtol(buf, NULL, 10); + struct adm9240_data *data = i2c_get_clientdata(client); + unsigned long val; + + if (kstrtoul(buf, 10, &val) || val != 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + i2c_smbus_write_byte_data(client, ADM9240_REG_CHASSIS_CLEAR, 0x80); + data->valid = 0; /* Force cache refresh */ + mutex_unlock(&data->update_lock); + dev_dbg(&client->dev, "chassis intrusion latch cleared\n"); - if (val == 1) { - i2c_smbus_write_byte_data(client, - ADM9240_REG_CHASSIS_CLEAR, 0x80); - dev_dbg(&client->dev, "chassis intrusion latch cleared\n"); - } return count; } -static DEVICE_ATTR(chassis_clear, S_IWUSR, NULL, chassis_clear); +static SENSOR_DEVICE_ATTR(intrusion0_alarm, S_IRUGO | S_IWUSR, show_alarm, + chassis_clear, 12); static struct attribute *adm9240_attributes[] = { &sensor_dev_attr_in0_input.dev_attr.attr, @@ -531,7 +563,7 @@ static struct attribute *adm9240_attributes[] = { &sensor_dev_attr_fan2_alarm.dev_attr.attr, &dev_attr_alarms.attr, &dev_attr_aout_output.attr, - &dev_attr_chassis_clear.attr, + &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, &dev_attr_cpu0_vid.attr, NULL }; @@ -594,11 +626,9 @@ static int adm9240_probe(struct i2c_client *new_client, struct adm9240_data *data; int err; - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&new_client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(new_client, data); mutex_init(&data->update_lock); @@ -606,8 +636,9 @@ static int adm9240_probe(struct i2c_client *new_client, adm9240_init_client(new_client); /* populate sysfs filesystem */ - if ((err = sysfs_create_group(&new_client->dev.kobj, &adm9240_group))) - goto exit_free; + err = sysfs_create_group(&new_client->dev.kobj, &adm9240_group); + if (err) + return err; data->hwmon_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->hwmon_dev)) { @@ -619,9 +650,6 @@ static int adm9240_probe(struct i2c_client *new_client, exit_remove: sysfs_remove_group(&new_client->dev.kobj, &adm9240_group); -exit_free: - kfree(data); -exit: return err; } @@ -632,7 +660,6 @@ static int adm9240_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &adm9240_group); - kfree(data); return 0; } @@ -655,8 +682,7 @@ static void adm9240_init_client(struct i2c_client *client) } else { /* cold start: open limits before starting chip */ int i; - for (i = 0; i < 6; i++) - { + for (i = 0; i < 6; i++) { i2c_smbus_write_byte_data(client, ADM9240_REG_IN_MIN(i), 0); i2c_smbus_write_byte_data(client, @@ -674,8 +700,8 @@ static void adm9240_init_client(struct i2c_client *client) /* start measurement cycle */ i2c_smbus_write_byte_data(client, ADM9240_REG_CONFIG, 1); - dev_info(&client->dev, "cold start: config was 0x%02x " - "mode %u\n", conf, mode); + dev_info(&client->dev, + "cold start: config was 0x%02x mode %u\n", conf, mode); } } @@ -691,8 +717,7 @@ static struct adm9240_data *adm9240_update_device(struct device *dev) if (time_after(jiffies, data->last_updated_measure + (HZ * 7 / 4)) || !data->valid) { - for (i = 0; i < 6; i++) /* read voltages */ - { + for (i = 0; i < 6; i++) { /* read voltages */ data->in[i] = i2c_smbus_read_byte_data(client, ADM9240_REG_IN(i)); } @@ -701,16 +726,17 @@ static struct adm9240_data *adm9240_update_device(struct device *dev) i2c_smbus_read_byte_data(client, ADM9240_REG_INT(1)) << 8; - /* read temperature: assume temperature changes less than + /* + * read temperature: assume temperature changes less than * 0.5'C per two measurement cycles thus ignore possible - * but unlikely aliasing error on lsb reading. --Grant */ + * but unlikely aliasing error on lsb reading. --Grant + */ data->temp = ((i2c_smbus_read_byte_data(client, ADM9240_REG_TEMP) << 8) | i2c_smbus_read_byte_data(client, ADM9240_REG_TEMP_CONF)) / 128; - for (i = 0; i < 2; i++) /* read fans */ - { + for (i = 0; i < 2; i++) { /* read fans */ data->fan[i] = i2c_smbus_read_byte_data(client, ADM9240_REG_FAN(i)); @@ -734,15 +760,13 @@ static struct adm9240_data *adm9240_update_device(struct device *dev) if (time_after(jiffies, data->last_updated_config + (HZ * 300)) || !data->valid) { - for (i = 0; i < 6; i++) - { + for (i = 0; i < 6; i++) { data->in_min[i] = i2c_smbus_read_byte_data(client, ADM9240_REG_IN_MIN(i)); data->in_max[i] = i2c_smbus_read_byte_data(client, ADM9240_REG_IN_MAX(i)); } - for (i = 0; i < 2; i++) - { + for (i = 0; i < 2; i++) { data->fan_min[i] = i2c_smbus_read_byte_data(client, ADM9240_REG_FAN_MIN(i)); } @@ -769,21 +793,9 @@ static struct adm9240_data *adm9240_update_device(struct device *dev) return data; } -static int __init sensors_adm9240_init(void) -{ - return i2c_add_driver(&adm9240_driver); -} - -static void __exit sensors_adm9240_exit(void) -{ - i2c_del_driver(&adm9240_driver); -} +module_i2c_driver(adm9240_driver); MODULE_AUTHOR("Michiel Rook <michiel@grendelproject.nl>, " "Grant Coady <gcoady.lk@gmail.com> and others"); MODULE_DESCRIPTION("ADM9240/DS1780/LM81 driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_adm9240_init); -module_exit(sensors_adm9240_exit); - diff --git a/drivers/hwmon/ads1015.c b/drivers/hwmon/ads1015.c new file mode 100644 index 00000000000..7f9dc2f86b6 --- /dev/null +++ b/drivers/hwmon/ads1015.c @@ -0,0 +1,322 @@ +/* + * ads1015.c - lm_sensors driver for ads1015 12-bit 4-input ADC + * (C) Copyright 2010 + * Dirk Eibach, Guntermann & Drunck GmbH <eibach@gdsys.de> + * + * Based on the ads7828 driver by Steve Hardy. + * + * Datasheet available at: http://focus.ti.com/lit/ds/symlink/ads1015.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/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/of.h> + +#include <linux/i2c/ads1015.h> + +/* ADS1015 registers */ +enum { + ADS1015_CONVERSION = 0, + ADS1015_CONFIG = 1, +}; + +/* PGA fullscale voltages in mV */ +static const unsigned int fullscale_table[8] = { + 6144, 4096, 2048, 1024, 512, 256, 256, 256 }; + +/* Data rates in samples per second */ +static const unsigned int data_rate_table_1015[8] = { + 128, 250, 490, 920, 1600, 2400, 3300, 3300 +}; + +static const unsigned int data_rate_table_1115[8] = { + 8, 16, 32, 64, 128, 250, 475, 860 +}; + +#define ADS1015_DEFAULT_CHANNELS 0xff +#define ADS1015_DEFAULT_PGA 2 +#define ADS1015_DEFAULT_DATA_RATE 4 + +enum ads1015_chips { + ads1015, + ads1115, +}; + +struct ads1015_data { + struct device *hwmon_dev; + struct mutex update_lock; /* mutex protect updates */ + struct ads1015_channel_data channel_data[ADS1015_CHANNELS]; + enum ads1015_chips id; +}; + +static int ads1015_read_adc(struct i2c_client *client, unsigned int channel) +{ + u16 config; + struct ads1015_data *data = i2c_get_clientdata(client); + unsigned int pga = data->channel_data[channel].pga; + unsigned int data_rate = data->channel_data[channel].data_rate; + unsigned int conversion_time_ms; + const unsigned int * const rate_table = data->id == ads1115 ? + data_rate_table_1115 : data_rate_table_1015; + int res; + + mutex_lock(&data->update_lock); + + /* get channel parameters */ + res = i2c_smbus_read_word_swapped(client, ADS1015_CONFIG); + if (res < 0) + goto err_unlock; + config = res; + conversion_time_ms = DIV_ROUND_UP(1000, rate_table[data_rate]); + + /* setup and start single conversion */ + config &= 0x001f; + config |= (1 << 15) | (1 << 8); + config |= (channel & 0x0007) << 12; + config |= (pga & 0x0007) << 9; + config |= (data_rate & 0x0007) << 5; + + res = i2c_smbus_write_word_swapped(client, ADS1015_CONFIG, config); + if (res < 0) + goto err_unlock; + + /* wait until conversion finished */ + msleep(conversion_time_ms); + res = i2c_smbus_read_word_swapped(client, ADS1015_CONFIG); + if (res < 0) + goto err_unlock; + config = res; + if (!(config & (1 << 15))) { + /* conversion not finished in time */ + res = -EIO; + goto err_unlock; + } + + res = i2c_smbus_read_word_swapped(client, ADS1015_CONVERSION); + +err_unlock: + mutex_unlock(&data->update_lock); + return res; +} + +static int ads1015_reg_to_mv(struct i2c_client *client, unsigned int channel, + s16 reg) +{ + struct ads1015_data *data = i2c_get_clientdata(client); + unsigned int pga = data->channel_data[channel].pga; + int fullscale = fullscale_table[pga]; + const unsigned mask = data->id == ads1115 ? 0x7fff : 0x7ff0; + + return DIV_ROUND_CLOSEST(reg * fullscale, mask); +} + +/* sysfs callback function */ +static ssize_t show_in(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); + int res; + int index = attr->index; + + res = ads1015_read_adc(client, index); + if (res < 0) + return res; + + return sprintf(buf, "%d\n", ads1015_reg_to_mv(client, index, res)); +} + +static const struct sensor_device_attribute ads1015_in[] = { + SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0), + SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1), + SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2), + SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3), + SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4), + SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5), + SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6), + SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7), +}; + +/* + * Driver interface + */ + +static int ads1015_remove(struct i2c_client *client) +{ + struct ads1015_data *data = i2c_get_clientdata(client); + int k; + + hwmon_device_unregister(data->hwmon_dev); + for (k = 0; k < ADS1015_CHANNELS; ++k) + device_remove_file(&client->dev, &ads1015_in[k].dev_attr); + return 0; +} + +#ifdef CONFIG_OF +static int ads1015_get_channels_config_of(struct i2c_client *client) +{ + struct ads1015_data *data = i2c_get_clientdata(client); + struct device_node *node; + + if (!client->dev.of_node + || !of_get_next_child(client->dev.of_node, NULL)) + return -EINVAL; + + for_each_child_of_node(client->dev.of_node, node) { + const __be32 *property; + int len; + unsigned int channel; + unsigned int pga = ADS1015_DEFAULT_PGA; + unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE; + + property = of_get_property(node, "reg", &len); + if (!property || len != sizeof(int)) { + dev_err(&client->dev, "invalid reg on %s\n", + node->full_name); + continue; + } + + channel = be32_to_cpup(property); + if (channel > ADS1015_CHANNELS) { + dev_err(&client->dev, + "invalid channel index %d on %s\n", + channel, node->full_name); + continue; + } + + property = of_get_property(node, "ti,gain", &len); + if (property && len == sizeof(int)) { + pga = be32_to_cpup(property); + if (pga > 6) { + dev_err(&client->dev, + "invalid gain on %s\n", + node->full_name); + } + } + + property = of_get_property(node, "ti,datarate", &len); + if (property && len == sizeof(int)) { + data_rate = be32_to_cpup(property); + if (data_rate > 7) { + dev_err(&client->dev, + "invalid data_rate on %s\n", + node->full_name); + } + } + + data->channel_data[channel].enabled = true; + data->channel_data[channel].pga = pga; + data->channel_data[channel].data_rate = data_rate; + } + + return 0; +} +#endif + +static void ads1015_get_channels_config(struct i2c_client *client) +{ + unsigned int k; + struct ads1015_data *data = i2c_get_clientdata(client); + struct ads1015_platform_data *pdata = dev_get_platdata(&client->dev); + + /* prefer platform data */ + if (pdata) { + memcpy(data->channel_data, pdata->channel_data, + sizeof(data->channel_data)); + return; + } + +#ifdef CONFIG_OF + if (!ads1015_get_channels_config_of(client)) + return; +#endif + + /* fallback on default configuration */ + for (k = 0; k < ADS1015_CHANNELS; ++k) { + data->channel_data[k].enabled = true; + data->channel_data[k].pga = ADS1015_DEFAULT_PGA; + data->channel_data[k].data_rate = ADS1015_DEFAULT_DATA_RATE; + } +} + +static int ads1015_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ads1015_data *data; + int err; + unsigned int k; + + data = devm_kzalloc(&client->dev, sizeof(struct ads1015_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + data->id = id->driver_data; + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* build sysfs attribute group */ + ads1015_get_channels_config(client); + for (k = 0; k < ADS1015_CHANNELS; ++k) { + if (!data->channel_data[k].enabled) + continue; + err = device_create_file(&client->dev, &ads1015_in[k].dev_attr); + if (err) + goto exit_remove; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + return 0; + +exit_remove: + for (k = 0; k < ADS1015_CHANNELS; ++k) + device_remove_file(&client->dev, &ads1015_in[k].dev_attr); + return err; +} + +static const struct i2c_device_id ads1015_id[] = { + { "ads1015", ads1015}, + { "ads1115", ads1115}, + { } +}; +MODULE_DEVICE_TABLE(i2c, ads1015_id); + +static struct i2c_driver ads1015_driver = { + .driver = { + .name = "ads1015", + }, + .probe = ads1015_probe, + .remove = ads1015_remove, + .id_table = ads1015_id, +}; + +module_i2c_driver(ads1015_driver); + +MODULE_AUTHOR("Dirk Eibach <eibach@gdsys.de>"); +MODULE_DESCRIPTION("ADS1015 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c index aac85f3aed5..7092c78f814 100644 --- a/drivers/hwmon/ads7828.c +++ b/drivers/hwmon/ads7828.c @@ -1,92 +1,72 @@ /* - ads7828.c - lm_sensors driver for ads7828 12-bit 8-channel ADC - (C) 2007 EADS Astrium + * ads7828.c - driver for TI ADS7828 8-channel A/D converter and compatibles + * (C) 2007 EADS Astrium + * + * This driver is based on the lm75 and other lm_sensors/hwmon drivers + * + * Written by Steve Hardy <shardy@redhat.com> + * + * ADS7830 support, by Guillaume Roguez <guillaume.roguez@savoirfairelinux.com> + * + * For further information, see the Documentation/hwmon/ads7828 file. + * + * 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. + */ - This driver is based on the lm75 and other lm_sensors/hwmon drivers - - Written by Steve Hardy <steve@linuxrealtime.co.uk> - - Datasheet available at: http://focus.ti.com/lit/ds/symlink/ads7828.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/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/jiffies.h> -#include <linux/i2c.h> +#include <linux/err.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> -#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/jiffies.h> +#include <linux/module.h> #include <linux/mutex.h> +#include <linux/platform_data/ads7828.h> +#include <linux/slab.h> /* The ADS7828 registers */ -#define ADS7828_NCH 8 /* 8 channels of 12-bit A-D supported */ -#define ADS7828_CMD_SD_SE 0x80 /* Single ended inputs */ -#define ADS7828_CMD_SD_DIFF 0x00 /* Differential inputs */ -#define ADS7828_CMD_PD0 0x0 /* Power Down between A-D conversions */ -#define ADS7828_CMD_PD1 0x04 /* Internal ref OFF && A-D ON */ -#define ADS7828_CMD_PD2 0x08 /* Internal ref ON && A-D OFF */ -#define ADS7828_CMD_PD3 0x0C /* Internal ref ON && A-D ON */ -#define ADS7828_INT_VREF_MV 2500 /* Internal vref is 2.5V, 2500mV */ - -/* Addresses to scan */ -static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, - I2C_CLIENT_END }; - -/* Module parameters */ -static int se_input = 1; /* Default is SE, 0 == diff */ -static int int_vref = 1; /* Default is internal ref ON */ -static int vref_mv = ADS7828_INT_VREF_MV; /* set if vref != 2.5V */ -module_param(se_input, bool, S_IRUGO); -module_param(int_vref, bool, S_IRUGO); -module_param(vref_mv, int, S_IRUGO); - -/* Global Variables */ -static u8 ads7828_cmd_byte; /* cmd byte without channel bits */ -static unsigned int ads7828_lsb_resol; /* resolution of the ADC sample lsb */ - -/* Each client has this additional data */ +#define ADS7828_NCH 8 /* 8 channels supported */ +#define ADS7828_CMD_SD_SE 0x80 /* Single ended inputs */ +#define ADS7828_CMD_PD1 0x04 /* Internal vref OFF && A/D ON */ +#define ADS7828_CMD_PD3 0x0C /* Internal vref ON && A/D ON */ +#define ADS7828_INT_VREF_MV 2500 /* Internal vref is 2.5V, 2500mV */ +#define ADS7828_EXT_VREF_MV_MIN 50 /* External vref min value 0.05V */ +#define ADS7828_EXT_VREF_MV_MAX 5250 /* External vref max value 5.25V */ + +/* List of supported devices */ +enum ads7828_chips { ads7828, ads7830 }; + +/* Client specific data */ struct ads7828_data { struct device *hwmon_dev; - struct mutex update_lock; /* mutex protect updates */ - char valid; /* !=0 if following fields are valid */ - unsigned long last_updated; /* In jiffies */ - u16 adc_input[ADS7828_NCH]; /* ADS7828_NCH 12-bit samples */ + struct mutex update_lock; /* Mutex protecting updates */ + unsigned long last_updated; /* Last updated time (in jiffies) */ + u16 adc_input[ADS7828_NCH]; /* ADS7828_NCH samples */ + bool valid; /* Validity flag */ + bool diff_input; /* Differential input */ + bool ext_vref; /* External voltage reference */ + unsigned int vref_mv; /* voltage reference value */ + u8 cmd_byte; /* Command byte without channel bits */ + unsigned int lsb_resol; /* Resolution of the ADC sample LSB */ + s32 (*read_channel)(const struct i2c_client *client, u8 command); }; -/* Function declaration - necessary due to function dependencies */ -static int ads7828_detect(struct i2c_client *client, - struct i2c_board_info *info); -static int ads7828_probe(struct i2c_client *client, - const struct i2c_device_id *id); - -/* The ADS7828 returns the 12-bit sample in two bytes, - these are read as a word then byte-swapped */ -static u16 ads7828_read_value(struct i2c_client *client, u8 reg) +/* Command byte C2,C1,C0 - see datasheet */ +static inline u8 ads7828_cmd_byte(u8 cmd, int ch) { - return swab16(i2c_smbus_read_word_data(client, reg)); -} - -static inline u8 channel_cmd_byte(int ch) -{ - /* cmd byte C2,C1,C0 - see datasheet */ - u8 cmd = (((ch>>1) | (ch&0x01)<<2)<<4); - cmd |= ads7828_cmd_byte; - return cmd; + return cmd | (((ch >> 1) | (ch & 0x01) << 2) << 4); } /* Update data for the device (all 8 channels) */ @@ -103,11 +83,11 @@ static struct ads7828_data *ads7828_update_device(struct device *dev) dev_dbg(&client->dev, "Starting ads7828 update\n"); for (ch = 0; ch < ADS7828_NCH; ch++) { - u8 cmd = channel_cmd_byte(ch); - data->adc_input[ch] = ads7828_read_value(client, cmd); + u8 cmd = ads7828_cmd_byte(data->cmd_byte, ch); + data->adc_input[ch] = data->read_channel(client, cmd); } data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } mutex_unlock(&data->update_lock); @@ -116,28 +96,25 @@ static struct ads7828_data *ads7828_update_device(struct device *dev) } /* sysfs callback function */ -static ssize_t show_in(struct device *dev, struct device_attribute *da, - char *buf) +static ssize_t ads7828_show_in(struct device *dev, struct device_attribute *da, + char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct ads7828_data *data = ads7828_update_device(dev); - /* Print value (in mV as specified in sysfs-interface documentation) */ - return sprintf(buf, "%d\n", (data->adc_input[attr->index] * - ads7828_lsb_resol)/1000); -} + unsigned int value = DIV_ROUND_CLOSEST(data->adc_input[attr->index] * + data->lsb_resol, 1000); -#define in_reg(offset)\ -static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in,\ - NULL, offset) + return sprintf(buf, "%d\n", value); +} -in_reg(0); -in_reg(1); -in_reg(2); -in_reg(3); -in_reg(4); -in_reg(5); -in_reg(6); -in_reg(7); +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ads7828_show_in, NULL, 0); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ads7828_show_in, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ads7828_show_in, NULL, 2); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ads7828_show_in, NULL, 3); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ads7828_show_in, NULL, 4); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, ads7828_show_in, NULL, 5); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, ads7828_show_in, NULL, 6); +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, ads7828_show_in, NULL, 7); static struct attribute *ads7828_attributes[] = { &sensor_dev_attr_in0_input.dev_attr.attr, @@ -158,60 +135,9 @@ static const struct attribute_group ads7828_group = { static int ads7828_remove(struct i2c_client *client) { struct ads7828_data *data = i2c_get_clientdata(client); + hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &ads7828_group); - kfree(i2c_get_clientdata(client)); - return 0; -} - -static const struct i2c_device_id ads7828_id[] = { - { "ads7828", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ads7828_id); - -/* This is the driver that will be inserted */ -static struct i2c_driver ads7828_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "ads7828", - }, - .probe = ads7828_probe, - .remove = ads7828_remove, - .id_table = ads7828_id, - .detect = ads7828_detect, - .address_list = normal_i2c, -}; - -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int ads7828_detect(struct i2c_client *client, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = client->adapter; - int ch; - - /* Check we have a valid client */ - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) - return -ENODEV; - - /* Now, we do the remaining detection. There is no identification - dedicated register so attempt to sanity check using knowledge of - the chip - - Read from the 8 channel addresses - - Check the top 4 bits of each result are not set (12 data bits) - */ - for (ch = 0; ch < ADS7828_NCH; ch++) { - u16 in_data; - u8 cmd = channel_cmd_byte(ch); - in_data = ads7828_read_value(client, cmd); - if (in_data & 0xF000) { - pr_debug("%s : Doesn't look like an ads7828 device\n", - __func__); - return -ENODEV; - } - } - - strlcpy(info->type, "ads7828", I2C_NAME_SIZE); return 0; } @@ -219,61 +145,82 @@ static int ads7828_detect(struct i2c_client *client, static int ads7828_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct ads7828_platform_data *pdata = dev_get_platdata(&client->dev); struct ads7828_data *data; int err; - data = kzalloc(sizeof(struct ads7828_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; + data = devm_kzalloc(&client->dev, sizeof(struct ads7828_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (pdata) { + data->diff_input = pdata->diff_input; + data->ext_vref = pdata->ext_vref; + if (data->ext_vref) + data->vref_mv = pdata->vref_mv; + } + + /* Bound Vref with min/max values if it was provided */ + if (data->vref_mv) + data->vref_mv = clamp_val(data->vref_mv, + ADS7828_EXT_VREF_MV_MIN, + ADS7828_EXT_VREF_MV_MAX); + else + data->vref_mv = ADS7828_INT_VREF_MV; + + /* ADS7828 uses 12-bit samples, while ADS7830 is 8-bit */ + if (id->driver_data == ads7828) { + data->lsb_resol = DIV_ROUND_CLOSEST(data->vref_mv * 1000, 4096); + data->read_channel = i2c_smbus_read_word_swapped; + } else { + data->lsb_resol = DIV_ROUND_CLOSEST(data->vref_mv * 1000, 256); + data->read_channel = i2c_smbus_read_byte_data; } + data->cmd_byte = data->ext_vref ? ADS7828_CMD_PD1 : ADS7828_CMD_PD3; + if (!data->diff_input) + data->cmd_byte |= ADS7828_CMD_SD_SE; + i2c_set_clientdata(client, data); mutex_init(&data->update_lock); - /* Register sysfs hooks */ err = sysfs_create_group(&client->dev.kobj, &ads7828_group); if (err) - goto exit_free; + return err; data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); - goto exit_remove; + goto error; } return 0; -exit_remove: +error: sysfs_remove_group(&client->dev.kobj, &ads7828_group); -exit_free: - kfree(data); -exit: return err; } -static int __init sensors_ads7828_init(void) -{ - /* Initialize the command byte according to module parameters */ - ads7828_cmd_byte = se_input ? - ADS7828_CMD_SD_SE : ADS7828_CMD_SD_DIFF; - ads7828_cmd_byte |= int_vref ? - ADS7828_CMD_PD3 : ADS7828_CMD_PD1; +static const struct i2c_device_id ads7828_device_ids[] = { + { "ads7828", ads7828 }, + { "ads7830", ads7830 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ads7828_device_ids); - /* Calculate the LSB resolution */ - ads7828_lsb_resol = (vref_mv*1000)/4096; +static struct i2c_driver ads7828_driver = { + .driver = { + .name = "ads7828", + }, - return i2c_add_driver(&ads7828_driver); -} + .id_table = ads7828_device_ids, + .probe = ads7828_probe, + .remove = ads7828_remove, +}; -static void __exit sensors_ads7828_exit(void) -{ - i2c_del_driver(&ads7828_driver); -} +module_i2c_driver(ads7828_driver); -MODULE_AUTHOR("Steve Hardy <steve@linuxrealtime.co.uk>"); -MODULE_DESCRIPTION("ADS7828 driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_ads7828_init); -module_exit(sensors_ads7828_exit); +MODULE_AUTHOR("Steve Hardy <shardy@redhat.com>"); +MODULE_DESCRIPTION("Driver for TI ADS7828 A/D converter and compatibles"); diff --git a/drivers/hwmon/ads7871.c b/drivers/hwmon/ads7871.c new file mode 100644 index 00000000000..3eff73b6220 --- /dev/null +++ b/drivers/hwmon/ads7871.c @@ -0,0 +1,251 @@ +/* + * ads7871 - driver for TI ADS7871 A/D converter + * + * Copyright (c) 2010 Paul Thomas <pthomas8589@gmail.com> + * + * 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. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * later as publishhed by the Free Software Foundation. + * + * You need to have something like this in struct spi_board_info + * { + * .modalias = "ads7871", + * .max_speed_hz = 2*1000*1000, + * .chip_select = 0, + * .bus_num = 1, + * }, + */ + +/*From figure 18 in the datasheet*/ +/*Register addresses*/ +#define REG_LS_BYTE 0 /*A/D Output Data, LS Byte*/ +#define REG_MS_BYTE 1 /*A/D Output Data, MS Byte*/ +#define REG_PGA_VALID 2 /*PGA Valid Register*/ +#define REG_AD_CONTROL 3 /*A/D Control Register*/ +#define REG_GAIN_MUX 4 /*Gain/Mux Register*/ +#define REG_IO_STATE 5 /*Digital I/O State Register*/ +#define REG_IO_CONTROL 6 /*Digital I/O Control Register*/ +#define REG_OSC_CONTROL 7 /*Rev/Oscillator Control Register*/ +#define REG_SER_CONTROL 24 /*Serial Interface Control Register*/ +#define REG_ID 31 /*ID Register*/ + +/* + * From figure 17 in the datasheet + * These bits get ORed with the address to form + * the instruction byte + */ +/*Instruction Bit masks*/ +#define INST_MODE_BM (1 << 7) +#define INST_READ_BM (1 << 6) +#define INST_16BIT_BM (1 << 5) + +/*From figure 18 in the datasheet*/ +/*bit masks for Rev/Oscillator Control Register*/ +#define MUX_CNV_BV 7 +#define MUX_CNV_BM (1 << MUX_CNV_BV) +#define MUX_M3_BM (1 << 3) /*M3 selects single ended*/ +#define MUX_G_BV 4 /*allows for reg = (gain << MUX_G_BV) | ...*/ + +/*From figure 18 in the datasheet*/ +/*bit masks for Rev/Oscillator Control Register*/ +#define OSC_OSCR_BM (1 << 5) +#define OSC_OSCE_BM (1 << 4) +#define OSC_REFE_BM (1 << 3) +#define OSC_BUFE_BM (1 << 2) +#define OSC_R2V_BM (1 << 1) +#define OSC_RBG_BM (1 << 0) + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/spi/spi.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/delay.h> + +#define DEVICE_NAME "ads7871" + +struct ads7871_data { + struct device *hwmon_dev; + struct mutex update_lock; +}; + +static int ads7871_read_reg8(struct spi_device *spi, int reg) +{ + int ret; + reg = reg | INST_READ_BM; + ret = spi_w8r8(spi, reg); + return ret; +} + +static int ads7871_read_reg16(struct spi_device *spi, int reg) +{ + int ret; + reg = reg | INST_READ_BM | INST_16BIT_BM; + ret = spi_w8r16(spi, reg); + return ret; +} + +static int ads7871_write_reg8(struct spi_device *spi, int reg, u8 val) +{ + u8 tmp[2] = {reg, val}; + return spi_write(spi, tmp, sizeof(tmp)); +} + +static ssize_t show_voltage(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int ret, val, i = 0; + uint8_t channel, mux_cnv; + + channel = attr->index; + /* + * TODO: add support for conversions + * other than single ended with a gain of 1 + */ + /*MUX_M3_BM forces single ended*/ + /*This is also where the gain of the PGA would be set*/ + ads7871_write_reg8(spi, REG_GAIN_MUX, + (MUX_CNV_BM | MUX_M3_BM | channel)); + + ret = ads7871_read_reg8(spi, REG_GAIN_MUX); + mux_cnv = ((ret & MUX_CNV_BM) >> MUX_CNV_BV); + /* + * on 400MHz arm9 platform the conversion + * is already done when we do this test + */ + while ((i < 2) && mux_cnv) { + i++; + ret = ads7871_read_reg8(spi, REG_GAIN_MUX); + mux_cnv = ((ret & MUX_CNV_BM) >> MUX_CNV_BV); + msleep_interruptible(1); + } + + if (mux_cnv == 0) { + val = ads7871_read_reg16(spi, REG_LS_BYTE); + /*result in volts*10000 = (val/8192)*2.5*10000*/ + val = ((val >> 2) * 25000) / 8192; + return sprintf(buf, "%d\n", val); + } else { + return -1; + } +} + +static ssize_t ads7871_show_name(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + return sprintf(buf, "%s\n", to_spi_device(dev)->modalias); +} + +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_voltage, NULL, 0); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_voltage, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_voltage, NULL, 2); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_voltage, NULL, 3); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_voltage, NULL, 4); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_voltage, NULL, 5); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_voltage, NULL, 6); +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_voltage, NULL, 7); + +static DEVICE_ATTR(name, S_IRUGO, ads7871_show_name, NULL); + +static struct attribute *ads7871_attributes[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + &dev_attr_name.attr, + NULL +}; + +static const struct attribute_group ads7871_group = { + .attrs = ads7871_attributes, +}; + +static int ads7871_probe(struct spi_device *spi) +{ + int ret, err; + uint8_t val; + struct ads7871_data *pdata; + + dev_dbg(&spi->dev, "probe\n"); + + /* Configure the SPI bus */ + spi->mode = (SPI_MODE_0); + spi->bits_per_word = 8; + spi_setup(spi); + + ads7871_write_reg8(spi, REG_SER_CONTROL, 0); + ads7871_write_reg8(spi, REG_AD_CONTROL, 0); + + val = (OSC_OSCR_BM | OSC_OSCE_BM | OSC_REFE_BM | OSC_BUFE_BM); + ads7871_write_reg8(spi, REG_OSC_CONTROL, val); + ret = ads7871_read_reg8(spi, REG_OSC_CONTROL); + + dev_dbg(&spi->dev, "REG_OSC_CONTROL write:%x, read:%x\n", val, ret); + /* + * because there is no other error checking on an SPI bus + * we need to make sure we really have a chip + */ + if (val != ret) + return -ENODEV; + + pdata = devm_kzalloc(&spi->dev, sizeof(struct ads7871_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + err = sysfs_create_group(&spi->dev.kobj, &ads7871_group); + if (err < 0) + return err; + + spi_set_drvdata(spi, pdata); + + pdata->hwmon_dev = hwmon_device_register(&spi->dev); + if (IS_ERR(pdata->hwmon_dev)) { + err = PTR_ERR(pdata->hwmon_dev); + goto error_remove; + } + + return 0; + +error_remove: + sysfs_remove_group(&spi->dev.kobj, &ads7871_group); + return err; +} + +static int ads7871_remove(struct spi_device *spi) +{ + struct ads7871_data *pdata = spi_get_drvdata(spi); + + hwmon_device_unregister(pdata->hwmon_dev); + sysfs_remove_group(&spi->dev.kobj, &ads7871_group); + return 0; +} + +static struct spi_driver ads7871_driver = { + .driver = { + .name = DEVICE_NAME, + .owner = THIS_MODULE, + }, + + .probe = ads7871_probe, + .remove = ads7871_remove, +}; + +module_spi_driver(ads7871_driver); + +MODULE_AUTHOR("Paul Thomas <pthomas8589@gmail.com>"); +MODULE_DESCRIPTION("TI ADS7871 A/D driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c new file mode 100644 index 00000000000..5994cf68e0a --- /dev/null +++ b/drivers/hwmon/adt7310.c @@ -0,0 +1,118 @@ +/* + * ADT7310/ADT7310 digital temperature sensor driver + * + * Copyright 2012-2013 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/spi/spi.h> +#include <asm/unaligned.h> + +#include "adt7x10.h" + +#define ADT7310_STATUS 0 +#define ADT7310_CONFIG 1 +#define ADT7310_TEMPERATURE 2 +#define ADT7310_ID 3 +#define ADT7310_T_CRIT 4 +#define ADT7310_T_HYST 5 +#define ADT7310_T_ALARM_HIGH 6 +#define ADT7310_T_ALARM_LOW 7 + +static const u8 adt7310_reg_table[] = { + [ADT7X10_TEMPERATURE] = ADT7310_TEMPERATURE, + [ADT7X10_STATUS] = ADT7310_STATUS, + [ADT7X10_CONFIG] = ADT7310_CONFIG, + [ADT7X10_T_ALARM_HIGH] = ADT7310_T_ALARM_HIGH, + [ADT7X10_T_ALARM_LOW] = ADT7310_T_ALARM_LOW, + [ADT7X10_T_CRIT] = ADT7310_T_CRIT, + [ADT7X10_T_HYST] = ADT7310_T_HYST, + [ADT7X10_ID] = ADT7310_ID, +}; + +#define ADT7310_CMD_REG_OFFSET 3 +#define ADT7310_CMD_READ 0x40 + +#define AD7310_COMMAND(reg) (adt7310_reg_table[(reg)] << ADT7310_CMD_REG_OFFSET) + +static int adt7310_spi_read_word(struct device *dev, u8 reg) +{ + struct spi_device *spi = to_spi_device(dev); + + return spi_w8r16be(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ); +} + +static int adt7310_spi_write_word(struct device *dev, u8 reg, u16 data) +{ + struct spi_device *spi = to_spi_device(dev); + u8 buf[3]; + + buf[0] = AD7310_COMMAND(reg); + put_unaligned_be16(data, &buf[1]); + + return spi_write(spi, buf, sizeof(buf)); +} + +static int adt7310_spi_read_byte(struct device *dev, u8 reg) +{ + struct spi_device *spi = to_spi_device(dev); + + return spi_w8r8(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ); +} + +static int adt7310_spi_write_byte(struct device *dev, u8 reg, + u8 data) +{ + struct spi_device *spi = to_spi_device(dev); + u8 buf[2]; + + buf[0] = AD7310_COMMAND(reg); + buf[1] = data; + + return spi_write(spi, buf, sizeof(buf)); +} + +static const struct adt7x10_ops adt7310_spi_ops = { + .read_word = adt7310_spi_read_word, + .write_word = adt7310_spi_write_word, + .read_byte = adt7310_spi_read_byte, + .write_byte = adt7310_spi_write_byte, +}; + +static int adt7310_spi_probe(struct spi_device *spi) +{ + return adt7x10_probe(&spi->dev, spi_get_device_id(spi)->name, spi->irq, + &adt7310_spi_ops); +} + +static int adt7310_spi_remove(struct spi_device *spi) +{ + return adt7x10_remove(&spi->dev, spi->irq); +} + +static const struct spi_device_id adt7310_id[] = { + { "adt7310", 0 }, + { "adt7320", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, adt7310_id); + +static struct spi_driver adt7310_driver = { + .driver = { + .name = "adt7310", + .owner = THIS_MODULE, + .pm = ADT7X10_DEV_PM_OPS, + }, + .probe = adt7310_spi_probe, + .remove = adt7310_spi_remove, + .id_table = adt7310_id, +}; +module_spi_driver(adt7310_driver); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("ADT7310/ADT7320 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c new file mode 100644 index 00000000000..0dc066a939b --- /dev/null +++ b/drivers/hwmon/adt7410.c @@ -0,0 +1,80 @@ +/* + * ADT7410/ADT7420 digital temperature sensor driver + * + * Copyright 2012-2013 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> + +#include "adt7x10.h" + +static int adt7410_i2c_read_word(struct device *dev, u8 reg) +{ + return i2c_smbus_read_word_swapped(to_i2c_client(dev), reg); +} + +static int adt7410_i2c_write_word(struct device *dev, u8 reg, u16 data) +{ + return i2c_smbus_write_word_swapped(to_i2c_client(dev), reg, data); +} + +static int adt7410_i2c_read_byte(struct device *dev, u8 reg) +{ + return i2c_smbus_read_byte_data(to_i2c_client(dev), reg); +} + +static int adt7410_i2c_write_byte(struct device *dev, u8 reg, u8 data) +{ + return i2c_smbus_write_byte_data(to_i2c_client(dev), reg, data); +} + +static const struct adt7x10_ops adt7410_i2c_ops = { + .read_word = adt7410_i2c_read_word, + .write_word = adt7410_i2c_write_word, + .read_byte = adt7410_i2c_read_byte, + .write_byte = adt7410_i2c_write_byte, +}; + +static int adt7410_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + return adt7x10_probe(&client->dev, NULL, client->irq, &adt7410_i2c_ops); +} + +static int adt7410_i2c_remove(struct i2c_client *client) +{ + return adt7x10_remove(&client->dev, client->irq); +} + +static const struct i2c_device_id adt7410_ids[] = { + { "adt7410", 0 }, + { "adt7420", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, adt7410_ids); + +static struct i2c_driver adt7410_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "adt7410", + .pm = ADT7X10_DEV_PM_OPS, + }, + .probe = adt7410_i2c_probe, + .remove = adt7410_i2c_remove, + .id_table = adt7410_ids, + .address_list = I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b), +}; +module_i2c_driver(adt7410_driver); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("ADT7410/AD7420 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c new file mode 100644 index 00000000000..d9299dee37d --- /dev/null +++ b/drivers/hwmon/adt7411.c @@ -0,0 +1,354 @@ +/* + * Driver for the ADT7411 (I2C/SPI 8 channel 10 bit ADC & temperature-sensor) + * + * Copyright (C) 2008, 2010 Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * TODO: SPI, support for external temperature sensor + * use power-down mode for suspend?, interrupt handling? + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/slab.h> + +#define ADT7411_REG_INT_TEMP_VDD_LSB 0x03 +#define ADT7411_REG_EXT_TEMP_AIN14_LSB 0x04 +#define ADT7411_REG_VDD_MSB 0x06 +#define ADT7411_REG_INT_TEMP_MSB 0x07 +#define ADT7411_REG_EXT_TEMP_AIN1_MSB 0x08 + +#define ADT7411_REG_CFG1 0x18 +#define ADT7411_CFG1_START_MONITOR (1 << 0) + +#define ADT7411_REG_CFG2 0x19 +#define ADT7411_CFG2_DISABLE_AVG (1 << 5) + +#define ADT7411_REG_CFG3 0x1a +#define ADT7411_CFG3_ADC_CLK_225 (1 << 0) +#define ADT7411_CFG3_REF_VDD (1 << 4) + +#define ADT7411_REG_DEVICE_ID 0x4d +#define ADT7411_REG_MANUFACTURER_ID 0x4e + +#define ADT7411_DEVICE_ID 0x2 +#define ADT7411_MANUFACTURER_ID 0x41 + +static const unsigned short normal_i2c[] = { 0x48, 0x4a, 0x4b, I2C_CLIENT_END }; + +struct adt7411_data { + struct mutex device_lock; /* for "atomic" device accesses */ + struct mutex update_lock; + unsigned long next_update; + int vref_cached; + struct device *hwmon_dev; +}; + +/* + * When reading a register containing (up to 4) lsb, all associated + * msb-registers get locked by the hardware. After _one_ of those msb is read, + * _all_ are unlocked. In order to use this locking correctly, reading lsb/msb + * is protected here with a mutex, too. + */ +static int adt7411_read_10_bit(struct i2c_client *client, u8 lsb_reg, + u8 msb_reg, u8 lsb_shift) +{ + struct adt7411_data *data = i2c_get_clientdata(client); + int val, tmp; + + mutex_lock(&data->device_lock); + + val = i2c_smbus_read_byte_data(client, lsb_reg); + if (val < 0) + goto exit_unlock; + + tmp = (val >> lsb_shift) & 3; + val = i2c_smbus_read_byte_data(client, msb_reg); + + if (val >= 0) + val = (val << 2) | tmp; + + exit_unlock: + mutex_unlock(&data->device_lock); + + return val; +} + +static int adt7411_modify_bit(struct i2c_client *client, u8 reg, u8 bit, + bool flag) +{ + struct adt7411_data *data = i2c_get_clientdata(client); + int ret, val; + + mutex_lock(&data->device_lock); + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) + goto exit_unlock; + + if (flag) + val = ret | bit; + else + val = ret & ~bit; + + ret = i2c_smbus_write_byte_data(client, reg, val); + + exit_unlock: + mutex_unlock(&data->device_lock); + return ret; +} + +static ssize_t adt7411_show_vdd(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB, + ADT7411_REG_VDD_MSB, 2); + + return ret < 0 ? ret : sprintf(buf, "%u\n", ret * 7000 / 1024); +} + +static ssize_t adt7411_show_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int val = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB, + ADT7411_REG_INT_TEMP_MSB, 0); + + if (val < 0) + return val; + + val = val & 0x200 ? val - 0x400 : val; /* 10 bit signed */ + + return sprintf(buf, "%d\n", val * 250); +} + +static ssize_t adt7411_show_input(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int nr = to_sensor_dev_attr(attr)->index; + struct i2c_client *client = to_i2c_client(dev); + struct adt7411_data *data = i2c_get_clientdata(client); + int val; + u8 lsb_reg, lsb_shift; + + mutex_lock(&data->update_lock); + if (time_after_eq(jiffies, data->next_update)) { + val = i2c_smbus_read_byte_data(client, ADT7411_REG_CFG3); + if (val < 0) + goto exit_unlock; + + if (val & ADT7411_CFG3_REF_VDD) { + val = adt7411_read_10_bit(client, + ADT7411_REG_INT_TEMP_VDD_LSB, + ADT7411_REG_VDD_MSB, 2); + if (val < 0) + goto exit_unlock; + + data->vref_cached = val * 7000 / 1024; + } else { + data->vref_cached = 2250; + } + + data->next_update = jiffies + HZ; + } + + lsb_reg = ADT7411_REG_EXT_TEMP_AIN14_LSB + (nr >> 2); + lsb_shift = 2 * (nr & 0x03); + val = adt7411_read_10_bit(client, lsb_reg, + ADT7411_REG_EXT_TEMP_AIN1_MSB + nr, lsb_shift); + if (val < 0) + goto exit_unlock; + + val = sprintf(buf, "%u\n", val * data->vref_cached / 1024); + exit_unlock: + mutex_unlock(&data->update_lock); + return val; +} + +static ssize_t adt7411_show_bit(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr); + struct i2c_client *client = to_i2c_client(dev); + int ret = i2c_smbus_read_byte_data(client, attr2->index); + + return ret < 0 ? ret : sprintf(buf, "%u\n", !!(ret & attr2->nr)); +} + +static ssize_t adt7411_set_bit(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct sensor_device_attribute_2 *s_attr2 = to_sensor_dev_attr_2(attr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7411_data *data = i2c_get_clientdata(client); + int ret; + unsigned long flag; + + ret = kstrtoul(buf, 0, &flag); + if (ret || flag > 1) + return -EINVAL; + + ret = adt7411_modify_bit(client, s_attr2->index, s_attr2->nr, flag); + + /* force update */ + mutex_lock(&data->update_lock); + data->next_update = jiffies; + mutex_unlock(&data->update_lock); + + return ret < 0 ? ret : count; +} + +#define ADT7411_BIT_ATTR(__name, __reg, __bit) \ + SENSOR_DEVICE_ATTR_2(__name, S_IRUGO | S_IWUSR, adt7411_show_bit, \ + adt7411_set_bit, __bit, __reg) + +static DEVICE_ATTR(temp1_input, S_IRUGO, adt7411_show_temp, NULL); +static DEVICE_ATTR(in0_input, S_IRUGO, adt7411_show_vdd, NULL); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, adt7411_show_input, NULL, 0); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, adt7411_show_input, NULL, 1); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, adt7411_show_input, NULL, 2); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, adt7411_show_input, NULL, 3); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, adt7411_show_input, NULL, 4); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, adt7411_show_input, NULL, 5); +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, adt7411_show_input, NULL, 6); +static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, adt7411_show_input, NULL, 7); +static ADT7411_BIT_ATTR(no_average, ADT7411_REG_CFG2, ADT7411_CFG2_DISABLE_AVG); +static ADT7411_BIT_ATTR(fast_sampling, ADT7411_REG_CFG3, ADT7411_CFG3_ADC_CLK_225); +static ADT7411_BIT_ATTR(adc_ref_vdd, ADT7411_REG_CFG3, ADT7411_CFG3_REF_VDD); + +static struct attribute *adt7411_attrs[] = { + &dev_attr_temp1_input.attr, + &dev_attr_in0_input.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in8_input.dev_attr.attr, + &sensor_dev_attr_no_average.dev_attr.attr, + &sensor_dev_attr_fast_sampling.dev_attr.attr, + &sensor_dev_attr_adc_ref_vdd.dev_attr.attr, + NULL +}; + +static const struct attribute_group adt7411_attr_grp = { + .attrs = adt7411_attrs, +}; + +static int adt7411_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + int val; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + val = i2c_smbus_read_byte_data(client, ADT7411_REG_MANUFACTURER_ID); + if (val < 0 || val != ADT7411_MANUFACTURER_ID) { + dev_dbg(&client->dev, + "Wrong manufacturer ID. Got %d, expected %d\n", + val, ADT7411_MANUFACTURER_ID); + return -ENODEV; + } + + val = i2c_smbus_read_byte_data(client, ADT7411_REG_DEVICE_ID); + if (val < 0 || val != ADT7411_DEVICE_ID) { + dev_dbg(&client->dev, + "Wrong device ID. Got %d, expected %d\n", + val, ADT7411_DEVICE_ID); + return -ENODEV; + } + + strlcpy(info->type, "adt7411", I2C_NAME_SIZE); + + return 0; +} + +static int adt7411_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adt7411_data *data; + int ret; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + mutex_init(&data->device_lock); + mutex_init(&data->update_lock); + + ret = adt7411_modify_bit(client, ADT7411_REG_CFG1, + ADT7411_CFG1_START_MONITOR, 1); + if (ret < 0) + return ret; + + /* force update on first occasion */ + data->next_update = jiffies; + + ret = sysfs_create_group(&client->dev.kobj, &adt7411_attr_grp); + if (ret) + return ret; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + dev_info(&client->dev, "successfully registered\n"); + + return 0; + + exit_remove: + sysfs_remove_group(&client->dev.kobj, &adt7411_attr_grp); + return ret; +} + +static int adt7411_remove(struct i2c_client *client) +{ + struct adt7411_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &adt7411_attr_grp); + return 0; +} + +static const struct i2c_device_id adt7411_id[] = { + { "adt7411", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adt7411_id); + +static struct i2c_driver adt7411_driver = { + .driver = { + .name = "adt7411", + }, + .probe = adt7411_probe, + .remove = adt7411_remove, + .id_table = adt7411_id, + .detect = adt7411_detect, + .address_list = normal_i2c, + .class = I2C_CLASS_HWMON, +}; + +module_i2c_driver(adt7411_driver); + +MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de> and " + "Wolfram Sang <w.sang@pengutronix.de>"); +MODULE_DESCRIPTION("ADT7411 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c index a31e77c776a..562cc3881d3 100644 --- a/drivers/hwmon/adt7462.c +++ b/drivers/hwmon/adt7462.c @@ -2,7 +2,7 @@ * A hwmon driver for the Analog Devices ADT7462 * Copyright (C) 2008 IBM * - * Author: Darrick J. Wong <djwong@us.ibm.com> + * Author: Darrick J. Wong <darrick.wong@oracle.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 @@ -26,8 +26,8 @@ #include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> -#include <linux/delay.h> #include <linux/log2.h> +#include <linux/slab.h> /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x58, 0x5C, I2C_CLIENT_END }; @@ -64,8 +64,8 @@ static const unsigned short normal_i2c[] = { 0x58, 0x5C, I2C_CLIENT_END }; #define ADT7462_REG_PWM_TEMP_MIN_MAX_ADDR 0x5F #define ADT7462_REG_PWM_TEMP_RANGE_BASE_ADDR 0x60 #define ADT7462_REG_PWM_TEMP_RANGE_MAX_ADDR 0x63 -#define ADT7462_PWM_HYST_MASK 0x0F -#define ADT7462_PWM_RANGE_MASK 0xF0 +#define ADT7462_PWM_HYST_MASK 0x0F +#define ADT7462_PWM_RANGE_MASK 0xF0 #define ADT7462_PWM_RANGE_SHIFT 4 #define ADT7462_REG_PWM_CFG_BASE_ADDR 0x21 #define ADT7462_REG_PWM_CFG_MAX_ADDR 0x24 @@ -84,7 +84,7 @@ static const unsigned short normal_i2c[] = { 0x58, 0x5C, I2C_CLIENT_END }; #define ADT7462_PIN15_INPUT 0x20 #define ADT7462_PIN13_INPUT 0x40 #define ADT7462_PIN8_INPUT 0x80 -#define ADT7462_PIN23_MASK 0x03 +#define ADT7462_PIN23_MASK 0x03 #define ADT7462_PIN23_SHIFT 0 #define ADT7462_PIN26_MASK 0x0C /* cfg2 */ #define ADT7462_PIN26_SHIFT 2 @@ -98,7 +98,7 @@ static const unsigned short normal_i2c[] = { 0x58, 0x5C, I2C_CLIENT_END }; #define ADT7462_PIN28_VOLT 0x5 #define ADT7462_REG_ALARM1 0xB8 -#define ADT7462_LT_ALARM 0x02 +#define ADT7462_LT_ALARM 0x02 #define ADT7462_R1T_ALARM 0x04 #define ADT7462_R2T_ALARM 0x08 #define ADT7462_R3T_ALARM 0x10 @@ -134,9 +134,9 @@ static const unsigned short normal_i2c[] = { 0x58, 0x5C, I2C_CLIENT_END }; #define ADT7462_ALARM_FLAG_MASK 0x0F #define ADT7462_TEMP_COUNT 4 -#define ADT7462_TEMP_REG(x) (ADT7462_REG_TEMP_BASE_ADDR + (x * 2)) -#define ADT7462_TEMP_MIN_REG(x) (ADT7462_REG_MIN_TEMP_BASE_ADDR + (x)) -#define ADT7462_TEMP_MAX_REG(x) (ADT7462_REG_MAX_TEMP_BASE_ADDR + (x)) +#define ADT7462_TEMP_REG(x) (ADT7462_REG_TEMP_BASE_ADDR + ((x) * 2)) +#define ADT7462_TEMP_MIN_REG(x) (ADT7462_REG_MIN_TEMP_BASE_ADDR + (x)) +#define ADT7462_TEMP_MAX_REG(x) (ADT7462_REG_MAX_TEMP_BASE_ADDR + (x)) #define TEMP_FRAC_OFFSET 6 #define ADT7462_FAN_COUNT 8 @@ -179,7 +179,7 @@ static const unsigned short normal_i2c[] = { 0x58, 0x5C, I2C_CLIENT_END }; * * Some, but not all, of these voltages have low/high limits. */ -#define ADT7462_VOLT_COUNT 12 +#define ADT7462_VOLT_COUNT 13 #define ADT7462_VENDOR 0x41 #define ADT7462_DEVICE 0x62 @@ -333,7 +333,7 @@ static int ADT7462_REG_VOLT_MAX(struct adt7462_data *data, int which) return 0x4C; break; } - return -ENODEV; + return 0; } static int ADT7462_REG_VOLT_MIN(struct adt7462_data *data, int which) @@ -392,7 +392,7 @@ static int ADT7462_REG_VOLT_MIN(struct adt7462_data *data, int which) return 0x77; break; } - return -ENODEV; + return 0; } static int ADT7462_REG_VOLT(struct adt7462_data *data, int which) @@ -700,7 +700,7 @@ static int find_trange_value(int trange) if (trange_values[i] == trange) return i; - return -ENODEV; + return -EINVAL; } static struct adt7462_data *adt7462_update_device(struct device *dev) @@ -832,11 +832,11 @@ static ssize_t set_temp_min(struct device *dev, struct adt7462_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp) || !temp_enabled(data, attr->index)) + if (kstrtol(buf, 10, &temp) || !temp_enabled(data, attr->index)) return -EINVAL; temp = DIV_ROUND_CLOSEST(temp, 1000) + 64; - temp = SENSORS_LIMIT(temp, 0, 255); + temp = clamp_val(temp, 0, 255); mutex_lock(&data->lock); data->temp_min[attr->index] = temp; @@ -870,11 +870,11 @@ static ssize_t set_temp_max(struct device *dev, struct adt7462_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp) || !temp_enabled(data, attr->index)) + if (kstrtol(buf, 10, &temp) || !temp_enabled(data, attr->index)) return -EINVAL; temp = DIV_ROUND_CLOSEST(temp, 1000) + 64; - temp = SENSORS_LIMIT(temp, 0, 255); + temp = clamp_val(temp, 0, 255); mutex_lock(&data->lock); data->temp_max[attr->index] = temp; @@ -934,12 +934,12 @@ static ssize_t set_volt_max(struct device *dev, int x = voltage_multiplier(data, attr->index); long temp; - if (strict_strtol(buf, 10, &temp) || !x) + if (kstrtol(buf, 10, &temp) || !x) return -EINVAL; temp *= 1000; /* convert mV to uV */ temp = DIV_ROUND_CLOSEST(temp, x); - temp = SENSORS_LIMIT(temp, 0, 255); + temp = clamp_val(temp, 0, 255); mutex_lock(&data->lock); data->volt_max[attr->index] = temp; @@ -976,12 +976,12 @@ static ssize_t set_volt_min(struct device *dev, int x = voltage_multiplier(data, attr->index); long temp; - if (strict_strtol(buf, 10, &temp) || !x) + if (kstrtol(buf, 10, &temp) || !x) return -EINVAL; temp *= 1000; /* convert mV to uV */ temp = DIV_ROUND_CLOSEST(temp, x); - temp = SENSORS_LIMIT(temp, 0, 255); + temp = clamp_val(temp, 0, 255); mutex_lock(&data->lock); data->volt_min[attr->index] = temp; @@ -1065,13 +1065,13 @@ static ssize_t set_fan_min(struct device *dev, struct adt7462_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp) || !temp || + if (kstrtol(buf, 10, &temp) || !temp || !fan_enabled(data, attr->index)) return -EINVAL; temp = FAN_RPM_TO_PERIOD(temp); temp >>= 8; - temp = SENSORS_LIMIT(temp, 1, 255); + temp = clamp_val(temp, 1, 255); mutex_lock(&data->lock); data->fan_min[attr->index] = temp; @@ -1114,7 +1114,7 @@ static ssize_t set_force_pwm_max(struct device *dev, long temp; u8 reg; - if (strict_strtol(buf, 10, &temp)) + if (kstrtol(buf, 10, &temp)) return -EINVAL; mutex_lock(&data->lock); @@ -1146,10 +1146,10 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, struct adt7462_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp)) + if (kstrtol(buf, 10, &temp)) return -EINVAL; - temp = SENSORS_LIMIT(temp, 0, 255); + temp = clamp_val(temp, 0, 255); mutex_lock(&data->lock); data->pwm[attr->index] = temp; @@ -1176,10 +1176,10 @@ static ssize_t set_pwm_max(struct device *dev, struct adt7462_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp)) + if (kstrtol(buf, 10, &temp)) return -EINVAL; - temp = SENSORS_LIMIT(temp, 0, 255); + temp = clamp_val(temp, 0, 255); mutex_lock(&data->lock); data->pwm_max = temp; @@ -1208,10 +1208,10 @@ static ssize_t set_pwm_min(struct device *dev, struct adt7462_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp)) + if (kstrtol(buf, 10, &temp)) return -EINVAL; - temp = SENSORS_LIMIT(temp, 0, 255); + temp = clamp_val(temp, 0, 255); mutex_lock(&data->lock); data->pwm_min[attr->index] = temp; @@ -1242,11 +1242,11 @@ static ssize_t set_pwm_hyst(struct device *dev, struct adt7462_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp)) + if (kstrtol(buf, 10, &temp)) return -EINVAL; temp = DIV_ROUND_CLOSEST(temp, 1000); - temp = SENSORS_LIMIT(temp, 0, 15); + temp = clamp_val(temp, 0, 15); /* package things up */ temp &= ADT7462_PWM_HYST_MASK; @@ -1288,15 +1288,14 @@ static ssize_t set_pwm_tmax(struct device *dev, int tmin, trange_value; long trange; - if (strict_strtol(buf, 10, &trange)) + if (kstrtol(buf, 10, &trange)) return -EINVAL; /* trange = tmax - tmin */ tmin = (data->pwm_tmin[attr->index] - 64) * 1000; trange_value = find_trange_value(trange - tmin); - if (trange_value < 0) - return -EINVAL; + return trange_value; temp = trange_value << ADT7462_PWM_RANGE_SHIFT; temp |= data->pwm_trange[attr->index] & ADT7462_PWM_HYST_MASK; @@ -1329,11 +1328,11 @@ static ssize_t set_pwm_tmin(struct device *dev, struct adt7462_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp)) + if (kstrtol(buf, 10, &temp)) return -EINVAL; temp = DIV_ROUND_CLOSEST(temp, 1000) + 64; - temp = SENSORS_LIMIT(temp, 0, 255); + temp = clamp_val(temp, 0, 255); mutex_lock(&data->lock); data->pwm_tmin[attr->index] = temp; @@ -1386,7 +1385,7 @@ static ssize_t set_pwm_auto(struct device *dev, struct adt7462_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp)) + if (kstrtol(buf, 10, &temp)) return -EINVAL; switch (temp) { @@ -1445,7 +1444,7 @@ static ssize_t set_pwm_auto_temp(struct device *dev, struct adt7462_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp)) + if (kstrtol(buf, 10, &temp)) return -EINVAL; temp = cvt_auto_temp(temp); @@ -1726,8 +1725,7 @@ static SENSOR_DEVICE_ATTR(pwm3_auto_channels_temp, S_IWUSR | S_IRUGO, static SENSOR_DEVICE_ATTR(pwm4_auto_channels_temp, S_IWUSR | S_IRUGO, show_pwm_auto_temp, set_pwm_auto_temp, 3); -static struct attribute *adt7462_attr[] = -{ +static struct attribute *adt7462_attr[] = { &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr, &sensor_dev_attr_temp3_max.dev_attr.attr, @@ -1931,11 +1929,10 @@ static int adt7462_probe(struct i2c_client *client, struct adt7462_data *data; int err; - data = kzalloc(sizeof(struct adt7462_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&client->dev, sizeof(struct adt7462_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); mutex_init(&data->lock); @@ -1946,7 +1943,7 @@ static int adt7462_probe(struct i2c_client *client, data->attrs.attrs = adt7462_attr; err = sysfs_create_group(&client->dev.kobj, &data->attrs); if (err) - goto exit_free; + return err; data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { @@ -1958,9 +1955,6 @@ static int adt7462_probe(struct i2c_client *client, exit_remove: sysfs_remove_group(&client->dev.kobj, &data->attrs); -exit_free: - kfree(data); -exit: return err; } @@ -1970,23 +1964,11 @@ static int adt7462_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &data->attrs); - kfree(data); return 0; } -static int __init adt7462_init(void) -{ - return i2c_add_driver(&adt7462_driver); -} +module_i2c_driver(adt7462_driver); -static void __exit adt7462_exit(void) -{ - i2c_del_driver(&adt7462_driver); -} - -MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); +MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>"); MODULE_DESCRIPTION("ADT7462 driver"); MODULE_LICENSE("GPL"); - -module_init(adt7462_init); -module_exit(adt7462_exit); diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 3445ce1cba8..9ee3913850d 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -2,7 +2,7 @@ * A hwmon driver for the Analog Devices ADT7470 * Copyright (C) 2007 IBM * - * Author: Darrick J. Wong <djwong@us.ibm.com> + * Author: Darrick J. Wong <darrick.wong@oracle.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 @@ -19,6 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/jiffies.h> #include <linux/i2c.h> @@ -29,6 +31,7 @@ #include <linux/delay.h> #include <linux/log2.h> #include <linux/kthread.h> +#include <linux/slab.h> /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; @@ -212,7 +215,7 @@ static inline int adt7470_write_word_data(struct i2c_client *client, u8 reg, u16 value) { return i2c_smbus_write_byte_data(client, reg, value & 0xFF) - && i2c_smbus_write_byte_data(client, reg + 1, value >> 8); + || i2c_smbus_write_byte_data(client, reg + 1, value >> 8); } static void adt7470_init_client(struct i2c_client *client) @@ -273,7 +276,7 @@ static int adt7470_read_temperatures(struct i2c_client *client, i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); if (res) { - printk(KERN_ERR "ha ha, interrupted"); + pr_err("ha ha, interrupted\n"); return -EAGAIN; } @@ -446,10 +449,10 @@ static ssize_t set_auto_update_interval(struct device *dev, struct adt7470_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp)) + if (kstrtol(buf, 10, &temp)) return -EINVAL; - temp = SENSORS_LIMIT(temp, 0, 60000); + temp = clamp_val(temp, 0, 60000); mutex_lock(&data->lock); data->auto_update_interval = temp; @@ -475,10 +478,10 @@ static ssize_t set_num_temp_sensors(struct device *dev, struct adt7470_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp)) + if (kstrtol(buf, 10, &temp)) return -EINVAL; - temp = SENSORS_LIMIT(temp, -1, 10); + temp = clamp_val(temp, -1, 10); mutex_lock(&data->lock); data->num_temp_sensors = temp; @@ -508,11 +511,11 @@ static ssize_t set_temp_min(struct device *dev, struct adt7470_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp)) + if (kstrtol(buf, 10, &temp)) return -EINVAL; temp = DIV_ROUND_CLOSEST(temp, 1000); - temp = SENSORS_LIMIT(temp, 0, 255); + temp = clamp_val(temp, -128, 127); mutex_lock(&data->lock); data->temp_min[attr->index] = temp; @@ -542,11 +545,11 @@ static ssize_t set_temp_max(struct device *dev, struct adt7470_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp)) + if (kstrtol(buf, 10, &temp)) return -EINVAL; temp = DIV_ROUND_CLOSEST(temp, 1000); - temp = SENSORS_LIMIT(temp, 0, 255); + temp = clamp_val(temp, -128, 127); mutex_lock(&data->lock); data->temp_max[attr->index] = temp; @@ -597,11 +600,11 @@ static ssize_t set_fan_max(struct device *dev, struct adt7470_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp) || !temp) + if (kstrtol(buf, 10, &temp) || !temp) return -EINVAL; temp = FAN_RPM_TO_PERIOD(temp); - temp = SENSORS_LIMIT(temp, 1, 65534); + temp = clamp_val(temp, 1, 65534); mutex_lock(&data->lock); data->fan_max[attr->index] = temp; @@ -634,11 +637,11 @@ static ssize_t set_fan_min(struct device *dev, struct adt7470_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp) || !temp) + if (kstrtol(buf, 10, &temp) || !temp) return -EINVAL; temp = FAN_RPM_TO_PERIOD(temp); - temp = SENSORS_LIMIT(temp, 1, 65534); + temp = clamp_val(temp, 1, 65534); mutex_lock(&data->lock); data->fan_min[attr->index] = temp; @@ -679,7 +682,7 @@ static ssize_t set_force_pwm_max(struct device *dev, long temp; u8 reg; - if (strict_strtol(buf, 10, &temp)) + if (kstrtol(buf, 10, &temp)) return -EINVAL; mutex_lock(&data->lock); @@ -711,10 +714,10 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, struct adt7470_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp)) + if (kstrtol(buf, 10, &temp)) return -EINVAL; - temp = SENSORS_LIMIT(temp, 0, 255); + temp = clamp_val(temp, 0, 255); mutex_lock(&data->lock); data->pwm[attr->index] = temp; @@ -743,10 +746,10 @@ static ssize_t set_pwm_max(struct device *dev, struct adt7470_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp)) + if (kstrtol(buf, 10, &temp)) return -EINVAL; - temp = SENSORS_LIMIT(temp, 0, 255); + temp = clamp_val(temp, 0, 255); mutex_lock(&data->lock); data->pwm_max[attr->index] = temp; @@ -776,10 +779,10 @@ static ssize_t set_pwm_min(struct device *dev, struct adt7470_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp)) + if (kstrtol(buf, 10, &temp)) return -EINVAL; - temp = SENSORS_LIMIT(temp, 0, 255); + temp = clamp_val(temp, 0, 255); mutex_lock(&data->lock); data->pwm_min[attr->index] = temp; @@ -819,11 +822,11 @@ static ssize_t set_pwm_tmin(struct device *dev, struct adt7470_data *data = i2c_get_clientdata(client); long temp; - if (strict_strtol(buf, 10, &temp)) + if (kstrtol(buf, 10, &temp)) return -EINVAL; temp = DIV_ROUND_CLOSEST(temp, 1000); - temp = SENSORS_LIMIT(temp, 0, 255); + temp = clamp_val(temp, -128, 127); mutex_lock(&data->lock); data->pwm_tmin[attr->index] = temp; @@ -856,7 +859,7 @@ static ssize_t set_pwm_auto(struct device *dev, long temp; u8 reg; - if (strict_strtol(buf, 10, &temp)) + if (kstrtol(buf, 10, &temp)) return -EINVAL; if (attr->index % 2) @@ -916,7 +919,7 @@ static ssize_t set_pwm_auto_temp(struct device *dev, long temp; u8 reg; - if (strict_strtol(buf, 10, &temp)) + if (kstrtol(buf, 10, &temp)) return -EINVAL; temp = cvt_auto_temp(temp); @@ -1128,8 +1131,7 @@ static SENSOR_DEVICE_ATTR(pwm3_auto_channels_temp, S_IWUSR | S_IRUGO, static SENSOR_DEVICE_ATTR(pwm4_auto_channels_temp, S_IWUSR | S_IRUGO, show_pwm_auto_temp, set_pwm_auto_temp, 3); -static struct attribute *adt7470_attr[] = -{ +static struct attribute *adt7470_attr[] = { &dev_attr_alarm_mask.attr, &dev_attr_num_temp_sensors.attr, &dev_attr_auto_update_interval.attr, @@ -1254,11 +1256,10 @@ static int adt7470_probe(struct i2c_client *client, struct adt7470_data *data; int err; - data = kzalloc(sizeof(struct adt7470_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&client->dev, sizeof(struct adt7470_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; data->num_temp_sensors = -1; data->auto_update_interval = AUTO_UPDATE_INTERVAL; @@ -1273,8 +1274,9 @@ static int adt7470_probe(struct i2c_client *client, /* Register sysfs hooks */ data->attrs.attrs = adt7470_attr; - if ((err = sysfs_create_group(&client->dev.kobj, &data->attrs))) - goto exit_free; + err = sysfs_create_group(&client->dev.kobj, &data->attrs); + if (err) + return err; data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { @@ -1283,10 +1285,12 @@ static int adt7470_probe(struct i2c_client *client, } init_completion(&data->auto_update_stop); - data->auto_update = kthread_run(adt7470_update_thread, client, + data->auto_update = kthread_run(adt7470_update_thread, client, "%s", dev_name(data->hwmon_dev)); - if (IS_ERR(data->auto_update)) + if (IS_ERR(data->auto_update)) { + err = PTR_ERR(data->auto_update); goto exit_unregister; + } return 0; @@ -1294,9 +1298,6 @@ exit_unregister: hwmon_device_unregister(data->hwmon_dev); exit_remove: sysfs_remove_group(&client->dev.kobj, &data->attrs); -exit_free: - kfree(data); -exit: return err; } @@ -1308,23 +1309,11 @@ static int adt7470_remove(struct i2c_client *client) wait_for_completion(&data->auto_update_stop); hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &data->attrs); - kfree(data); return 0; } -static int __init adt7470_init(void) -{ - return i2c_add_driver(&adt7470_driver); -} - -static void __exit adt7470_exit(void) -{ - i2c_del_driver(&adt7470_driver); -} +module_i2c_driver(adt7470_driver); -MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); +MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>"); MODULE_DESCRIPTION("ADT7470 driver"); MODULE_LICENSE("GPL"); - -module_init(adt7470_init); -module_exit(adt7470_exit); diff --git a/drivers/hwmon/adt7473.c b/drivers/hwmon/adt7473.c deleted file mode 100644 index 434576f61c8..00000000000 --- a/drivers/hwmon/adt7473.c +++ /dev/null @@ -1,1180 +0,0 @@ -/* - * A hwmon driver for the Analog Devices ADT7473 - * Copyright (C) 2007 IBM - * - * Author: Darrick J. Wong <djwong@us.ibm.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/jiffies.h> -#include <linux/i2c.h> -#include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> -#include <linux/err.h> -#include <linux/mutex.h> -#include <linux/delay.h> -#include <linux/log2.h> - -/* Addresses to scan */ -static const unsigned short normal_i2c[] = { 0x2C, 0x2D, 0x2E, I2C_CLIENT_END }; - -/* ADT7473 registers */ -#define ADT7473_REG_BASE_ADDR 0x20 - -#define ADT7473_REG_VOLT_BASE_ADDR 0x21 -#define ADT7473_REG_VOLT_MIN_BASE_ADDR 0x46 - -#define ADT7473_REG_TEMP_BASE_ADDR 0x25 -#define ADT7473_REG_TEMP_LIMITS_BASE_ADDR 0x4E -#define ADT7473_REG_TEMP_TMIN_BASE_ADDR 0x67 -#define ADT7473_REG_TEMP_TMAX_BASE_ADDR 0x6A - -#define ADT7473_REG_FAN_BASE_ADDR 0x28 -#define ADT7473_REG_FAN_MIN_BASE_ADDR 0x54 - -#define ADT7473_REG_PWM_BASE_ADDR 0x30 -#define ADT7473_REG_PWM_MIN_BASE_ADDR 0x64 -#define ADT7473_REG_PWM_MAX_BASE_ADDR 0x38 -#define ADT7473_REG_PWM_BHVR_BASE_ADDR 0x5C -#define ADT7473_PWM_BHVR_MASK 0xE0 -#define ADT7473_PWM_BHVR_SHIFT 5 - -#define ADT7473_REG_CFG1 0x40 -#define ADT7473_CFG1_START 0x01 -#define ADT7473_CFG1_READY 0x04 -#define ADT7473_REG_CFG2 0x73 -#define ADT7473_REG_CFG3 0x78 -#define ADT7473_REG_CFG4 0x7D -#define ADT7473_CFG4_MAX_DUTY_AT_OVT 0x08 -#define ADT7473_REG_CFG5 0x7C -#define ADT7473_CFG5_TEMP_TWOS 0x01 -#define ADT7473_CFG5_TEMP_OFFSET 0x02 - -#define ADT7473_REG_DEVICE 0x3D -#define ADT7473_VENDOR 0x41 -#define ADT7473_REG_VENDOR 0x3E -#define ADT7473_DEVICE 0x73 -#define ADT7473_REG_REVISION 0x3F -#define ADT7473_REV_68 0x68 -#define ADT7473_REV_69 0x69 - -#define ADT7473_REG_ALARM1 0x41 -#define ADT7473_VCCP_ALARM 0x02 -#define ADT7473_VCC_ALARM 0x04 -#define ADT7473_R1T_ALARM 0x10 -#define ADT7473_LT_ALARM 0x20 -#define ADT7473_R2T_ALARM 0x40 -#define ADT7473_OOL 0x80 -#define ADT7473_REG_ALARM2 0x42 -#define ADT7473_OVT_ALARM 0x02 -#define ADT7473_FAN1_ALARM 0x04 -#define ADT7473_FAN2_ALARM 0x08 -#define ADT7473_FAN3_ALARM 0x10 -#define ADT7473_FAN4_ALARM 0x20 -#define ADT7473_R1T_SHORT 0x40 -#define ADT7473_R2T_SHORT 0x80 - -#define ALARM2(x) ((x) << 8) - -#define ADT7473_VOLT_COUNT 2 -#define ADT7473_REG_VOLT(x) (ADT7473_REG_VOLT_BASE_ADDR + (x)) -#define ADT7473_REG_VOLT_MIN(x) (ADT7473_REG_VOLT_MIN_BASE_ADDR + ((x) * 2)) -#define ADT7473_REG_VOLT_MAX(x) (ADT7473_REG_VOLT_MIN_BASE_ADDR + \ - ((x) * 2) + 1) - -#define ADT7473_TEMP_COUNT 3 -#define ADT7473_REG_TEMP(x) (ADT7473_REG_TEMP_BASE_ADDR + (x)) -#define ADT7473_REG_TEMP_MIN(x) (ADT7473_REG_TEMP_LIMITS_BASE_ADDR + ((x) * 2)) -#define ADT7473_REG_TEMP_MAX(x) (ADT7473_REG_TEMP_LIMITS_BASE_ADDR + \ - ((x) * 2) + 1) -#define ADT7473_REG_TEMP_TMIN(x) (ADT7473_REG_TEMP_TMIN_BASE_ADDR + (x)) -#define ADT7473_REG_TEMP_TMAX(x) (ADT7473_REG_TEMP_TMAX_BASE_ADDR + (x)) - -#define ADT7473_FAN_COUNT 4 -#define ADT7473_REG_FAN(x) (ADT7473_REG_FAN_BASE_ADDR + ((x) * 2)) -#define ADT7473_REG_FAN_MIN(x) (ADT7473_REG_FAN_MIN_BASE_ADDR + ((x) * 2)) - -#define ADT7473_PWM_COUNT 3 -#define ADT7473_REG_PWM(x) (ADT7473_REG_PWM_BASE_ADDR + (x)) -#define ADT7473_REG_PWM_MAX(x) (ADT7473_REG_PWM_MAX_BASE_ADDR + (x)) -#define ADT7473_REG_PWM_MIN(x) (ADT7473_REG_PWM_MIN_BASE_ADDR + (x)) -#define ADT7473_REG_PWM_BHVR(x) (ADT7473_REG_PWM_BHVR_BASE_ADDR + (x)) - -/* How often do we reread sensors values? (In jiffies) */ -#define SENSOR_REFRESH_INTERVAL (2 * HZ) - -/* How often do we reread sensor limit values? (In jiffies) */ -#define LIMIT_REFRESH_INTERVAL (60 * HZ) - -/* datasheet says to divide this number by the fan reading to get fan rpm */ -#define FAN_PERIOD_TO_RPM(x) ((90000 * 60) / (x)) -#define FAN_RPM_TO_PERIOD FAN_PERIOD_TO_RPM -#define FAN_PERIOD_INVALID 65535 -#define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) - -struct adt7473_data { - struct device *hwmon_dev; - struct attribute_group attrs; - struct mutex lock; - char sensors_valid; - char limits_valid; - unsigned long sensors_last_updated; /* In jiffies */ - unsigned long limits_last_updated; /* In jiffies */ - - u8 volt[ADT7473_VOLT_COUNT]; - s8 volt_min[ADT7473_VOLT_COUNT]; - s8 volt_max[ADT7473_VOLT_COUNT]; - - s8 temp[ADT7473_TEMP_COUNT]; - s8 temp_min[ADT7473_TEMP_COUNT]; - s8 temp_max[ADT7473_TEMP_COUNT]; - s8 temp_tmin[ADT7473_TEMP_COUNT]; - /* This is called the !THERM limit in the datasheet */ - s8 temp_tmax[ADT7473_TEMP_COUNT]; - - u16 fan[ADT7473_FAN_COUNT]; - u16 fan_min[ADT7473_FAN_COUNT]; - - u8 pwm[ADT7473_PWM_COUNT]; - u8 pwm_max[ADT7473_PWM_COUNT]; - u8 pwm_min[ADT7473_PWM_COUNT]; - u8 pwm_behavior[ADT7473_PWM_COUNT]; - - u8 temp_twos_complement; - u8 temp_offset; - - u16 alarm; - u8 max_duty_at_overheat; -}; - -static int adt7473_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int adt7473_detect(struct i2c_client *client, - struct i2c_board_info *info); -static int adt7473_remove(struct i2c_client *client); - -static const struct i2c_device_id adt7473_id[] = { - { "adt7473", 0 }, - { } -}; - -static struct i2c_driver adt7473_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "adt7473", - }, - .probe = adt7473_probe, - .remove = adt7473_remove, - .id_table = adt7473_id, - .detect = adt7473_detect, - .address_list = normal_i2c, -}; - -/* - * 16-bit registers on the ADT7473 are low-byte first. The data sheet says - * that the low byte must be read before the high byte. - */ -static inline int adt7473_read_word_data(struct i2c_client *client, u8 reg) -{ - u16 foo; - foo = i2c_smbus_read_byte_data(client, reg); - foo |= ((u16)i2c_smbus_read_byte_data(client, reg + 1) << 8); - return foo; -} - -static inline int adt7473_write_word_data(struct i2c_client *client, u8 reg, - u16 value) -{ - return i2c_smbus_write_byte_data(client, reg, value & 0xFF) - && i2c_smbus_write_byte_data(client, reg + 1, value >> 8); -} - -static void adt7473_init_client(struct i2c_client *client) -{ - int reg = i2c_smbus_read_byte_data(client, ADT7473_REG_CFG1); - - if (!(reg & ADT7473_CFG1_READY)) { - dev_err(&client->dev, "Chip not ready.\n"); - } else { - /* start monitoring */ - i2c_smbus_write_byte_data(client, ADT7473_REG_CFG1, - reg | ADT7473_CFG1_START); - } -} - -static struct adt7473_data *adt7473_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - unsigned long local_jiffies = jiffies; - u8 cfg; - int i; - - mutex_lock(&data->lock); - if (time_before(local_jiffies, data->sensors_last_updated + - SENSOR_REFRESH_INTERVAL) - && data->sensors_valid) - goto no_sensor_update; - - for (i = 0; i < ADT7473_VOLT_COUNT; i++) - data->volt[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_VOLT(i)); - - /* Determine temperature encoding */ - cfg = i2c_smbus_read_byte_data(client, ADT7473_REG_CFG5); - data->temp_twos_complement = (cfg & ADT7473_CFG5_TEMP_TWOS); - - /* - * What does this do? it implies a variable temperature sensor - * offset, but the datasheet doesn't say anything about this bit - * and other parts of the datasheet imply that "offset64" mode - * means that you shift temp values by -64 if the above bit was set. - */ - data->temp_offset = (cfg & ADT7473_CFG5_TEMP_OFFSET); - - for (i = 0; i < ADT7473_TEMP_COUNT; i++) - data->temp[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_TEMP(i)); - - for (i = 0; i < ADT7473_FAN_COUNT; i++) - data->fan[i] = adt7473_read_word_data(client, - ADT7473_REG_FAN(i)); - - for (i = 0; i < ADT7473_PWM_COUNT; i++) - data->pwm[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_PWM(i)); - - data->alarm = i2c_smbus_read_byte_data(client, ADT7473_REG_ALARM1); - if (data->alarm & ADT7473_OOL) - data->alarm |= ALARM2(i2c_smbus_read_byte_data(client, - ADT7473_REG_ALARM2)); - - data->sensors_last_updated = local_jiffies; - data->sensors_valid = 1; - -no_sensor_update: - if (time_before(local_jiffies, data->limits_last_updated + - LIMIT_REFRESH_INTERVAL) - && data->limits_valid) - goto out; - - for (i = 0; i < ADT7473_VOLT_COUNT; i++) { - data->volt_min[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_VOLT_MIN(i)); - data->volt_max[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_VOLT_MAX(i)); - } - - for (i = 0; i < ADT7473_TEMP_COUNT; i++) { - data->temp_min[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_TEMP_MIN(i)); - data->temp_max[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_TEMP_MAX(i)); - data->temp_tmin[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_TEMP_TMIN(i)); - data->temp_tmax[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_TEMP_TMAX(i)); - } - - for (i = 0; i < ADT7473_FAN_COUNT; i++) - data->fan_min[i] = adt7473_read_word_data(client, - ADT7473_REG_FAN_MIN(i)); - - for (i = 0; i < ADT7473_PWM_COUNT; i++) { - data->pwm_max[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_PWM_MAX(i)); - data->pwm_min[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_PWM_MIN(i)); - data->pwm_behavior[i] = i2c_smbus_read_byte_data(client, - ADT7473_REG_PWM_BHVR(i)); - } - - i = i2c_smbus_read_byte_data(client, ADT7473_REG_CFG4); - data->max_duty_at_overheat = !!(i & ADT7473_CFG4_MAX_DUTY_AT_OVT); - - data->limits_last_updated = local_jiffies; - data->limits_valid = 1; - -out: - mutex_unlock(&data->lock); - return data; -} - -/* - * Conversions - */ - -/* IN are scaled acording to built-in resistors */ -static const int adt7473_scaling[] = { /* .001 Volts */ - 2250, 3300 -}; -#define SCALE(val, from, to) (((val) * (to) + ((from) / 2)) / (from)) - -static int decode_volt(int volt_index, u8 raw) -{ - return SCALE(raw, 192, adt7473_scaling[volt_index]); -} - -static u8 encode_volt(int volt_index, int cooked) -{ - int raw = SCALE(cooked, adt7473_scaling[volt_index], 192); - return SENSORS_LIMIT(raw, 0, 255); -} - -static ssize_t show_volt_min(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", - decode_volt(attr->index, data->volt_min[attr->index])); -} - -static ssize_t set_volt_min(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long volt; - - if (strict_strtol(buf, 10, &volt)) - return -EINVAL; - - volt = encode_volt(attr->index, volt); - - mutex_lock(&data->lock); - data->volt_min[attr->index] = volt; - i2c_smbus_write_byte_data(client, ADT7473_REG_VOLT_MIN(attr->index), - volt); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_volt_max(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", - decode_volt(attr->index, data->volt_max[attr->index])); -} - -static ssize_t set_volt_max(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long volt; - - if (strict_strtol(buf, 10, &volt)) - return -EINVAL; - - volt = encode_volt(attr->index, volt); - - mutex_lock(&data->lock); - data->volt_max[attr->index] = volt; - i2c_smbus_write_byte_data(client, ADT7473_REG_VOLT_MAX(attr->index), - volt); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_volt(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - - return sprintf(buf, "%d\n", - decode_volt(attr->index, data->volt[attr->index])); -} - -/* - * This chip can report temperature data either as a two's complement - * number in the range -128 to 127, or as an unsigned number that must - * be offset by 64. - */ -static int decode_temp(u8 twos_complement, u8 raw) -{ - return twos_complement ? (s8)raw : raw - 64; -} - -static u8 encode_temp(u8 twos_complement, int cooked) -{ - u8 ret = twos_complement ? cooked & 0xFF : cooked + 64; - return SENSORS_LIMIT(ret, 0, 255); -} - -static ssize_t show_temp_min(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", 1000 * decode_temp( - data->temp_twos_complement, - data->temp_min[attr->index])); -} - -static ssize_t set_temp_min(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - temp = DIV_ROUND_CLOSEST(temp, 1000); - temp = encode_temp(data->temp_twos_complement, temp); - - mutex_lock(&data->lock); - data->temp_min[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7473_REG_TEMP_MIN(attr->index), - temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_temp_max(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", 1000 * decode_temp( - data->temp_twos_complement, - data->temp_max[attr->index])); -} - -static ssize_t set_temp_max(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - temp = DIV_ROUND_CLOSEST(temp, 1000); - temp = encode_temp(data->temp_twos_complement, temp); - - mutex_lock(&data->lock); - data->temp_max[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7473_REG_TEMP_MAX(attr->index), - temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", 1000 * decode_temp( - data->temp_twos_complement, - data->temp[attr->index])); -} - -static ssize_t show_fan_min(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - - if (FAN_DATA_VALID(data->fan_min[attr->index])) - return sprintf(buf, "%d\n", - FAN_PERIOD_TO_RPM(data->fan_min[attr->index])); - else - return sprintf(buf, "0\n"); -} - -static ssize_t set_fan_min(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp) || !temp) - return -EINVAL; - - temp = FAN_RPM_TO_PERIOD(temp); - temp = SENSORS_LIMIT(temp, 1, 65534); - - mutex_lock(&data->lock); - data->fan_min[attr->index] = temp; - adt7473_write_word_data(client, ADT7473_REG_FAN_MIN(attr->index), temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - - if (FAN_DATA_VALID(data->fan[attr->index])) - return sprintf(buf, "%d\n", - FAN_PERIOD_TO_RPM(data->fan[attr->index])); - else - return sprintf(buf, "0\n"); -} - -static ssize_t show_max_duty_at_crit(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", data->max_duty_at_overheat); -} - -static ssize_t set_max_duty_at_crit(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - u8 reg; - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - mutex_lock(&data->lock); - data->max_duty_at_overheat = !!temp; - reg = i2c_smbus_read_byte_data(client, ADT7473_REG_CFG4); - if (temp) - reg |= ADT7473_CFG4_MAX_DUTY_AT_OVT; - else - reg &= ~ADT7473_CFG4_MAX_DUTY_AT_OVT; - i2c_smbus_write_byte_data(client, ADT7473_REG_CFG4, reg); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", data->pwm[attr->index]); -} - -static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - temp = SENSORS_LIMIT(temp, 0, 255); - - mutex_lock(&data->lock); - data->pwm[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7473_REG_PWM(attr->index), temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_pwm_max(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", data->pwm_max[attr->index]); -} - -static ssize_t set_pwm_max(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - temp = SENSORS_LIMIT(temp, 0, 255); - - mutex_lock(&data->lock); - data->pwm_max[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7473_REG_PWM_MAX(attr->index), - temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_pwm_min(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", data->pwm_min[attr->index]); -} - -static ssize_t set_pwm_min(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - temp = SENSORS_LIMIT(temp, 0, 255); - - mutex_lock(&data->lock); - data->pwm_min[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7473_REG_PWM_MIN(attr->index), - temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_temp_tmax(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", 1000 * decode_temp( - data->temp_twos_complement, - data->temp_tmax[attr->index])); -} - -static ssize_t set_temp_tmax(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - temp = DIV_ROUND_CLOSEST(temp, 1000); - temp = encode_temp(data->temp_twos_complement, temp); - - mutex_lock(&data->lock); - data->temp_tmax[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7473_REG_TEMP_TMAX(attr->index), - temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_temp_tmin(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - return sprintf(buf, "%d\n", 1000 * decode_temp( - data->temp_twos_complement, - data->temp_tmin[attr->index])); -} - -static ssize_t set_temp_tmin(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - temp = DIV_ROUND_CLOSEST(temp, 1000); - temp = encode_temp(data->temp_twos_complement, temp); - - mutex_lock(&data->lock); - data->temp_tmin[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7473_REG_TEMP_TMIN(attr->index), - temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_pwm_enable(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - - switch (data->pwm_behavior[attr->index] >> ADT7473_PWM_BHVR_SHIFT) { - case 3: - return sprintf(buf, "0\n"); - case 7: - return sprintf(buf, "1\n"); - default: - return sprintf(buf, "2\n"); - } -} - -static ssize_t set_pwm_enable(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - u8 reg; - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - switch (temp) { - case 0: - temp = 3; - break; - case 1: - temp = 7; - break; - case 2: - /* Enter automatic mode with fans off */ - temp = 4; - break; - default: - return -EINVAL; - } - - mutex_lock(&data->lock); - reg = i2c_smbus_read_byte_data(client, - ADT7473_REG_PWM_BHVR(attr->index)); - reg = (temp << ADT7473_PWM_BHVR_SHIFT) | - (reg & ~ADT7473_PWM_BHVR_MASK); - i2c_smbus_write_byte_data(client, ADT7473_REG_PWM_BHVR(attr->index), - reg); - data->pwm_behavior[attr->index] = reg; - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_pwm_auto_temp(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - int bhvr = data->pwm_behavior[attr->index] >> ADT7473_PWM_BHVR_SHIFT; - - switch (bhvr) { - case 3: - case 4: - case 7: - return sprintf(buf, "0\n"); - case 0: - case 1: - case 5: - case 6: - return sprintf(buf, "%d\n", bhvr + 1); - case 2: - return sprintf(buf, "4\n"); - } - /* shouldn't ever get here */ - BUG(); -} - -static ssize_t set_pwm_auto_temp(struct device *dev, - struct device_attribute *devattr, - const char *buf, - size_t count) -{ - u8 reg; - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7473_data *data = i2c_get_clientdata(client); - long temp; - - if (strict_strtol(buf, 10, &temp)) - return -EINVAL; - - switch (temp) { - case 1: - case 2: - case 6: - case 7: - temp--; - break; - case 0: - temp = 4; - break; - default: - return -EINVAL; - } - - mutex_lock(&data->lock); - reg = i2c_smbus_read_byte_data(client, - ADT7473_REG_PWM_BHVR(attr->index)); - reg = (temp << ADT7473_PWM_BHVR_SHIFT) | - (reg & ~ADT7473_PWM_BHVR_MASK); - i2c_smbus_write_byte_data(client, ADT7473_REG_PWM_BHVR(attr->index), - reg); - data->pwm_behavior[attr->index] = reg; - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t show_alarm(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7473_data *data = adt7473_update_device(dev); - - if (data->alarm & attr->index) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - - -static SENSOR_DEVICE_ATTR(in1_max, S_IWUSR | S_IRUGO, show_volt_max, - set_volt_max, 0); -static SENSOR_DEVICE_ATTR(in2_max, S_IWUSR | S_IRUGO, show_volt_max, - set_volt_max, 1); - -static SENSOR_DEVICE_ATTR(in1_min, S_IWUSR | S_IRUGO, show_volt_min, - set_volt_min, 0); -static SENSOR_DEVICE_ATTR(in2_min, S_IWUSR | S_IRUGO, show_volt_min, - set_volt_min, 1); - -static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_volt, NULL, 0); -static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_volt, NULL, 1); - -static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, - ADT7473_VCCP_ALARM); -static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, - ADT7473_VCC_ALARM); - -static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, - set_temp_max, 0); -static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_max, - set_temp_max, 1); -static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_temp_max, - set_temp_max, 2); - -static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_min, - set_temp_min, 0); -static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_min, - set_temp_min, 1); -static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_temp_min, - set_temp_min, 2); - -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); -static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); -static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); - -static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, - ADT7473_R1T_ALARM | ALARM2(ADT7473_R1T_SHORT)); -static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, - ADT7473_LT_ALARM); -static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, - ADT7473_R2T_ALARM | ALARM2(ADT7473_R2T_SHORT)); - -static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, - set_fan_min, 0); -static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, - set_fan_min, 1); -static SENSOR_DEVICE_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, - set_fan_min, 2); -static SENSOR_DEVICE_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min, - set_fan_min, 3); - -static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); -static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); -static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2); -static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3); - -static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, - ALARM2(ADT7473_FAN1_ALARM)); -static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, - ALARM2(ADT7473_FAN2_ALARM)); -static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, - ALARM2(ADT7473_FAN3_ALARM)); -static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, - ALARM2(ADT7473_FAN4_ALARM)); - -static SENSOR_DEVICE_ATTR(pwm_use_point2_pwm_at_crit, S_IWUSR | S_IRUGO, - show_max_duty_at_crit, set_max_duty_at_crit, 0); - -static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0); -static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1); -static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2); - -static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO, - show_pwm_min, set_pwm_min, 0); -static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO, - show_pwm_min, set_pwm_min, 1); -static SENSOR_DEVICE_ATTR(pwm3_auto_point1_pwm, S_IWUSR | S_IRUGO, - show_pwm_min, set_pwm_min, 2); - -static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO, - show_pwm_max, set_pwm_max, 0); -static SENSOR_DEVICE_ATTR(pwm2_auto_point2_pwm, S_IWUSR | S_IRUGO, - show_pwm_max, set_pwm_max, 1); -static SENSOR_DEVICE_ATTR(pwm3_auto_point2_pwm, S_IWUSR | S_IRUGO, - show_pwm_max, set_pwm_max, 2); - -static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, S_IWUSR | S_IRUGO, - show_temp_tmin, set_temp_tmin, 0); -static SENSOR_DEVICE_ATTR(temp2_auto_point1_temp, S_IWUSR | S_IRUGO, - show_temp_tmin, set_temp_tmin, 1); -static SENSOR_DEVICE_ATTR(temp3_auto_point1_temp, S_IWUSR | S_IRUGO, - show_temp_tmin, set_temp_tmin, 2); - -static SENSOR_DEVICE_ATTR(temp1_auto_point2_temp, S_IWUSR | S_IRUGO, - show_temp_tmax, set_temp_tmax, 0); -static SENSOR_DEVICE_ATTR(temp2_auto_point2_temp, S_IWUSR | S_IRUGO, - show_temp_tmax, set_temp_tmax, 1); -static SENSOR_DEVICE_ATTR(temp3_auto_point2_temp, S_IWUSR | S_IRUGO, - show_temp_tmax, set_temp_tmax, 2); - -static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable, - set_pwm_enable, 0); -static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable, - set_pwm_enable, 1); -static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable, - set_pwm_enable, 2); - -static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, S_IWUSR | S_IRUGO, - show_pwm_auto_temp, set_pwm_auto_temp, 0); -static SENSOR_DEVICE_ATTR(pwm2_auto_channels_temp, S_IWUSR | S_IRUGO, - show_pwm_auto_temp, set_pwm_auto_temp, 1); -static SENSOR_DEVICE_ATTR(pwm3_auto_channels_temp, S_IWUSR | S_IRUGO, - show_pwm_auto_temp, set_pwm_auto_temp, 2); - -static struct attribute *adt7473_attr[] = -{ - &sensor_dev_attr_in1_max.dev_attr.attr, - &sensor_dev_attr_in2_max.dev_attr.attr, - &sensor_dev_attr_in1_min.dev_attr.attr, - &sensor_dev_attr_in2_min.dev_attr.attr, - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_in2_input.dev_attr.attr, - &sensor_dev_attr_in1_alarm.dev_attr.attr, - &sensor_dev_attr_in2_alarm.dev_attr.attr, - - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp3_max.dev_attr.attr, - &sensor_dev_attr_temp1_min.dev_attr.attr, - &sensor_dev_attr_temp2_min.dev_attr.attr, - &sensor_dev_attr_temp3_min.dev_attr.attr, - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp1_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr, - &sensor_dev_attr_temp2_auto_point1_temp.dev_attr.attr, - &sensor_dev_attr_temp3_auto_point1_temp.dev_attr.attr, - &sensor_dev_attr_temp1_auto_point2_temp.dev_attr.attr, - &sensor_dev_attr_temp2_auto_point2_temp.dev_attr.attr, - &sensor_dev_attr_temp3_auto_point2_temp.dev_attr.attr, - - &sensor_dev_attr_fan1_min.dev_attr.attr, - &sensor_dev_attr_fan2_min.dev_attr.attr, - &sensor_dev_attr_fan3_min.dev_attr.attr, - &sensor_dev_attr_fan4_min.dev_attr.attr, - &sensor_dev_attr_fan1_input.dev_attr.attr, - &sensor_dev_attr_fan2_input.dev_attr.attr, - &sensor_dev_attr_fan3_input.dev_attr.attr, - &sensor_dev_attr_fan4_input.dev_attr.attr, - &sensor_dev_attr_fan1_alarm.dev_attr.attr, - &sensor_dev_attr_fan2_alarm.dev_attr.attr, - &sensor_dev_attr_fan3_alarm.dev_attr.attr, - &sensor_dev_attr_fan4_alarm.dev_attr.attr, - - &sensor_dev_attr_pwm_use_point2_pwm_at_crit.dev_attr.attr, - - &sensor_dev_attr_pwm1.dev_attr.attr, - &sensor_dev_attr_pwm2.dev_attr.attr, - &sensor_dev_attr_pwm3.dev_attr.attr, - &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, - &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, - &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, - &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr, - - &sensor_dev_attr_pwm1_enable.dev_attr.attr, - &sensor_dev_attr_pwm2_enable.dev_attr.attr, - &sensor_dev_attr_pwm3_enable.dev_attr.attr, - &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, - &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr, - - NULL -}; - -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int adt7473_detect(struct i2c_client *client, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = client->adapter; - int vendor, device, revision; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - vendor = i2c_smbus_read_byte_data(client, ADT7473_REG_VENDOR); - if (vendor != ADT7473_VENDOR) - return -ENODEV; - - device = i2c_smbus_read_byte_data(client, ADT7473_REG_DEVICE); - if (device != ADT7473_DEVICE) - return -ENODEV; - - revision = i2c_smbus_read_byte_data(client, ADT7473_REG_REVISION); - if (revision != ADT7473_REV_68 && revision != ADT7473_REV_69) - return -ENODEV; - - strlcpy(info->type, "adt7473", I2C_NAME_SIZE); - - return 0; -} - -static int adt7473_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct adt7473_data *data; - int err; - - data = kzalloc(sizeof(struct adt7473_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } - - i2c_set_clientdata(client, data); - mutex_init(&data->lock); - - dev_info(&client->dev, "%s chip found\n", client->name); - - /* Initialize the ADT7473 chip */ - adt7473_init_client(client); - - /* Register sysfs hooks */ - data->attrs.attrs = adt7473_attr; - err = sysfs_create_group(&client->dev.kobj, &data->attrs); - if (err) - goto exit_free; - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove; - } - - return 0; - -exit_remove: - sysfs_remove_group(&client->dev.kobj, &data->attrs); -exit_free: - kfree(data); -exit: - return err; -} - -static int adt7473_remove(struct i2c_client *client) -{ - struct adt7473_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &data->attrs); - kfree(data); - return 0; -} - -static int __init adt7473_init(void) -{ - pr_notice("The adt7473 driver is deprecated, please use the adt7475 " - "driver instead\n"); - return i2c_add_driver(&adt7473_driver); -} - -static void __exit adt7473_exit(void) -{ - i2c_del_driver(&adt7473_driver); -} - -MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); -MODULE_DESCRIPTION("ADT7473 driver"); -MODULE_LICENSE("GPL"); - -module_init(adt7473_init); -module_exit(adt7473_exit); diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index a0c38514568..3cefd1aeb24 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -3,7 +3,7 @@ * Copyright (C) 2007-2008, Advanced Micro Devices, Inc. * Copyright (C) 2008 Jordan Crouse <jordan@cosmicpenguin.net> * Copyright (C) 2008 Hans de Goede <hdegoede@redhat.com> - * Copyright (C) 2009 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2009 Jean Delvare <jdelvare@suse.de> * * Derived from the lm83 driver by Jean Delvare * @@ -20,6 +20,7 @@ #include <linux/hwmon-sysfs.h> #include <linux/hwmon-vid.h> #include <linux/err.h> +#include <linux/jiffies.h> /* Indexes for the sysfs hooks */ @@ -32,9 +33,10 @@ #define THERM 5 #define HYSTERSIS 6 -/* These are unique identifiers for the sysfs functions - unlike the - numbers above, these are not also indexes into an array -*/ +/* + * These are unique identifiers for the sysfs functions - unlike the + * numbers above, these are not also indexes into an array + */ #define ALARM 9 #define FAULT 10 @@ -146,7 +148,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 }; @@ -199,10 +201,10 @@ static inline u16 temp2reg(struct adt7475_data *data, long val) u16 ret; if (!(data->config5 & CONFIG5_TWOSCOMP)) { - val = SENSORS_LIMIT(val, -64000, 191000); + val = clamp_val(val, -64000, 191000); ret = (val + 64500) / 1000; } else { - val = SENSORS_LIMIT(val, -128000, 127000); + val = clamp_val(val, -128000, 127000); if (val < -500) ret = (256500 + val) / 1000; else @@ -238,7 +240,7 @@ static inline u16 rpm2tach(unsigned long rpm) if (rpm == 0) return 0; - return SENSORS_LIMIT((90000 * 60) / rpm, 1, 0xFFFF); + return clamp_val((90000 * 60) / rpm, 1, 0xFFFF); } /* Scaling factors for voltage inputs, taken from the ADT7490 datasheet */ @@ -269,7 +271,7 @@ static inline u16 volt2reg(int channel, long volt, u8 bypass_attn) reg = (volt * 1024) / 2250; else reg = (volt * r[1] * 1024) / ((r[0] + r[1]) * 2250); - return SENSORS_LIMIT(reg, 0, 1023) & (0xff << 2); + return clamp_val(reg, 0, 1023) & (0xff << 2); } static u16 adt7475_read_word(struct i2c_client *client, int reg) @@ -288,8 +290,10 @@ static void adt7475_write_word(struct i2c_client *client, int reg, u16 val) i2c_smbus_write_byte_data(client, reg, val & 0xFF); } -/* Find the nearest value in a table - used for pwm frequency and - auto temp range */ +/* + * Find the nearest value in a table - used for pwm frequency and + * auto temp range + */ static int find_nearest(long val, const int *array, int size) { int i; @@ -343,7 +347,7 @@ static ssize_t set_voltage(struct device *dev, struct device_attribute *attr, unsigned char reg; long val; - if (strict_strtol(buf, 10, &val)) + if (kstrtol(buf, 10, &val)) return -EINVAL; mutex_lock(&data->lock); @@ -385,16 +389,20 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *attr, out = (out >> 4) & 0xF; else out = (out & 0xF); - /* Show the value as an absolute number tied to - * THERM */ + /* + * Show the value as an absolute number tied to + * THERM + */ out = reg2temp(data, data->temp[THERM][sattr->index]) - out * 1000; mutex_unlock(&data->lock); break; case OFFSET: - /* Offset is always 2's complement, regardless of the - * setting in CONFIG5 */ + /* + * Offset is always 2's complement, regardless of the + * setting in CONFIG5 + */ mutex_lock(&data->lock); out = (s8)data->temp[sattr->nr][sattr->index]; if (data->config5 & CONFIG5_TEMPOFFSET) @@ -432,7 +440,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *attr, int temp; long val; - if (strict_strtol(buf, 10, &val)) + if (kstrtol(buf, 10, &val)) return -EINVAL; mutex_lock(&data->lock); @@ -443,17 +451,19 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *attr, switch (sattr->nr) { case OFFSET: if (data->config5 & CONFIG5_TEMPOFFSET) { - val = SENSORS_LIMIT(val, -63000, 127000); + val = clamp_val(val, -63000, 127000); out = data->temp[OFFSET][sattr->index] = val / 1000; } else { - val = SENSORS_LIMIT(val, -63000, 64000); + val = clamp_val(val, -63000, 64000); out = data->temp[OFFSET][sattr->index] = val / 500; } break; case HYSTERSIS: - /* The value will be given as an absolute value, turn it - into an offset based on THERM */ + /* + * The value will be given as an absolute value, turn it + * into an offset based on THERM + */ /* Read fresh THERM and HYSTERSIS values from the chip */ data->temp[THERM][sattr->index] = @@ -461,7 +471,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *attr, adt7475_read_hystersis(client); temp = reg2temp(data, data->temp[THERM][sattr->index]); - val = SENSORS_LIMIT(val, temp - 15000, temp); + val = clamp_val(val, temp - 15000, temp); val = (temp - val) / 1000; if (sattr->index != 1) { @@ -478,8 +488,10 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *attr, default: data->temp[sattr->nr][sattr->index] = temp2reg(data, val); - /* We maintain an extra 2 digits of precision for simplicity - * - shift those back off before writing the value */ + /* + * We maintain an extra 2 digits of precision for simplicity + * - shift those back off before writing the value + */ out = (u8) (data->temp[sattr->nr][sattr->index] >> 2); } @@ -514,8 +526,10 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *attr, return count; } -/* Table of autorange values - the user will write the value in millidegrees, - and we'll convert it */ +/* + * Table of autorange values - the user will write the value in millidegrees, + * and we'll convert it + */ static const int autorange_table[] = { 2000, 2500, 3330, 4000, 5000, 6670, 8000, 10000, 13330, 16000, 20000, 26670, 32000, 40000, @@ -546,7 +560,7 @@ static ssize_t set_point2(struct device *dev, struct device_attribute *attr, int temp; long val; - if (strict_strtol(buf, 10, &val)) + if (kstrtol(buf, 10, &val)) return -EINVAL; mutex_lock(&data->lock); @@ -558,10 +572,12 @@ static ssize_t set_point2(struct device *dev, struct device_attribute *attr, data->range[sattr->index] = adt7475_read(TEMP_TRANGE_REG(sattr->index)); - /* The user will write an absolute value, so subtract the start point - to figure the range */ + /* + * The user will write an absolute value, so subtract the start point + * to figure the range + */ temp = reg2temp(data, data->temp[AUTOMIN][sattr->index]); - val = SENSORS_LIMIT(val, temp + autorange_table[0], + val = clamp_val(val, temp + autorange_table[0], temp + autorange_table[ARRAY_SIZE(autorange_table) - 1]); val -= temp; @@ -602,7 +618,7 @@ static ssize_t set_tach(struct device *dev, struct device_attribute *attr, struct adt7475_data *data = i2c_get_clientdata(client); unsigned long val; - if (strict_strtoul(buf, 10, &val)) + if (kstrtoul(buf, 10, &val)) return -EINVAL; mutex_lock(&data->lock); @@ -653,7 +669,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, unsigned char reg = 0; long val; - if (strict_strtol(buf, 10, &val)) + if (kstrtol(buf, 10, &val)) return -EINVAL; mutex_lock(&data->lock); @@ -664,8 +680,10 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, data->pwm[CONTROL][sattr->index] = adt7475_read(PWM_CONFIG_REG(sattr->index)); - /* If we are not in manual mode, then we shouldn't allow - * the user to set the pwm speed */ + /* + * If we are not in manual mode, then we shouldn't allow + * the user to set the pwm speed + */ if (((data->pwm[CONTROL][sattr->index] >> 5) & 7) != 7) { mutex_unlock(&data->lock); return count; @@ -683,7 +701,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, break; } - data->pwm[sattr->nr][sattr->index] = SENSORS_LIMIT(val, 0, 0xFF); + data->pwm[sattr->nr][sattr->index] = clamp_val(val, 0, 0xFF); i2c_smbus_write_byte_data(client, reg, data->pwm[sattr->nr][sattr->index]); @@ -758,7 +776,7 @@ static ssize_t set_pwmchan(struct device *dev, struct device_attribute *attr, int r; long val; - if (strict_strtol(buf, 10, &val)) + if (kstrtol(buf, 10, &val)) return -EINVAL; mutex_lock(&data->lock); @@ -781,7 +799,7 @@ static ssize_t set_pwmctrl(struct device *dev, struct device_attribute *attr, int r; long val; - if (strict_strtol(buf, 10, &val)) + if (kstrtol(buf, 10, &val)) return -EINVAL; mutex_lock(&data->lock); @@ -819,7 +837,7 @@ static ssize_t set_pwmfreq(struct device *dev, struct device_attribute *attr, int out; long val; - if (strict_strtol(buf, 10, &val)) + if (kstrtol(buf, 10, &val)) return -EINVAL; out = find_nearest(val, pwmfreq_table, ARRAY_SIZE(pwmfreq_table)); @@ -853,7 +871,7 @@ static ssize_t set_pwm_at_crit(struct device *dev, struct adt7475_data *data = i2c_get_clientdata(client); long val; - if (strict_strtol(buf, 10, &val)) + if (kstrtol(buf, 10, &val)) return -EINVAL; if (val != 0 && val != 1) return -EINVAL; @@ -883,7 +901,7 @@ static ssize_t set_vrm(struct device *dev, struct device_attribute *devattr, struct adt7475_data *data = dev_get_drvdata(dev); long val; - if (strict_strtol(buf, 10, &val)) + if (kstrtol(buf, 10, &val)) return -EINVAL; if (val < 0 || val > 255) return -EINVAL; @@ -1232,7 +1250,7 @@ static void adt7475_remove_files(struct i2c_client *client, static int adt7475_probe(struct i2c_client *client, const struct i2c_device_id *id) { - static const char *names[] = { + static const char * const names[] = { [adt7473] = "ADT7473", [adt7475] = "ADT7475", [adt7476] = "ADT7476", @@ -1243,7 +1261,7 @@ static int adt7475_probe(struct i2c_client *client, int i, ret = 0, revision; u8 config2, config3; - data = kzalloc(sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); if (data == NULL) return -ENOMEM; @@ -1280,9 +1298,11 @@ static int adt7475_probe(struct i2c_client *client, if ((data->config4 & CONFIG4_PINFUNC) == 0x0) data->has_fan4 = 1; - /* THERM configuration is more complex on the ADT7476 and ADT7490, - because 2 different pins (TACH4 and +2.5 Vin) can be used for - this function */ + /* + * THERM configuration is more complex on the ADT7476 and ADT7490, + * because 2 different pins (TACH4 and +2.5 Vin) can be used for + * this function + */ if (id->driver_data == adt7490) { if ((data->config4 & CONFIG4_PINFUNC) == 0x1 && !(config3 & CONFIG3_THERM)) @@ -1294,8 +1314,10 @@ static int adt7475_probe(struct i2c_client *client, data->has_voltage |= (1 << 0); /* in0 */ } - /* On the ADT7476, the +12V input pin may instead be used as VID5, - and VID pins may alternatively be used as GPIO */ + /* + * On the ADT7476, the +12V input pin may instead be used as VID5, + * and VID pins may alternatively be used as GPIO + */ if (id->driver_data == adt7476) { u8 vid = adt7475_read(REG_VID); if (!(vid & VID_VIDSEL)) @@ -1314,14 +1336,16 @@ static int adt7475_probe(struct i2c_client *client, } data->bypass_attn &= data->has_voltage; - /* Call adt7475_read_pwm for all pwm's as this will reprogram any - pwm's which are disabled to manual mode with 0% duty cycle */ + /* + * Call adt7475_read_pwm for all pwm's as this will reprogram any + * pwm's which are disabled to manual mode with 0% duty cycle + */ for (i = 0; i < ADT7475_PWM_COUNT; i++) adt7475_read_pwm(client, i); ret = sysfs_create_group(&client->dev.kobj, &adt7475_attr_group); if (ret) - goto efree; + return ret; /* Features that can be disabled individually */ if (data->has_fan4) { @@ -1387,8 +1411,6 @@ static int adt7475_probe(struct i2c_client *client, eremove: adt7475_remove_files(client, data); -efree: - kfree(data); return ret; } @@ -1398,7 +1420,6 @@ static int adt7475_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); adt7475_remove_files(client, data); - kfree(data); return 0; } @@ -1431,8 +1452,10 @@ static void adt7475_read_pwm(struct i2c_client *client, int index) data->pwm[CONTROL][index] = adt7475_read(PWM_CONFIG_REG(index)); - /* Figure out the internal value for pwmctrl and pwmchan - based on the current settings */ + /* + * Figure out the internal value for pwmctrl and pwmchan + * based on the current settings + */ v = (data->pwm[CONTROL][index] >> 5) & 7; if (v == 3) @@ -1440,10 +1463,11 @@ static void adt7475_read_pwm(struct i2c_client *client, int index) else if (v == 7) data->pwmctl[index] = 1; else if (v == 4) { - /* The fan is disabled - we don't want to - support that, so change to manual mode and - set the duty cycle to 0 instead - */ + /* + * The fan is disabled - we don't want to + * support that, so change to manual mode and + * set the duty cycle to 0 instead + */ data->pwm[INPUT][index] = 0; data->pwm[CONTROL][index] &= ~0xE0; data->pwm[CONTROL][index] |= (7 << 5); @@ -1600,19 +1624,8 @@ static struct adt7475_data *adt7475_update_device(struct device *dev) return data; } -static int __init sensors_adt7475_init(void) -{ - return i2c_add_driver(&adt7475_driver); -} - -static void __exit sensors_adt7475_exit(void) -{ - i2c_del_driver(&adt7475_driver); -} +module_i2c_driver(adt7475_driver); MODULE_AUTHOR("Advanced Micro Devices, Inc"); MODULE_DESCRIPTION("adt7475 driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_adt7475_init); -module_exit(sensors_adt7475_exit); diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c new file mode 100644 index 00000000000..98141f48316 --- /dev/null +++ b/drivers/hwmon/adt7x10.c @@ -0,0 +1,511 @@ +/* + * adt7x10.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * This driver handles the ADT7410 and compatible digital temperature sensors. + * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22 + * based on lm75.c by Frodo Looijaard <frodol@dds.nl> + * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/interrupt.h> + +#include "adt7x10.h" + +/* + * ADT7X10 status + */ +#define ADT7X10_STAT_T_LOW (1 << 4) +#define ADT7X10_STAT_T_HIGH (1 << 5) +#define ADT7X10_STAT_T_CRIT (1 << 6) +#define ADT7X10_STAT_NOT_RDY (1 << 7) + +/* + * ADT7X10 config + */ +#define ADT7X10_FAULT_QUEUE_MASK (1 << 0 | 1 << 1) +#define ADT7X10_CT_POLARITY (1 << 2) +#define ADT7X10_INT_POLARITY (1 << 3) +#define ADT7X10_EVENT_MODE (1 << 4) +#define ADT7X10_MODE_MASK (1 << 5 | 1 << 6) +#define ADT7X10_FULL (0 << 5 | 0 << 6) +#define ADT7X10_PD (1 << 5 | 1 << 6) +#define ADT7X10_RESOLUTION (1 << 7) + +/* + * ADT7X10 masks + */ +#define ADT7X10_T13_VALUE_MASK 0xFFF8 +#define ADT7X10_T_HYST_MASK 0xF + +/* straight from the datasheet */ +#define ADT7X10_TEMP_MIN (-55000) +#define ADT7X10_TEMP_MAX 150000 + +/* Each client has this additional data */ +struct adt7x10_data { + const struct adt7x10_ops *ops; + const char *name; + struct device *hwmon_dev; + struct mutex update_lock; + u8 config; + u8 oldconfig; + bool valid; /* true if registers valid */ + unsigned long last_updated; /* In jiffies */ + s16 temp[4]; /* Register values, + 0 = input + 1 = high + 2 = low + 3 = critical */ + u8 hyst; /* hysteresis offset */ +}; + +static int adt7x10_read_byte(struct device *dev, u8 reg) +{ + struct adt7x10_data *d = dev_get_drvdata(dev); + return d->ops->read_byte(dev, reg); +} + +static int adt7x10_write_byte(struct device *dev, u8 reg, u8 data) +{ + struct adt7x10_data *d = dev_get_drvdata(dev); + return d->ops->write_byte(dev, reg, data); +} + +static int adt7x10_read_word(struct device *dev, u8 reg) +{ + struct adt7x10_data *d = dev_get_drvdata(dev); + return d->ops->read_word(dev, reg); +} + +static int adt7x10_write_word(struct device *dev, u8 reg, u16 data) +{ + struct adt7x10_data *d = dev_get_drvdata(dev); + return d->ops->write_word(dev, reg, data); +} + +static const u8 ADT7X10_REG_TEMP[4] = { + ADT7X10_TEMPERATURE, /* input */ + ADT7X10_T_ALARM_HIGH, /* high */ + ADT7X10_T_ALARM_LOW, /* low */ + ADT7X10_T_CRIT, /* critical */ +}; + +static irqreturn_t adt7x10_irq_handler(int irq, void *private) +{ + struct device *dev = private; + int status; + + status = adt7x10_read_byte(dev, ADT7X10_STATUS); + if (status < 0) + return IRQ_HANDLED; + + if (status & ADT7X10_STAT_T_HIGH) + sysfs_notify(&dev->kobj, NULL, "temp1_max_alarm"); + if (status & ADT7X10_STAT_T_LOW) + sysfs_notify(&dev->kobj, NULL, "temp1_min_alarm"); + if (status & ADT7X10_STAT_T_CRIT) + sysfs_notify(&dev->kobj, NULL, "temp1_crit_alarm"); + + return IRQ_HANDLED; +} + +static int adt7x10_temp_ready(struct device *dev) +{ + int i, status; + + for (i = 0; i < 6; i++) { + status = adt7x10_read_byte(dev, ADT7X10_STATUS); + if (status < 0) + return status; + if (!(status & ADT7X10_STAT_NOT_RDY)) + return 0; + msleep(60); + } + return -ETIMEDOUT; +} + +static int adt7x10_update_temp(struct device *dev) +{ + struct adt7x10_data *data = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int temp; + + dev_dbg(dev, "Starting update\n"); + + ret = adt7x10_temp_ready(dev); /* check for new value */ + if (ret) + goto abort; + + temp = adt7x10_read_word(dev, ADT7X10_REG_TEMP[0]); + if (temp < 0) { + ret = temp; + dev_dbg(dev, "Failed to read value: reg %d, error %d\n", + ADT7X10_REG_TEMP[0], ret); + goto abort; + } + data->temp[0] = temp; + data->last_updated = jiffies; + data->valid = true; + } + +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +static int adt7x10_fill_cache(struct device *dev) +{ + struct adt7x10_data *data = dev_get_drvdata(dev); + int ret; + int i; + + for (i = 1; i < ARRAY_SIZE(data->temp); i++) { + ret = adt7x10_read_word(dev, ADT7X10_REG_TEMP[i]); + if (ret < 0) { + dev_dbg(dev, "Failed to read value: reg %d, error %d\n", + ADT7X10_REG_TEMP[i], ret); + return ret; + } + data->temp[i] = ret; + } + + ret = adt7x10_read_byte(dev, ADT7X10_T_HYST); + if (ret < 0) { + dev_dbg(dev, "Failed to read value: reg %d, error %d\n", + ADT7X10_T_HYST, ret); + return ret; + } + data->hyst = ret; + + return 0; +} + +static s16 ADT7X10_TEMP_TO_REG(long temp) +{ + return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7X10_TEMP_MIN, + ADT7X10_TEMP_MAX) * 128, 1000); +} + +static int ADT7X10_REG_TO_TEMP(struct adt7x10_data *data, s16 reg) +{ + /* in 13 bit mode, bits 0-2 are status flags - mask them out */ + if (!(data->config & ADT7X10_RESOLUTION)) + reg &= ADT7X10_T13_VALUE_MASK; + /* + * temperature is stored in twos complement format, in steps of + * 1/128°C + */ + return DIV_ROUND_CLOSEST(reg * 1000, 128); +} + +/*-----------------------------------------------------------------------*/ + +/* sysfs attributes for hwmon */ + +static ssize_t adt7x10_show_temp(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct adt7x10_data *data = dev_get_drvdata(dev); + + + if (attr->index == 0) { + int ret; + + ret = adt7x10_update_temp(dev); + if (ret) + return ret; + } + + return sprintf(buf, "%d\n", ADT7X10_REG_TO_TEMP(data, + data->temp[attr->index])); +} + +static ssize_t adt7x10_set_temp(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct adt7x10_data *data = dev_get_drvdata(dev); + int nr = attr->index; + long temp; + int ret; + + ret = kstrtol(buf, 10, &temp); + if (ret) + return ret; + + mutex_lock(&data->update_lock); + data->temp[nr] = ADT7X10_TEMP_TO_REG(temp); + ret = adt7x10_write_word(dev, ADT7X10_REG_TEMP[nr], data->temp[nr]); + if (ret) + count = ret; + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t adt7x10_show_t_hyst(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct adt7x10_data *data = dev_get_drvdata(dev); + int nr = attr->index; + int hyst; + + hyst = (data->hyst & ADT7X10_T_HYST_MASK) * 1000; + + /* + * hysteresis is stored as a 4 bit offset in the device, convert it + * to an absolute value + */ + if (nr == 2) /* min has positive offset, others have negative */ + hyst = -hyst; + return sprintf(buf, "%d\n", + ADT7X10_REG_TO_TEMP(data, data->temp[nr]) - hyst); +} + +static ssize_t adt7x10_set_t_hyst(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct adt7x10_data *data = dev_get_drvdata(dev); + int limit, ret; + long hyst; + + ret = kstrtol(buf, 10, &hyst); + if (ret) + return ret; + /* convert absolute hysteresis value to a 4 bit delta value */ + limit = ADT7X10_REG_TO_TEMP(data, data->temp[1]); + hyst = clamp_val(hyst, ADT7X10_TEMP_MIN, ADT7X10_TEMP_MAX); + data->hyst = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000), + 0, ADT7X10_T_HYST_MASK); + ret = adt7x10_write_byte(dev, ADT7X10_T_HYST, data->hyst); + if (ret) + return ret; + + return count; +} + +static ssize_t adt7x10_show_alarm(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int ret; + + ret = adt7x10_read_byte(dev, ADT7X10_STATUS); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", !!(ret & attr->index)); +} + +static ssize_t adt7x10_show_name(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct adt7x10_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", data->name); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7x10_show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, + adt7x10_show_temp, adt7x10_set_temp, 1); +static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, + adt7x10_show_temp, adt7x10_set_temp, 2); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, + adt7x10_show_temp, adt7x10_set_temp, 3); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, + adt7x10_show_t_hyst, adt7x10_set_t_hyst, 1); +static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO, + adt7x10_show_t_hyst, NULL, 2); +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, + adt7x10_show_t_hyst, NULL, 3); +static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, adt7x10_show_alarm, + NULL, ADT7X10_STAT_T_LOW); +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adt7x10_show_alarm, + NULL, ADT7X10_STAT_T_HIGH); +static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, adt7x10_show_alarm, + NULL, ADT7X10_STAT_T_CRIT); +static DEVICE_ATTR(name, S_IRUGO, adt7x10_show_name, NULL); + +static struct attribute *adt7x10_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_min_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group adt7x10_group = { + .attrs = adt7x10_attributes, +}; + +int adt7x10_probe(struct device *dev, const char *name, int irq, + const struct adt7x10_ops *ops) +{ + struct adt7x10_data *data; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->ops = ops; + data->name = name; + + dev_set_drvdata(dev, data); + mutex_init(&data->update_lock); + + /* configure as specified */ + ret = adt7x10_read_byte(dev, ADT7X10_CONFIG); + if (ret < 0) { + dev_dbg(dev, "Can't read config? %d\n", ret); + return ret; + } + data->oldconfig = ret; + + /* + * Set to 16 bit resolution, continous conversion and comparator mode. + */ + data->config = data->oldconfig; + data->config &= ~(ADT7X10_MODE_MASK | ADT7X10_CT_POLARITY | + ADT7X10_INT_POLARITY); + data->config |= ADT7X10_FULL | ADT7X10_RESOLUTION | ADT7X10_EVENT_MODE; + + if (data->config != data->oldconfig) { + ret = adt7x10_write_byte(dev, ADT7X10_CONFIG, data->config); + if (ret) + return ret; + } + dev_dbg(dev, "Config %02x\n", data->config); + + ret = adt7x10_fill_cache(dev); + if (ret) + goto exit_restore; + + /* Register sysfs hooks */ + ret = sysfs_create_group(&dev->kobj, &adt7x10_group); + if (ret) + goto exit_restore; + + /* + * The I2C device will already have it's own 'name' attribute, but for + * the SPI device we need to register it. name will only be non NULL if + * the device doesn't register the 'name' attribute on its own. + */ + if (name) { + ret = device_create_file(dev, &dev_attr_name); + if (ret) + goto exit_remove; + } + + data->hwmon_dev = hwmon_device_register(dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + goto exit_remove_name; + } + + if (irq > 0) { + ret = request_threaded_irq(irq, NULL, adt7x10_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(dev), dev); + if (ret) + goto exit_hwmon_device_unregister; + } + + return 0; + +exit_hwmon_device_unregister: + hwmon_device_unregister(data->hwmon_dev); +exit_remove_name: + if (name) + device_remove_file(dev, &dev_attr_name); +exit_remove: + sysfs_remove_group(&dev->kobj, &adt7x10_group); +exit_restore: + adt7x10_write_byte(dev, ADT7X10_CONFIG, data->oldconfig); + return ret; +} +EXPORT_SYMBOL_GPL(adt7x10_probe); + +int adt7x10_remove(struct device *dev, int irq) +{ + struct adt7x10_data *data = dev_get_drvdata(dev); + + if (irq > 0) + free_irq(irq, dev); + + hwmon_device_unregister(data->hwmon_dev); + if (data->name) + device_remove_file(dev, &dev_attr_name); + sysfs_remove_group(&dev->kobj, &adt7x10_group); + if (data->oldconfig != data->config) + adt7x10_write_byte(dev, ADT7X10_CONFIG, data->oldconfig); + return 0; +} +EXPORT_SYMBOL_GPL(adt7x10_remove); + +#ifdef CONFIG_PM_SLEEP + +static int adt7x10_suspend(struct device *dev) +{ + struct adt7x10_data *data = dev_get_drvdata(dev); + + return adt7x10_write_byte(dev, ADT7X10_CONFIG, + data->config | ADT7X10_PD); +} + +static int adt7x10_resume(struct device *dev) +{ + struct adt7x10_data *data = dev_get_drvdata(dev); + + return adt7x10_write_byte(dev, ADT7X10_CONFIG, data->config); +} + +SIMPLE_DEV_PM_OPS(adt7x10_dev_pm_ops, adt7x10_suspend, adt7x10_resume); +EXPORT_SYMBOL_GPL(adt7x10_dev_pm_ops); + +#endif /* CONFIG_PM_SLEEP */ + +MODULE_AUTHOR("Hartmut Knaack"); +MODULE_DESCRIPTION("ADT7410/ADT7420, ADT7310/ADT7320 common code"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h new file mode 100644 index 00000000000..d491c698529 --- /dev/null +++ b/drivers/hwmon/adt7x10.h @@ -0,0 +1,37 @@ +#ifndef __HWMON_ADT7X10_H__ +#define __HWMON_ADT7X10_H__ + +#include <linux/types.h> +#include <linux/pm.h> + +/* ADT7410 registers definition */ +#define ADT7X10_TEMPERATURE 0 +#define ADT7X10_STATUS 2 +#define ADT7X10_CONFIG 3 +#define ADT7X10_T_ALARM_HIGH 4 +#define ADT7X10_T_ALARM_LOW 6 +#define ADT7X10_T_CRIT 8 +#define ADT7X10_T_HYST 0xA +#define ADT7X10_ID 0xB + +struct device; + +struct adt7x10_ops { + int (*read_byte)(struct device *, u8 reg); + int (*write_byte)(struct device *, u8 reg, u8 data); + int (*read_word)(struct device *, u8 reg); + int (*write_word)(struct device *, u8 reg, u16 data); +}; + +int adt7x10_probe(struct device *dev, const char *name, int irq, + const struct adt7x10_ops *ops); +int adt7x10_remove(struct device *dev, int irq); + +#ifdef CONFIG_PM_SLEEP +extern const struct dev_pm_ops adt7x10_dev_pm_ops; +#define ADT7X10_DEV_PM_OPS (&adt7x10_dev_pm_ops) +#else +#define ADT7X10_DEV_PM_OPS NULL +#endif + +#endif diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index fa9708c2d72..9f2be3dd28f 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -1,25 +1,25 @@ /* - amc6821.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (C) 2009 T. Mertelj <tomaz.mertelj@guest.arnes.si> - - Based on max6650.c: - Copyright (C) 2007 Hans J. Koch <hjk@linutronix.de> - - 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. -*/ + * amc6821.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (C) 2009 T. Mertelj <tomaz.mertelj@guest.arnes.si> + * + * Based on max6650.c: + * Copyright (C) 2007 Hans J. Koch <hjk@hansjkoch.de> + * + * 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> /* Needed for KERN_INFO */ @@ -47,7 +47,7 @@ static const unsigned short normal_i2c[] = {0x18, 0x19, 0x1a, 0x2c, 0x2d, 0x2e, * Insmod parameters */ -static int pwminv = 0; /*Inverted PWM output. */ +static int pwminv; /*Inverted PWM output. */ module_param(pwminv, int, S_IRUGO); static int init = 1; /*Power-on initialization.*/ @@ -188,7 +188,7 @@ static struct i2c_driver amc6821_driver = { /* * Client data (each client gets its own) - */ + */ struct amc6821_data { struct device *hwmon_dev; @@ -238,10 +238,10 @@ static ssize_t set_temp( int ix = to_sensor_dev_attr(attr)->index; long val; - int ret = strict_strtol(buf, 10, &val); + int ret = kstrtol(buf, 10, &val); if (ret) return ret; - val = SENSORS_LIMIT(val / 1000, -128, 127); + val = clamp_val(val / 1000, -128, 127); mutex_lock(&data->update_lock); data->temp[ix] = val; @@ -327,12 +327,12 @@ static ssize_t set_pwm1( struct i2c_client *client = to_i2c_client(dev); struct amc6821_data *data = i2c_get_clientdata(client); long val; - int ret = strict_strtol(buf, 10, &val); + int ret = kstrtol(buf, 10, &val); if (ret) return ret; mutex_lock(&data->update_lock); - data->pwm1 = SENSORS_LIMIT(val , 0, 255); + data->pwm1 = clamp_val(val , 0, 255); i2c_smbus_write_byte_data(client, AMC6821_REG_DCY, data->pwm1); mutex_unlock(&data->update_lock); return count; @@ -356,7 +356,7 @@ static ssize_t set_pwm1_enable( struct i2c_client *client = to_i2c_client(dev); struct amc6821_data *data = i2c_get_clientdata(client); long val; - int config = strict_strtol(buf, 10, &val); + int config = kstrtol(buf, 10, &val); if (config) return config; @@ -364,7 +364,7 @@ static ssize_t set_pwm1_enable( if (config < 0) { dev_err(&client->dev, "Error reading configuration register, aborting.\n"); - return -EIO; + return config; } switch (val) { @@ -416,11 +416,9 @@ static ssize_t get_temp_auto_point_temp( case 1: return sprintf(buf, "%d\n", data->temp1_auto_point_temp[ix] * 1000); - break; case 2: return sprintf(buf, "%d\n", data->temp2_auto_point_temp[ix] * 1000); - break; default: dev_dbg(dev, "Unknown attr->nr (%d).\n", nr); return -EINVAL; @@ -477,7 +475,7 @@ static ssize_t set_temp_auto_point_temp( u8 reg; int dpwm; long val; - int ret = strict_strtol(buf, 10, &val); + int ret = kstrtol(buf, 10, &val); if (ret) return ret; @@ -499,11 +497,11 @@ static ssize_t set_temp_auto_point_temp( mutex_lock(&data->update_lock); switch (ix) { case 0: - ptemp[0] = SENSORS_LIMIT(val / 1000, 0, - data->temp1_auto_point_temp[1]); - ptemp[0] = SENSORS_LIMIT(ptemp[0], 0, - data->temp2_auto_point_temp[1]); - ptemp[0] = SENSORS_LIMIT(ptemp[0], 0, 63); + ptemp[0] = clamp_val(val / 1000, 0, + data->temp1_auto_point_temp[1]); + ptemp[0] = clamp_val(ptemp[0], 0, + data->temp2_auto_point_temp[1]); + ptemp[0] = clamp_val(ptemp[0], 0, 63); if (i2c_smbus_write_byte_data( client, AMC6821_REG_PSV_TEMP, @@ -513,22 +511,13 @@ static ssize_t set_temp_auto_point_temp( count = -EIO; } goto EXIT; - break; case 1: - ptemp[1] = SENSORS_LIMIT( - val / 1000, - (ptemp[0] & 0x7C) + 4, - 124); + ptemp[1] = clamp_val(val / 1000, (ptemp[0] & 0x7C) + 4, 124); ptemp[1] &= 0x7C; - ptemp[2] = SENSORS_LIMIT( - ptemp[2], ptemp[1] + 1, - 255); + ptemp[2] = clamp_val(ptemp[2], ptemp[1] + 1, 255); break; case 2: - ptemp[2] = SENSORS_LIMIT( - val / 1000, - ptemp[1]+1, - 255); + ptemp[2] = clamp_val(val / 1000, ptemp[1]+1, 255); break; default: dev_dbg(dev, "Unknown attr->index (%d).\n", ix); @@ -556,12 +545,12 @@ static ssize_t set_pwm1_auto_point_pwm( struct amc6821_data *data = i2c_get_clientdata(client); int dpwm; long val; - int ret = strict_strtol(buf, 10, &val); + int ret = kstrtol(buf, 10, &val); if (ret) return ret; mutex_lock(&data->update_lock); - data->pwm1_auto_point_pwm[1] = SENSORS_LIMIT(val, 0, 254); + data->pwm1_auto_point_pwm[1] = clamp_val(val, 0, 254); if (i2c_smbus_write_byte_data(client, AMC6821_REG_DCY_LOW_TEMP, data->pwm1_auto_point_pwm[1])) { dev_err(&client->dev, "Register write error, aborting.\n"); @@ -623,13 +612,13 @@ static ssize_t set_fan( struct amc6821_data *data = i2c_get_clientdata(client); long val; int ix = to_sensor_dev_attr(attr)->index; - int ret = strict_strtol(buf, 10, &val); + int ret = kstrtol(buf, 10, &val); if (ret) return ret; val = 1 > val ? 0xFFFF : 6000000/val; mutex_lock(&data->update_lock); - data->fan[ix] = (u16) SENSORS_LIMIT(val, 1, 0xFFFF); + data->fan[ix] = (u16) clamp_val(val, 1, 0xFFFF); if (i2c_smbus_write_byte_data(client, fan_reg_low[ix], data->fan[ix] & 0xFF)) { dev_err(&client->dev, "Register write error, aborting.\n"); @@ -665,7 +654,7 @@ static ssize_t set_fan1_div( struct i2c_client *client = to_i2c_client(dev); struct amc6821_data *data = i2c_get_clientdata(client); long val; - int config = strict_strtol(buf, 10, &val); + int config = kstrtol(buf, 10, &val); if (config) return config; @@ -673,7 +662,7 @@ static ssize_t set_fan1_div( if (config < 0) { dev_err(&client->dev, "Error reading configuration register, aborting.\n"); - return -EIO; + return config; } mutex_lock(&data->update_lock); switch (val) { @@ -715,7 +704,7 @@ static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, get_temp_alarm, NULL, IDX_TEMP1_MAX); static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, get_temp_alarm, NULL, IDX_TEMP1_CRIT); -static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO | S_IWUSR, +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, get_temp, NULL, IDX_TEMP2_INPUT); static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO | S_IWUSR, get_temp, set_temp, IDX_TEMP2_MIN); @@ -836,8 +825,10 @@ static int amc6821_detect( return -ENODEV; } - /* Bit 7 of the address register is ignored, so we can check the - ID registers again */ + /* + * Bit 7 of the address register is ignored, so we can check the + * ID registers again + */ dev_id = i2c_smbus_read_byte_data(client, 0x80 | AMC6821_REG_DEV_ID); comp_id = i2c_smbus_read_byte_data(client, 0x80 | AMC6821_REG_COMP_ID); if (dev_id != 0x21 || comp_id != 0x49) { @@ -860,12 +851,10 @@ static int amc6821_probe( struct amc6821_data *data; int err; - data = kzalloc(sizeof(struct amc6821_data), GFP_KERNEL); - if (!data) { - dev_err(&client->dev, "out of memory.\n"); + data = devm_kzalloc(&client->dev, sizeof(struct amc6821_data), + GFP_KERNEL); + if (!data) return -ENOMEM; - } - i2c_set_clientdata(client, data); mutex_init(&data->update_lock); @@ -875,11 +864,11 @@ static int amc6821_probe( */ err = amc6821_init_client(client); if (err) - goto err_free; + return err; err = sysfs_create_group(&client->dev.kobj, &amc6821_attr_grp); if (err) - goto err_free; + return err; data->hwmon_dev = hwmon_device_register(&client->dev); if (!IS_ERR(data->hwmon_dev)) @@ -888,8 +877,6 @@ static int amc6821_probe( err = PTR_ERR(data->hwmon_dev); dev_err(&client->dev, "error registering hwmon device.\n"); sysfs_remove_group(&client->dev.kobj, &amc6821_attr_grp); -err_free: - kfree(data); return err; } @@ -900,8 +887,6 @@ static int amc6821_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &amc6821_attr_grp); - kfree(data); - return 0; } @@ -1080,9 +1065,10 @@ static struct amc6821_data *amc6821_update_device(struct device *dev) data->pwm1_auto_channels_temp = 3; data->pwm1_enable = 3; break; - case 1: /*semi-open loop: software sets rpm, chip controls pwm1, - *currently not implemented - */ + case 1: /* + * semi-open loop: software sets rpm, chip controls + * pwm1, currently not implemented + */ data->pwm1_auto_channels_temp = 0; data->pwm1_enable = 0; break; @@ -1095,20 +1081,7 @@ static struct amc6821_data *amc6821_update_device(struct device *dev) return data; } - -static int __init amc6821_init(void) -{ - return i2c_add_driver(&amc6821_driver); -} - -static void __exit amc6821_exit(void) -{ - i2c_del_driver(&amc6821_driver); -} - -module_init(amc6821_init); -module_exit(amc6821_exit); - +module_i2c_driver(amc6821_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("T. Mertelj <tomaz.mertelj@guest.arnes.si>"); 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 6c9ace1b76f..00000000000 --- a/drivers/hwmon/ams/ams-core.c +++ /dev/null @@ -1,247 +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_exit(void) -{ - /* Remove input device */ - ams_input_exit(); - - /* Remove attributes */ - device_remove_file(&ams_info.of_dev->dev, &dev_attr_current); - - /* Shut down implementation */ - ams_info.exit(); - - /* 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); -} - -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 2cbf8a6506c..00000000000 --- a/drivers/hwmon/ams/ams-i2c.c +++ /dev/null @@ -1,275 +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) { - /* 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 fb18b3d3162..00000000000 --- a/drivers/hwmon/ams/ams-pmu.c +++ /dev/null @@ -1,199 +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) -{ - /* 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 5ed387b0bd9..00000000000 --- a/drivers/hwmon/ams/ams.h +++ /dev/null @@ -1,69 +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 of_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 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/applesmc.c b/drivers/hwmon/applesmc.c index c1605b528e8..3288f13d2d8 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -4,10 +4,11 @@ * computers. * * Copyright (C) 2007 Nicolas Boichat <nicolas@boichat.ch> + * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se> * * Based on hdaps.c driver: * Copyright (C) 2005 Robert Love <rml@novell.com> - * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com> + * Copyright (C) 2005 Jesper Juhl <jj@chaosbits.net> * * Fan control based on smcFanControl: * Copyright (C) 2006 Hendrik Holtmann <holtmann@mac.com> @@ -26,10 +27,13 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/input-polldev.h> #include <linux/kernel.h> +#include <linux/slab.h> #include <linux/module.h> #include <linux/timer.h> #include <linux/dmi.h> @@ -39,6 +43,7 @@ #include <linux/leds.h> #include <linux/hwmon.h> #include <linux/workqueue.h> +#include <linux/err.h> /* data port used by Apple SMC */ #define APPLESMC_DATA_PORT 0x300 @@ -49,10 +54,11 @@ #define APPLESMC_MAX_DATA_LENGTH 32 -#define APPLESMC_MIN_WAIT 0x0040 -#define APPLESMC_MAX_WAIT 0x8000 +/* wait up to 128 ms for a status change. */ +#define APPLESMC_MIN_WAIT 0x0010 +#define APPLESMC_RETRY_WAIT 0x0100 +#define APPLESMC_MAX_WAIT 0x20000 -#define APPLESMC_STATUS_MASK 0x0f #define APPLESMC_READ_CMD 0x10 #define APPLESMC_WRITE_CMD 0x11 #define APPLESMC_GET_KEY_BY_INDEX_CMD 0x12 @@ -73,84 +79,17 @@ #define FANS_COUNT "FNum" /* r-o ui8 */ #define FANS_MANUAL "FS! " /* r-w ui16 */ -#define FAN_ACTUAL_SPEED "F0Ac" /* r-o fpe2 (2 bytes) */ -#define FAN_MIN_SPEED "F0Mn" /* r-o fpe2 (2 bytes) */ -#define FAN_MAX_SPEED "F0Mx" /* r-o fpe2 (2 bytes) */ -#define FAN_SAFE_SPEED "F0Sf" /* r-o fpe2 (2 bytes) */ -#define FAN_TARGET_SPEED "F0Tg" /* r-w fpe2 (2 bytes) */ -#define FAN_POSITION "F0ID" /* r-o char[16] */ +#define FAN_ID_FMT "F%dID" /* r-o char[16] */ -/* - * Temperature sensors keys (sp78 - 2 bytes). - */ -static const char *temperature_sensors_sets[][41] = { -/* Set 0: Macbook Pro */ - { "TA0P", "TB0T", "TC0D", "TC0P", "TG0H", "TG0P", "TG0T", "Th0H", - "Th1H", "Tm0P", "Ts0P", "Ts1P", NULL }, -/* Set 1: Macbook2 set */ - { "TB0T", "TC0D", "TC0P", "TM0P", "TN0P", "TN1P", "TTF0", "Th0H", - "Th0S", "Th1H", NULL }, -/* Set 2: Macbook set */ - { "TB0T", "TC0D", "TC0P", "TM0P", "TN0P", "TN1P", "Th0H", "Th0S", - "Th1H", "Ts0P", NULL }, -/* Set 3: Macmini set */ - { "TC0D", "TC0P", NULL }, -/* Set 4: Mac Pro (2 x Quad-Core) */ - { "TA0P", "TCAG", "TCAH", "TCBG", "TCBH", "TC0C", "TC0D", "TC0P", - "TC1C", "TC1D", "TC2C", "TC2D", "TC3C", "TC3D", "THTG", "TH0P", - "TH1P", "TH2P", "TH3P", "TMAP", "TMAS", "TMBS", "TM0P", "TM0S", - "TM1P", "TM1S", "TM2P", "TM2S", "TM3S", "TM8P", "TM8S", "TM9P", - "TM9S", "TN0H", "TS0C", NULL }, -/* Set 5: iMac */ - { "TC0D", "TA0P", "TG0P", "TG0D", "TG0H", "TH0P", "Tm0P", "TO0P", - "Tp0C", NULL }, -/* Set 6: Macbook3 set */ - { "TB0T", "TC0D", "TC0P", "TM0P", "TN0P", "TTF0", "TW0P", "Th0H", - "Th0S", "Th1H", NULL }, -/* Set 7: Macbook Air */ - { "TB0T", "TB1S", "TB1T", "TB2S", "TB2T", "TC0D", "TC0P", "TCFP", - "TTF0", "TW0P", "Th0H", "Tp0P", "TpFP", "Ts0P", "Ts0S", NULL }, -/* Set 8: Macbook Pro 4,1 (Penryn) */ - { "TB0T", "TC0D", "TC0P", "TG0D", "TG0H", "TTF0", "TW0P", "Th0H", - "Th1H", "Th2H", "Tm0P", "Ts0P", NULL }, -/* Set 9: Macbook Pro 3,1 (Santa Rosa) */ - { "TALP", "TB0T", "TC0D", "TC0P", "TG0D", "TG0H", "TTF0", "TW0P", - "Th0H", "Th1H", "Th2H", "Tm0P", "Ts0P", NULL }, -/* Set 10: iMac 5,1 */ - { "TA0P", "TC0D", "TC0P", "TG0D", "TH0P", "TO0P", "Tm0P", NULL }, -/* Set 11: Macbook 5,1 */ - { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0P", "TN0D", "TN0P", - "TTF0", "Th0H", "Th1H", "ThFH", "Ts0P", "Ts0S", NULL }, -/* Set 12: Macbook Pro 5,1 */ - { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0F", "TC0P", "TG0D", - "TG0F", "TG0H", "TG0P", "TG0T", "TG1H", "TN0D", "TN0P", "TTF0", - "Th2H", "Tm0P", "Ts0P", "Ts0S", NULL }, -/* Set 13: iMac 8,1 */ - { "TA0P", "TC0D", "TC0H", "TC0P", "TG0D", "TG0H", "TG0P", "TH0P", - "TL0P", "TO0P", "TW0P", "Tm0P", "Tp0P", NULL }, -/* Set 14: iMac 6,1 */ - { "TA0P", "TC0D", "TC0H", "TC0P", "TG0D", "TG0H", "TG0P", "TH0P", - "TO0P", "Tp0P", NULL }, -/* Set 15: MacBook Air 2,1 */ - { "TB0T", "TB1S", "TB1T", "TB2S", "TB2T", "TC0D", "TN0D", "TTF0", - "TV0P", "TVFP", "TW0P", "Th0P", "Tp0P", "Tp1P", "TpFP", "Ts0P", - "Ts0S", NULL }, -/* Set 16: Mac Pro 3,1 (2 x Quad-Core) */ - { "TA0P", "TCAG", "TCAH", "TCBG", "TCBH", "TC0C", "TC0D", "TC0P", - "TC1C", "TC1D", "TC2C", "TC2D", "TC3C", "TC3D", "TH0P", "TH1P", - "TH2P", "TH3P", "TMAP", "TMAS", "TMBS", "TM0P", "TM0S", "TM1P", - "TM1S", "TM2P", "TM2S", "TM3S", "TM8P", "TM8S", "TM9P", "TM9S", - "TN0C", "TN0D", "TN0H", "TS0C", "Tp0C", "Tp1C", "Tv0S", "Tv1S", - NULL }, -}; +#define TEMP_SENSOR_TYPE "sp78" /* List of keys used to read/write fan speeds */ -static const char* fan_speed_keys[] = { - FAN_ACTUAL_SPEED, - FAN_MIN_SPEED, - FAN_MAX_SPEED, - FAN_SAFE_SPEED, - FAN_TARGET_SPEED +static const char *const fan_speed_fmt[] = { + "F%dAc", /* actual speed */ + "F%dMn", /* minimum speed (rw) */ + "F%dMx", /* maximum speed */ + "F%dSf", /* safe speed - not all models */ + "F%dTg", /* target speed (manual: rw) */ }; #define INIT_TIMEOUT_MSECS 5000 /* wait up to 5s for device init ... */ @@ -160,18 +99,50 @@ static const char* fan_speed_keys[] = { #define APPLESMC_INPUT_FUZZ 4 /* input event threshold */ #define APPLESMC_INPUT_FLAT 4 -#define SENSOR_X 0 -#define SENSOR_Y 1 -#define SENSOR_Z 2 - -/* Structure to be passed to DMI_MATCH function */ -struct dmi_match_data { -/* Indicates whether this computer has an accelerometer. */ - int accelerometer; -/* Indicates whether this computer has light sensors and keyboard backlight. */ - int light; -/* Indicates which temperature sensors set to use. */ - int temperature_set; +#define to_index(attr) (to_sensor_dev_attr(attr)->index & 0xffff) +#define to_option(attr) (to_sensor_dev_attr(attr)->index >> 16) + +/* Dynamic device node attributes */ +struct applesmc_dev_attr { + struct sensor_device_attribute sda; /* hwmon attributes */ + char name[32]; /* room for node file name */ +}; + +/* Dynamic device node group */ +struct applesmc_node_group { + char *format; /* format string */ + void *show; /* show function */ + void *store; /* store function */ + int option; /* function argument */ + struct applesmc_dev_attr *nodes; /* dynamic node array */ +}; + +/* AppleSMC entry - cached register information */ +struct applesmc_entry { + char key[5]; /* four-letter key code */ + u8 valid; /* set when entry is successfully read once */ + u8 len; /* bounded by APPLESMC_MAX_DATA_LENGTH */ + char type[5]; /* four-letter type code */ + u8 flags; /* 0x10: func; 0x40: write; 0x80: read */ +}; + +/* Register lookup and registers common to all SMCs */ +static struct applesmc_registers { + struct mutex mutex; /* register read/write mutex */ + unsigned int key_count; /* number of SMC registers */ + unsigned int fan_count; /* number of fans */ + unsigned int temp_count; /* number of temperature registers */ + unsigned int temp_begin; /* temperature lower index bound */ + unsigned int temp_end; /* temperature upper index bound */ + unsigned int index_count; /* size of temperature index array */ + int num_light_sensors; /* number of light sensors */ + bool has_accelerometer; /* has motion sensor */ + bool has_key_backlight; /* has keyboard backlight */ + bool init_complete; /* true when fully initialized */ + struct applesmc_entry *cache; /* cached key entries */ + const char **index; /* temperature key index */ +} smcreg = { + .mutex = __MUTEX_INITIALIZER(smcreg.mutex), }; static const int debug; @@ -183,17 +154,6 @@ static u8 backlight_state[2]; static struct device *hwmon_dev; static struct input_polled_dev *applesmc_idev; -/* Indicates whether this computer has an accelerometer. */ -static unsigned int applesmc_accelerometer; - -/* Indicates whether this computer has light sensors and keyboard backlight. */ -static unsigned int applesmc_light; - -/* Indicates which temperature sensors set to use. */ -static unsigned int applesmc_temperature_set; - -static DEFINE_MUTEX(applesmc_lock); - /* * Last index written to key_at_index sysfs file, and value to use for all other * key_at_index_* sysfs files. @@ -203,287 +163,479 @@ static unsigned int key_at_index; static struct workqueue_struct *applesmc_led_wq; /* - * __wait_status - Wait up to 32ms for the status port to get a certain value - * (masked with 0x0f), returning zero if the value is obtained. Callers must + * wait_read - Wait for a byte to appear on SMC port. Callers must * hold applesmc_lock. */ -static int __wait_status(u8 val) +static int wait_read(void) { + u8 status; int us; - - val = val & APPLESMC_STATUS_MASK; - for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { udelay(us); - if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val) { - if (debug) - printk(KERN_DEBUG - "Waited %d us for status %x\n", - 2 * us - APPLESMC_MIN_WAIT, val); + status = inb(APPLESMC_CMD_PORT); + /* read: wait for smc to settle */ + if (status & 0x01) return 0; - } } - printk(KERN_WARNING "applesmc: wait status failed: %x != %x\n", - val, inb(APPLESMC_CMD_PORT)); - + pr_warn("wait_read() fail: 0x%02x\n", status); return -EIO; } /* - * special treatment of command port - on newer macbooks, it seems necessary - * to resend the command byte before polling the status again. Callers must - * hold applesmc_lock. + * send_byte - Write to SMC port, retrying when necessary. Callers + * must hold applesmc_lock. */ -static int send_command(u8 cmd) +static int send_byte(u8 cmd, u16 port) { + u8 status; int us; + + outb(cmd, port); for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { - outb(cmd, APPLESMC_CMD_PORT); udelay(us); - if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 0x0c) + status = inb(APPLESMC_CMD_PORT); + /* write: wait for smc to settle */ + if (status & 0x02) + continue; + /* ready: cmd accepted, return */ + if (status & 0x04) return 0; + /* timeout: give up */ + if (us << 1 == APPLESMC_MAX_WAIT) + break; + /* busy: long wait and resend */ + udelay(APPLESMC_RETRY_WAIT); + outb(cmd, port); } - printk(KERN_WARNING "applesmc: command failed: %x -> %x\n", - cmd, inb(APPLESMC_CMD_PORT)); + + pr_warn("send_byte(0x%02x, 0x%04x) fail: 0x%02x\n", cmd, port, status); return -EIO; } -/* - * applesmc_read_key - reads len bytes from a given key, and put them in buffer. - * Returns zero on success or a negative error on failure. Callers must - * hold applesmc_lock. - */ -static int applesmc_read_key(const char* key, u8* buffer, u8 len) +static int send_command(u8 cmd) +{ + return send_byte(cmd, APPLESMC_CMD_PORT); +} + +static int send_argument(const char *key) { int i; - if (len > APPLESMC_MAX_DATA_LENGTH) { - printk(KERN_ERR "applesmc_read_key: cannot read more than " - "%d bytes\n", APPLESMC_MAX_DATA_LENGTH); - return -EINVAL; - } + for (i = 0; i < 4; i++) + if (send_byte(key[i], APPLESMC_DATA_PORT)) + return -EIO; + return 0; +} - if (send_command(APPLESMC_READ_CMD)) - return -EIO; +static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len) +{ + u8 status, data = 0; + int i; - for (i = 0; i < 4; i++) { - outb(key[i], APPLESMC_DATA_PORT); - if (__wait_status(0x04)) - return -EIO; + if (send_command(cmd) || send_argument(key)) { + pr_warn("%.4s: read arg fail\n", key); + return -EIO; } - if (debug) - printk(KERN_DEBUG "<%s", key); - outb(len, APPLESMC_DATA_PORT); - if (debug) - printk(KERN_DEBUG ">%x", len); + /* This has no effect on newer (2012) SMCs */ + if (send_byte(len, APPLESMC_DATA_PORT)) { + pr_warn("%.4s: read len fail\n", key); + return -EIO; + } for (i = 0; i < len; i++) { - if (__wait_status(0x05)) + if (wait_read()) { + pr_warn("%.4s: read data[%d] fail\n", key, i); return -EIO; + } buffer[i] = inb(APPLESMC_DATA_PORT); - if (debug) - printk(KERN_DEBUG "<%x", buffer[i]); } - if (debug) - printk(KERN_DEBUG "\n"); + + /* Read the data port until bit0 is cleared */ + for (i = 0; i < 16; i++) { + udelay(APPLESMC_MIN_WAIT); + status = inb(APPLESMC_CMD_PORT); + if (!(status & 0x01)) + break; + data = inb(APPLESMC_DATA_PORT); + } + if (i) + pr_warn("flushed %d bytes, last value is: %d\n", i, data); return 0; } -/* - * applesmc_write_key - writes len bytes from buffer to a given key. - * Returns zero on success or a negative error on failure. Callers must - * hold applesmc_lock. - */ -static int applesmc_write_key(const char* key, u8* buffer, u8 len) +static int write_smc(u8 cmd, const char *key, const u8 *buffer, u8 len) { int i; - if (len > APPLESMC_MAX_DATA_LENGTH) { - printk(KERN_ERR "applesmc_write_key: cannot write more than " - "%d bytes\n", APPLESMC_MAX_DATA_LENGTH); - return -EINVAL; + if (send_command(cmd) || send_argument(key)) { + pr_warn("%s: write arg fail\n", key); + return -EIO; } - if (send_command(APPLESMC_WRITE_CMD)) + if (send_byte(len, APPLESMC_DATA_PORT)) { + pr_warn("%.4s: write len fail\n", key); return -EIO; - - for (i = 0; i < 4; i++) { - outb(key[i], APPLESMC_DATA_PORT); - if (__wait_status(0x04)) - return -EIO; } - outb(len, APPLESMC_DATA_PORT); - for (i = 0; i < len; i++) { - if (__wait_status(0x04)) + if (send_byte(buffer[i], APPLESMC_DATA_PORT)) { + pr_warn("%s: write data fail\n", key); return -EIO; - outb(buffer[i], APPLESMC_DATA_PORT); + } } return 0; } +static int read_register_count(unsigned int *count) +{ + __be32 be; + int ret; + + ret = read_smc(APPLESMC_READ_CMD, KEY_COUNT_KEY, (u8 *)&be, 4); + if (ret) + return ret; + + *count = be32_to_cpu(be); + return 0; +} + /* - * applesmc_get_key_at_index - get key at index, and put the result in key - * (char[6]). Returns zero on success or a negative error on failure. Callers - * must hold applesmc_lock. + * Serialized I/O + * + * Returns zero on success or a negative error on failure. + * All functions below are concurrency safe - callers should NOT hold lock. */ -static int applesmc_get_key_at_index(int index, char* key) + +static int applesmc_read_entry(const struct applesmc_entry *entry, + u8 *buf, u8 len) { - int i; - u8 readkey[4]; - readkey[0] = index >> 24; - readkey[1] = index >> 16; - readkey[2] = index >> 8; - readkey[3] = index; + int ret; - if (send_command(APPLESMC_GET_KEY_BY_INDEX_CMD)) - return -EIO; + if (entry->len != len) + return -EINVAL; + mutex_lock(&smcreg.mutex); + ret = read_smc(APPLESMC_READ_CMD, entry->key, buf, len); + mutex_unlock(&smcreg.mutex); - for (i = 0; i < 4; i++) { - outb(readkey[i], APPLESMC_DATA_PORT); - if (__wait_status(0x04)) - return -EIO; + return ret; +} + +static int applesmc_write_entry(const struct applesmc_entry *entry, + const u8 *buf, u8 len) +{ + int ret; + + if (entry->len != len) + return -EINVAL; + mutex_lock(&smcreg.mutex); + ret = write_smc(APPLESMC_WRITE_CMD, entry->key, buf, len); + mutex_unlock(&smcreg.mutex); + return ret; +} + +static const struct applesmc_entry *applesmc_get_entry_by_index(int index) +{ + struct applesmc_entry *cache = &smcreg.cache[index]; + u8 key[4], info[6]; + __be32 be; + int ret = 0; + + if (cache->valid) + return cache; + + mutex_lock(&smcreg.mutex); + + if (cache->valid) + goto out; + be = cpu_to_be32(index); + ret = read_smc(APPLESMC_GET_KEY_BY_INDEX_CMD, (u8 *)&be, key, 4); + if (ret) + goto out; + ret = read_smc(APPLESMC_GET_KEY_TYPE_CMD, key, info, 6); + if (ret) + goto out; + + memcpy(cache->key, key, 4); + cache->len = info[0]; + memcpy(cache->type, &info[1], 4); + cache->flags = info[5]; + cache->valid = 1; + +out: + mutex_unlock(&smcreg.mutex); + if (ret) + return ERR_PTR(ret); + return cache; +} + +static int applesmc_get_lower_bound(unsigned int *lo, const char *key) +{ + int begin = 0, end = smcreg.key_count; + const struct applesmc_entry *entry; + + while (begin != end) { + int middle = begin + (end - begin) / 2; + entry = applesmc_get_entry_by_index(middle); + if (IS_ERR(entry)) { + *lo = 0; + return PTR_ERR(entry); + } + if (strcmp(entry->key, key) < 0) + begin = middle + 1; + else + end = middle; } - outb(4, APPLESMC_DATA_PORT); + *lo = begin; + return 0; +} - for (i = 0; i < 4; i++) { - if (__wait_status(0x05)) - return -EIO; - key[i] = inb(APPLESMC_DATA_PORT); +static int applesmc_get_upper_bound(unsigned int *hi, const char *key) +{ + int begin = 0, end = smcreg.key_count; + const struct applesmc_entry *entry; + + while (begin != end) { + int middle = begin + (end - begin) / 2; + entry = applesmc_get_entry_by_index(middle); + if (IS_ERR(entry)) { + *hi = smcreg.key_count; + return PTR_ERR(entry); + } + if (strcmp(key, entry->key) < 0) + end = middle; + else + begin = middle + 1; } - key[4] = 0; + *hi = begin; return 0; } -/* - * applesmc_get_key_type - get key type, and put the result in type (char[6]). - * Returns zero on success or a negative error on failure. Callers must - * hold applesmc_lock. - */ -static int applesmc_get_key_type(char* key, char* type) +static const struct applesmc_entry *applesmc_get_entry_by_key(const char *key) { - int i; + int begin, end; + int ret; - if (send_command(APPLESMC_GET_KEY_TYPE_CMD)) - return -EIO; + ret = applesmc_get_lower_bound(&begin, key); + if (ret) + return ERR_PTR(ret); + ret = applesmc_get_upper_bound(&end, key); + if (ret) + return ERR_PTR(ret); + if (end - begin != 1) + return ERR_PTR(-EINVAL); - for (i = 0; i < 4; i++) { - outb(key[i], APPLESMC_DATA_PORT); - if (__wait_status(0x04)) - return -EIO; - } + return applesmc_get_entry_by_index(begin); +} - outb(6, APPLESMC_DATA_PORT); +static int applesmc_read_key(const char *key, u8 *buffer, u8 len) +{ + const struct applesmc_entry *entry; - for (i = 0; i < 6; i++) { - if (__wait_status(0x05)) - return -EIO; - type[i] = inb(APPLESMC_DATA_PORT); - } - type[5] = 0; + entry = applesmc_get_entry_by_key(key); + if (IS_ERR(entry)) + return PTR_ERR(entry); + + return applesmc_read_entry(entry, buffer, len); +} + +static int applesmc_write_key(const char *key, const u8 *buffer, u8 len) +{ + const struct applesmc_entry *entry; + + entry = applesmc_get_entry_by_key(key); + if (IS_ERR(entry)) + return PTR_ERR(entry); + return applesmc_write_entry(entry, buffer, len); +} + +static int applesmc_has_key(const char *key, bool *value) +{ + const struct applesmc_entry *entry; + + entry = applesmc_get_entry_by_key(key); + if (IS_ERR(entry) && PTR_ERR(entry) != -EINVAL) + return PTR_ERR(entry); + + *value = !IS_ERR(entry); return 0; } /* - * applesmc_read_motion_sensor - Read motion sensor (X, Y or Z). Callers must - * hold applesmc_lock. + * applesmc_read_s16 - Read 16-bit signed big endian register */ -static int applesmc_read_motion_sensor(int index, s16* value) +static int applesmc_read_s16(const char *key, s16 *value) { u8 buffer[2]; int ret; - switch (index) { - case SENSOR_X: - ret = applesmc_read_key(MOTION_SENSOR_X_KEY, buffer, 2); - break; - case SENSOR_Y: - ret = applesmc_read_key(MOTION_SENSOR_Y_KEY, buffer, 2); - break; - case SENSOR_Z: - ret = applesmc_read_key(MOTION_SENSOR_Z_KEY, buffer, 2); - break; - default: - ret = -EINVAL; - } + ret = applesmc_read_key(key, buffer, 2); + if (ret) + return ret; *value = ((s16)buffer[0] << 8) | buffer[1]; - - return ret; + return 0; } /* - * applesmc_device_init - initialize the accelerometer. Returns zero on success - * and negative error code on failure. Can sleep. + * applesmc_device_init - initialize the accelerometer. Can sleep. */ -static int applesmc_device_init(void) +static void applesmc_device_init(void) { - int total, ret = -ENXIO; + int total; u8 buffer[2]; - if (!applesmc_accelerometer) - return 0; - - mutex_lock(&applesmc_lock); + if (!smcreg.has_accelerometer) + return; for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) { - if (debug) - printk(KERN_DEBUG "applesmc try %d\n", total); if (!applesmc_read_key(MOTION_SENSOR_KEY, buffer, 2) && - (buffer[0] != 0x00 || buffer[1] != 0x00)) { - if (total == INIT_TIMEOUT_MSECS) { - printk(KERN_DEBUG "applesmc: device has" - " already been initialized" - " (0x%02x, 0x%02x).\n", - buffer[0], buffer[1]); - } else { - printk(KERN_DEBUG "applesmc: device" - " successfully initialized" - " (0x%02x, 0x%02x).\n", - buffer[0], buffer[1]); - } - ret = 0; - goto out; - } + (buffer[0] != 0x00 || buffer[1] != 0x00)) + return; buffer[0] = 0xe0; buffer[1] = 0x00; applesmc_write_key(MOTION_SENSOR_KEY, buffer, 2); msleep(INIT_WAIT_MSECS); } - printk(KERN_WARNING "applesmc: failed to init the device\n"); + pr_warn("failed to init the device\n"); +} -out: - mutex_unlock(&applesmc_lock); - return ret; +static int applesmc_init_index(struct applesmc_registers *s) +{ + const struct applesmc_entry *entry; + unsigned int i; + + if (s->index) + return 0; + + s->index = kcalloc(s->temp_count, sizeof(s->index[0]), GFP_KERNEL); + if (!s->index) + return -ENOMEM; + + for (i = s->temp_begin; i < s->temp_end; i++) { + entry = applesmc_get_entry_by_index(i); + if (IS_ERR(entry)) + continue; + if (strcmp(entry->type, TEMP_SENSOR_TYPE)) + continue; + s->index[s->index_count++] = entry->key; + } + + return 0; } /* - * applesmc_get_fan_count - get the number of fans. Callers must NOT hold - * applesmc_lock. + * applesmc_init_smcreg_try - Try to initialize register cache. Idempotent. */ -static int applesmc_get_fan_count(void) +static int applesmc_init_smcreg_try(void) { + struct applesmc_registers *s = &smcreg; + bool left_light_sensor, right_light_sensor; + unsigned int count; + u8 tmp[1]; int ret; - u8 buffer[1]; - mutex_lock(&applesmc_lock); + if (s->init_complete) + return 0; + + ret = read_register_count(&count); + if (ret) + return ret; + + if (s->cache && s->key_count != count) { + pr_warn("key count changed from %d to %d\n", + s->key_count, count); + kfree(s->cache); + s->cache = NULL; + } + s->key_count = count; - ret = applesmc_read_key(FANS_COUNT, buffer, 1); + if (!s->cache) + s->cache = kcalloc(s->key_count, sizeof(*s->cache), GFP_KERNEL); + if (!s->cache) + return -ENOMEM; - mutex_unlock(&applesmc_lock); + ret = applesmc_read_key(FANS_COUNT, tmp, 1); if (ret) return ret; - else - return buffer[0]; + s->fan_count = tmp[0]; + + ret = applesmc_get_lower_bound(&s->temp_begin, "T"); + if (ret) + return ret; + ret = applesmc_get_lower_bound(&s->temp_end, "U"); + if (ret) + return ret; + s->temp_count = s->temp_end - s->temp_begin; + + ret = applesmc_init_index(s); + if (ret) + return ret; + + ret = applesmc_has_key(LIGHT_SENSOR_LEFT_KEY, &left_light_sensor); + if (ret) + return ret; + ret = applesmc_has_key(LIGHT_SENSOR_RIGHT_KEY, &right_light_sensor); + if (ret) + return ret; + ret = applesmc_has_key(MOTION_SENSOR_KEY, &s->has_accelerometer); + if (ret) + return ret; + ret = applesmc_has_key(BACKLIGHT_KEY, &s->has_key_backlight); + if (ret) + return ret; + + s->num_light_sensors = left_light_sensor + right_light_sensor; + s->init_complete = true; + + pr_info("key=%d fan=%d temp=%d index=%d acc=%d lux=%d kbd=%d\n", + s->key_count, s->fan_count, s->temp_count, s->index_count, + s->has_accelerometer, + s->num_light_sensors, + s->has_key_backlight); + + return 0; +} + +static void applesmc_destroy_smcreg(void) +{ + kfree(smcreg.index); + smcreg.index = NULL; + kfree(smcreg.cache); + smcreg.cache = NULL; + smcreg.init_complete = false; +} + +/* + * applesmc_init_smcreg - Initialize register cache. + * + * Retries until initialization is successful, or the operation times out. + * + */ +static int applesmc_init_smcreg(void) +{ + int ms, ret; + + for (ms = 0; ms < INIT_TIMEOUT_MSECS; ms += INIT_WAIT_MSECS) { + ret = applesmc_init_smcreg_try(); + if (!ret) { + if (ms) + pr_info("init_smcreg() took %d ms\n", ms); + return 0; + } + msleep(INIT_WAIT_MSECS); + } + + applesmc_destroy_smcreg(); + + return ret; } /* Device model stuff */ @@ -491,30 +643,27 @@ static int applesmc_probe(struct platform_device *dev) { int ret; - ret = applesmc_device_init(); + ret = applesmc_init_smcreg(); if (ret) return ret; - printk(KERN_INFO "applesmc: device successfully initialized.\n"); + applesmc_device_init(); + return 0; } /* Synchronize device with memorized backlight state */ static int applesmc_pm_resume(struct device *dev) { - mutex_lock(&applesmc_lock); - if (applesmc_light) + if (smcreg.has_key_backlight) applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2); - mutex_unlock(&applesmc_lock); return 0; } /* Reinitialize device on resume from hibernation */ static int applesmc_pm_restore(struct device *dev) { - int ret = applesmc_device_init(); - if (ret) - return ret; + applesmc_device_init(); return applesmc_pm_resume(dev); } @@ -538,8 +687,8 @@ static struct platform_driver applesmc_driver = { */ static void applesmc_calibrate(void) { - applesmc_read_motion_sensor(SENSOR_X, &rest_x); - applesmc_read_motion_sensor(SENSOR_Y, &rest_y); + applesmc_read_s16(MOTION_SENSOR_X_KEY, &rest_x); + applesmc_read_s16(MOTION_SENSOR_Y_KEY, &rest_y); rest_x = -rest_x; } @@ -548,20 +697,15 @@ static void applesmc_idev_poll(struct input_polled_dev *dev) struct input_dev *idev = dev->input; s16 x, y; - mutex_lock(&applesmc_lock); - - if (applesmc_read_motion_sensor(SENSOR_X, &x)) - goto out; - if (applesmc_read_motion_sensor(SENSOR_Y, &y)) - goto out; + if (applesmc_read_s16(MOTION_SENSOR_X_KEY, &x)) + return; + if (applesmc_read_s16(MOTION_SENSOR_Y_KEY, &y)) + return; x = -x; input_report_abs(idev, ABS_X, x - rest_x); input_report_abs(idev, ABS_Y, y - rest_y); input_sync(idev); - -out: - mutex_unlock(&applesmc_lock); } /* Sysfs Files */ @@ -578,20 +722,17 @@ static ssize_t applesmc_position_show(struct device *dev, int ret; s16 x, y, z; - mutex_lock(&applesmc_lock); - - ret = applesmc_read_motion_sensor(SENSOR_X, &x); + ret = applesmc_read_s16(MOTION_SENSOR_X_KEY, &x); if (ret) goto out; - ret = applesmc_read_motion_sensor(SENSOR_Y, &y); + ret = applesmc_read_s16(MOTION_SENSOR_Y_KEY, &y); if (ret) goto out; - ret = applesmc_read_motion_sensor(SENSOR_Z, &z); + ret = applesmc_read_s16(MOTION_SENSOR_Z_KEY, &z); if (ret) goto out; out: - mutex_unlock(&applesmc_lock); if (ret) return ret; else @@ -601,20 +742,20 @@ out: static ssize_t applesmc_light_show(struct device *dev, struct device_attribute *attr, char *sysfsbuf) { + const struct applesmc_entry *entry; static int data_length; int ret; u8 left = 0, right = 0; - u8 buffer[10], query[6]; - - mutex_lock(&applesmc_lock); + u8 buffer[10]; if (!data_length) { - ret = applesmc_get_key_type(LIGHT_SENSOR_LEFT_KEY, query); - if (ret) - goto out; - data_length = clamp_val(query[0], 0, 10); - printk(KERN_INFO "applesmc: light sensor data length set to " - "%d\n", data_length); + entry = applesmc_get_entry_by_key(LIGHT_SENSOR_LEFT_KEY); + if (IS_ERR(entry)) + return PTR_ERR(entry); + if (entry->len > 10) + return -ENXIO; + data_length = entry->len; + pr_info("light sensor data length set to %d\n", data_length); } ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, data_length); @@ -630,36 +771,37 @@ static ssize_t applesmc_light_show(struct device *dev, right = buffer[2]; out: - mutex_unlock(&applesmc_lock); if (ret) return ret; else return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", left, right); } +/* Displays sensor key as label */ +static ssize_t applesmc_show_sensor_label(struct device *dev, + struct device_attribute *devattr, char *sysfsbuf) +{ + const char *key = smcreg.index[to_index(devattr)]; + + return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key); +} + /* Displays degree Celsius * 1000 */ static ssize_t applesmc_show_temperature(struct device *dev, struct device_attribute *devattr, char *sysfsbuf) { + const char *key = smcreg.index[to_index(devattr)]; int ret; - u8 buffer[2]; - unsigned int temp; - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - const char* key = - temperature_sensors_sets[applesmc_temperature_set][attr->index]; - - mutex_lock(&applesmc_lock); - - ret = applesmc_read_key(key, buffer, 2); - temp = buffer[0]*1000; - temp += (buffer[1] >> 6) * 250; - - mutex_unlock(&applesmc_lock); + s16 value; + int temp; + ret = applesmc_read_s16(key, &value); if (ret) return ret; - else - return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", temp); + + temp = 250 * (value >> 6); + + return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", temp); } static ssize_t applesmc_show_fan_speed(struct device *dev, @@ -669,21 +811,12 @@ static ssize_t applesmc_show_fan_speed(struct device *dev, unsigned int speed = 0; char newkey[5]; u8 buffer[2]; - struct sensor_device_attribute_2 *sensor_attr = - to_sensor_dev_attr_2(attr); - newkey[0] = fan_speed_keys[sensor_attr->nr][0]; - newkey[1] = '0' + sensor_attr->index; - newkey[2] = fan_speed_keys[sensor_attr->nr][2]; - newkey[3] = fan_speed_keys[sensor_attr->nr][3]; - newkey[4] = 0; - - mutex_lock(&applesmc_lock); + sprintf(newkey, fan_speed_fmt[to_option(attr)], to_index(attr)); ret = applesmc_read_key(newkey, buffer, 2); speed = ((buffer[0] << 8 | buffer[1]) >> 2); - mutex_unlock(&applesmc_lock); if (ret) return ret; else @@ -695,30 +828,19 @@ static ssize_t applesmc_store_fan_speed(struct device *dev, const char *sysfsbuf, size_t count) { int ret; - u32 speed; + unsigned long speed; char newkey[5]; u8 buffer[2]; - struct sensor_device_attribute_2 *sensor_attr = - to_sensor_dev_attr_2(attr); - speed = simple_strtoul(sysfsbuf, NULL, 10); + if (kstrtoul(sysfsbuf, 10, &speed) < 0 || speed >= 0x4000) + return -EINVAL; /* Bigger than a 14-bit value */ - if (speed > 0x4000) /* Bigger than a 14-bit value */ - return -EINVAL; - - newkey[0] = fan_speed_keys[sensor_attr->nr][0]; - newkey[1] = '0' + sensor_attr->index; - newkey[2] = fan_speed_keys[sensor_attr->nr][2]; - newkey[3] = fan_speed_keys[sensor_attr->nr][3]; - newkey[4] = 0; - - mutex_lock(&applesmc_lock); + sprintf(newkey, fan_speed_fmt[to_option(attr)], to_index(attr)); buffer[0] = (speed >> 6) & 0xff; buffer[1] = (speed << 2) & 0xff; ret = applesmc_write_key(newkey, buffer, 2); - mutex_unlock(&applesmc_lock); if (ret) return ret; else @@ -726,19 +848,15 @@ static ssize_t applesmc_store_fan_speed(struct device *dev, } static ssize_t applesmc_show_fan_manual(struct device *dev, - struct device_attribute *devattr, char *sysfsbuf) + struct device_attribute *attr, char *sysfsbuf) { int ret; u16 manual = 0; u8 buffer[2]; - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - - mutex_lock(&applesmc_lock); ret = applesmc_read_key(FANS_MANUAL, buffer, 2); - manual = ((buffer[0] << 8 | buffer[1]) >> attr->index) & 0x01; + manual = ((buffer[0] << 8 | buffer[1]) >> to_index(attr)) & 0x01; - mutex_unlock(&applesmc_lock); if (ret) return ret; else @@ -746,18 +864,16 @@ static ssize_t applesmc_show_fan_manual(struct device *dev, } static ssize_t applesmc_store_fan_manual(struct device *dev, - struct device_attribute *devattr, + struct device_attribute *attr, const char *sysfsbuf, size_t count) { int ret; u8 buffer[2]; - u32 input; + unsigned long input; u16 val; - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - - input = simple_strtoul(sysfsbuf, NULL, 10); - mutex_lock(&applesmc_lock); + if (kstrtoul(sysfsbuf, 10, &input) < 0) + return -EINVAL; ret = applesmc_read_key(FANS_MANUAL, buffer, 2); val = (buffer[0] << 8 | buffer[1]); @@ -765,9 +881,9 @@ static ssize_t applesmc_store_fan_manual(struct device *dev, goto out; if (input) - val = val | (0x01 << attr->index); + val = val | (0x01 << to_index(attr)); else - val = val & ~(0x01 << attr->index); + val = val & ~(0x01 << to_index(attr)); buffer[0] = (val >> 8) & 0xFF; buffer[1] = val & 0xFF; @@ -775,7 +891,6 @@ static ssize_t applesmc_store_fan_manual(struct device *dev, ret = applesmc_write_key(FANS_MANUAL, buffer, 2); out: - mutex_unlock(&applesmc_lock); if (ret) return ret; else @@ -788,21 +903,12 @@ static ssize_t applesmc_show_fan_position(struct device *dev, int ret; char newkey[5]; u8 buffer[17]; - struct sensor_device_attribute_2 *sensor_attr = - to_sensor_dev_attr_2(attr); - newkey[0] = FAN_POSITION[0]; - newkey[1] = '0' + sensor_attr->index; - newkey[2] = FAN_POSITION[2]; - newkey[3] = FAN_POSITION[3]; - newkey[4] = 0; - - mutex_lock(&applesmc_lock); + sprintf(newkey, FAN_ID_FMT, to_index(attr)); ret = applesmc_read_key(newkey, buffer, 16); buffer[16] = 0; - mutex_unlock(&applesmc_lock); if (ret) return ret; else @@ -818,18 +924,14 @@ static ssize_t applesmc_calibrate_show(struct device *dev, static ssize_t applesmc_calibrate_store(struct device *dev, struct device_attribute *attr, const char *sysfsbuf, size_t count) { - mutex_lock(&applesmc_lock); applesmc_calibrate(); - mutex_unlock(&applesmc_lock); return count; } static void applesmc_backlight_set(struct work_struct *work) { - mutex_lock(&applesmc_lock); applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2); - mutex_unlock(&applesmc_lock); } static DECLARE_WORK(backlight_work, &applesmc_backlight_set); @@ -842,7 +944,7 @@ static void applesmc_brightness_set(struct led_classdev *led_cdev, ret = queue_work(applesmc_led_wq, &backlight_work); if (debug && (!ret)) - printk(KERN_DEBUG "applesmc: work was already on the queue.\n"); + dev_dbg(led_cdev->dev, "work was already on the queue.\n"); } static ssize_t applesmc_key_count_show(struct device *dev, @@ -852,13 +954,10 @@ static ssize_t applesmc_key_count_show(struct device *dev, u8 buffer[4]; u32 count; - mutex_lock(&applesmc_lock); - ret = applesmc_read_key(KEY_COUNT_KEY, buffer, 4); count = ((u32)buffer[0]<<24) + ((u32)buffer[1]<<16) + ((u32)buffer[2]<<8) + buffer[3]; - mutex_unlock(&applesmc_lock); if (ret) return ret; else @@ -868,113 +967,53 @@ static ssize_t applesmc_key_count_show(struct device *dev, static ssize_t applesmc_key_at_index_read_show(struct device *dev, struct device_attribute *attr, char *sysfsbuf) { - char key[5]; - char info[6]; + const struct applesmc_entry *entry; int ret; - mutex_lock(&applesmc_lock); - - ret = applesmc_get_key_at_index(key_at_index, key); - - if (ret || !key[0]) { - mutex_unlock(&applesmc_lock); - - return -EINVAL; - } - - ret = applesmc_get_key_type(key, info); - - if (ret) { - mutex_unlock(&applesmc_lock); - + entry = applesmc_get_entry_by_index(key_at_index); + if (IS_ERR(entry)) + return PTR_ERR(entry); + ret = applesmc_read_entry(entry, sysfsbuf, entry->len); + if (ret) return ret; - } - - /* - * info[0] maximum value (APPLESMC_MAX_DATA_LENGTH) is much lower than - * PAGE_SIZE, so we don't need any checks before writing to sysfsbuf. - */ - ret = applesmc_read_key(key, sysfsbuf, info[0]); - - mutex_unlock(&applesmc_lock); - if (!ret) { - return info[0]; - } else { - return ret; - } + return entry->len; } static ssize_t applesmc_key_at_index_data_length_show(struct device *dev, struct device_attribute *attr, char *sysfsbuf) { - char key[5]; - char info[6]; - int ret; - - mutex_lock(&applesmc_lock); - - ret = applesmc_get_key_at_index(key_at_index, key); + const struct applesmc_entry *entry; - if (ret || !key[0]) { - mutex_unlock(&applesmc_lock); - - return -EINVAL; - } + entry = applesmc_get_entry_by_index(key_at_index); + if (IS_ERR(entry)) + return PTR_ERR(entry); - ret = applesmc_get_key_type(key, info); - - mutex_unlock(&applesmc_lock); - - if (!ret) - return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", info[0]); - else - return ret; + return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", entry->len); } static ssize_t applesmc_key_at_index_type_show(struct device *dev, struct device_attribute *attr, char *sysfsbuf) { - char key[5]; - char info[6]; - int ret; + const struct applesmc_entry *entry; - mutex_lock(&applesmc_lock); + entry = applesmc_get_entry_by_index(key_at_index); + if (IS_ERR(entry)) + return PTR_ERR(entry); - ret = applesmc_get_key_at_index(key_at_index, key); - - if (ret || !key[0]) { - mutex_unlock(&applesmc_lock); - - return -EINVAL; - } - - ret = applesmc_get_key_type(key, info); - - mutex_unlock(&applesmc_lock); - - if (!ret) - return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", info+1); - else - return ret; + return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", entry->type); } static ssize_t applesmc_key_at_index_name_show(struct device *dev, struct device_attribute *attr, char *sysfsbuf) { - char key[5]; - int ret; - - mutex_lock(&applesmc_lock); + const struct applesmc_entry *entry; - ret = applesmc_get_key_at_index(key_at_index, key); + entry = applesmc_get_entry_by_index(key_at_index); + if (IS_ERR(entry)) + return PTR_ERR(entry); - mutex_unlock(&applesmc_lock); - - if (!ret && key[0]) - return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key); - else - return -EINVAL; + return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", entry->key); } static ssize_t applesmc_key_at_index_show(struct device *dev, @@ -986,12 +1025,13 @@ static ssize_t applesmc_key_at_index_show(struct device *dev, static ssize_t applesmc_key_at_index_store(struct device *dev, struct device_attribute *attr, const char *sysfsbuf, size_t count) { - mutex_lock(&applesmc_lock); + unsigned long newkey; - key_at_index = simple_strtoul(sysfsbuf, NULL, 10); - - mutex_unlock(&applesmc_lock); + if (kstrtoul(sysfsbuf, 10, &newkey) < 0 + || newkey >= smcreg.key_count) + return -EINVAL; + key_at_index = newkey; return count; } @@ -1001,259 +1041,102 @@ static struct led_classdev applesmc_backlight = { .brightness_set = applesmc_brightness_set, }; -static DEVICE_ATTR(name, 0444, applesmc_name_show, NULL); - -static DEVICE_ATTR(position, 0444, applesmc_position_show, NULL); -static DEVICE_ATTR(calibrate, 0644, - applesmc_calibrate_show, applesmc_calibrate_store); - -static struct attribute *accelerometer_attributes[] = { - &dev_attr_position.attr, - &dev_attr_calibrate.attr, - NULL +static struct applesmc_node_group info_group[] = { + { "name", applesmc_name_show }, + { "key_count", applesmc_key_count_show }, + { "key_at_index", applesmc_key_at_index_show, applesmc_key_at_index_store }, + { "key_at_index_name", applesmc_key_at_index_name_show }, + { "key_at_index_type", applesmc_key_at_index_type_show }, + { "key_at_index_data_length", applesmc_key_at_index_data_length_show }, + { "key_at_index_data", applesmc_key_at_index_read_show }, + { } }; -static const struct attribute_group accelerometer_attributes_group = - { .attrs = accelerometer_attributes }; - -static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL); - -static DEVICE_ATTR(key_count, 0444, applesmc_key_count_show, NULL); -static DEVICE_ATTR(key_at_index, 0644, - applesmc_key_at_index_show, applesmc_key_at_index_store); -static DEVICE_ATTR(key_at_index_name, 0444, - applesmc_key_at_index_name_show, NULL); -static DEVICE_ATTR(key_at_index_type, 0444, - applesmc_key_at_index_type_show, NULL); -static DEVICE_ATTR(key_at_index_data_length, 0444, - applesmc_key_at_index_data_length_show, NULL); -static DEVICE_ATTR(key_at_index_data, 0444, - applesmc_key_at_index_read_show, NULL); - -static struct attribute *key_enumeration_attributes[] = { - &dev_attr_key_count.attr, - &dev_attr_key_at_index.attr, - &dev_attr_key_at_index_name.attr, - &dev_attr_key_at_index_type.attr, - &dev_attr_key_at_index_data_length.attr, - &dev_attr_key_at_index_data.attr, - NULL +static struct applesmc_node_group accelerometer_group[] = { + { "position", applesmc_position_show }, + { "calibrate", applesmc_calibrate_show, applesmc_calibrate_store }, + { } }; -static const struct attribute_group key_enumeration_group = - { .attrs = key_enumeration_attributes }; - -/* - * Macro defining SENSOR_DEVICE_ATTR for a fan sysfs entries. - * - show actual speed - * - show/store minimum speed - * - show maximum speed - * - show safe speed - * - show/store target speed - * - show/store manual mode - */ -#define sysfs_fan_speeds_offset(offset) \ -static SENSOR_DEVICE_ATTR_2(fan##offset##_input, S_IRUGO, \ - applesmc_show_fan_speed, NULL, 0, offset-1); \ -\ -static SENSOR_DEVICE_ATTR_2(fan##offset##_min, S_IRUGO | S_IWUSR, \ - applesmc_show_fan_speed, applesmc_store_fan_speed, 1, offset-1); \ -\ -static SENSOR_DEVICE_ATTR_2(fan##offset##_max, S_IRUGO, \ - applesmc_show_fan_speed, NULL, 2, offset-1); \ -\ -static SENSOR_DEVICE_ATTR_2(fan##offset##_safe, S_IRUGO, \ - applesmc_show_fan_speed, NULL, 3, offset-1); \ -\ -static SENSOR_DEVICE_ATTR_2(fan##offset##_output, S_IRUGO | S_IWUSR, \ - applesmc_show_fan_speed, applesmc_store_fan_speed, 4, offset-1); \ -\ -static SENSOR_DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \ - applesmc_show_fan_manual, applesmc_store_fan_manual, offset-1); \ -\ -static SENSOR_DEVICE_ATTR(fan##offset##_label, S_IRUGO, \ - applesmc_show_fan_position, NULL, offset-1); \ -\ -static struct attribute *fan##offset##_attributes[] = { \ - &sensor_dev_attr_fan##offset##_input.dev_attr.attr, \ - &sensor_dev_attr_fan##offset##_min.dev_attr.attr, \ - &sensor_dev_attr_fan##offset##_max.dev_attr.attr, \ - &sensor_dev_attr_fan##offset##_safe.dev_attr.attr, \ - &sensor_dev_attr_fan##offset##_output.dev_attr.attr, \ - &sensor_dev_attr_fan##offset##_manual.dev_attr.attr, \ - &sensor_dev_attr_fan##offset##_label.dev_attr.attr, \ - NULL \ +static struct applesmc_node_group light_sensor_group[] = { + { "light", applesmc_light_show }, + { } }; -/* - * Create the needed functions for each fan using the macro defined above - * (4 fans are supported) - */ -sysfs_fan_speeds_offset(1); -sysfs_fan_speeds_offset(2); -sysfs_fan_speeds_offset(3); -sysfs_fan_speeds_offset(4); - -static const struct attribute_group fan_attribute_groups[] = { - { .attrs = fan1_attributes }, - { .attrs = fan2_attributes }, - { .attrs = fan3_attributes }, - { .attrs = fan4_attributes }, +static struct applesmc_node_group fan_group[] = { + { "fan%d_label", applesmc_show_fan_position }, + { "fan%d_input", applesmc_show_fan_speed, NULL, 0 }, + { "fan%d_min", applesmc_show_fan_speed, applesmc_store_fan_speed, 1 }, + { "fan%d_max", applesmc_show_fan_speed, NULL, 2 }, + { "fan%d_safe", applesmc_show_fan_speed, NULL, 3 }, + { "fan%d_output", applesmc_show_fan_speed, applesmc_store_fan_speed, 4 }, + { "fan%d_manual", applesmc_show_fan_manual, applesmc_store_fan_manual }, + { } }; -/* - * Temperature sensors sysfs entries. - */ -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, - applesmc_show_temperature, NULL, 0); -static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, - applesmc_show_temperature, NULL, 1); -static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, - applesmc_show_temperature, NULL, 2); -static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, - applesmc_show_temperature, NULL, 3); -static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, - applesmc_show_temperature, NULL, 4); -static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, - applesmc_show_temperature, NULL, 5); -static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, - applesmc_show_temperature, NULL, 6); -static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, - applesmc_show_temperature, NULL, 7); -static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO, - applesmc_show_temperature, NULL, 8); -static SENSOR_DEVICE_ATTR(temp10_input, S_IRUGO, - applesmc_show_temperature, NULL, 9); -static SENSOR_DEVICE_ATTR(temp11_input, S_IRUGO, - applesmc_show_temperature, NULL, 10); -static SENSOR_DEVICE_ATTR(temp12_input, S_IRUGO, - applesmc_show_temperature, NULL, 11); -static SENSOR_DEVICE_ATTR(temp13_input, S_IRUGO, - applesmc_show_temperature, NULL, 12); -static SENSOR_DEVICE_ATTR(temp14_input, S_IRUGO, - applesmc_show_temperature, NULL, 13); -static SENSOR_DEVICE_ATTR(temp15_input, S_IRUGO, - applesmc_show_temperature, NULL, 14); -static SENSOR_DEVICE_ATTR(temp16_input, S_IRUGO, - applesmc_show_temperature, NULL, 15); -static SENSOR_DEVICE_ATTR(temp17_input, S_IRUGO, - applesmc_show_temperature, NULL, 16); -static SENSOR_DEVICE_ATTR(temp18_input, S_IRUGO, - applesmc_show_temperature, NULL, 17); -static SENSOR_DEVICE_ATTR(temp19_input, S_IRUGO, - applesmc_show_temperature, NULL, 18); -static SENSOR_DEVICE_ATTR(temp20_input, S_IRUGO, - applesmc_show_temperature, NULL, 19); -static SENSOR_DEVICE_ATTR(temp21_input, S_IRUGO, - applesmc_show_temperature, NULL, 20); -static SENSOR_DEVICE_ATTR(temp22_input, S_IRUGO, - applesmc_show_temperature, NULL, 21); -static SENSOR_DEVICE_ATTR(temp23_input, S_IRUGO, - applesmc_show_temperature, NULL, 22); -static SENSOR_DEVICE_ATTR(temp24_input, S_IRUGO, - applesmc_show_temperature, NULL, 23); -static SENSOR_DEVICE_ATTR(temp25_input, S_IRUGO, - applesmc_show_temperature, NULL, 24); -static SENSOR_DEVICE_ATTR(temp26_input, S_IRUGO, - applesmc_show_temperature, NULL, 25); -static SENSOR_DEVICE_ATTR(temp27_input, S_IRUGO, - applesmc_show_temperature, NULL, 26); -static SENSOR_DEVICE_ATTR(temp28_input, S_IRUGO, - applesmc_show_temperature, NULL, 27); -static SENSOR_DEVICE_ATTR(temp29_input, S_IRUGO, - applesmc_show_temperature, NULL, 28); -static SENSOR_DEVICE_ATTR(temp30_input, S_IRUGO, - applesmc_show_temperature, NULL, 29); -static SENSOR_DEVICE_ATTR(temp31_input, S_IRUGO, - applesmc_show_temperature, NULL, 30); -static SENSOR_DEVICE_ATTR(temp32_input, S_IRUGO, - applesmc_show_temperature, NULL, 31); -static SENSOR_DEVICE_ATTR(temp33_input, S_IRUGO, - applesmc_show_temperature, NULL, 32); -static SENSOR_DEVICE_ATTR(temp34_input, S_IRUGO, - applesmc_show_temperature, NULL, 33); -static SENSOR_DEVICE_ATTR(temp35_input, S_IRUGO, - applesmc_show_temperature, NULL, 34); -static SENSOR_DEVICE_ATTR(temp36_input, S_IRUGO, - applesmc_show_temperature, NULL, 35); -static SENSOR_DEVICE_ATTR(temp37_input, S_IRUGO, - applesmc_show_temperature, NULL, 36); -static SENSOR_DEVICE_ATTR(temp38_input, S_IRUGO, - applesmc_show_temperature, NULL, 37); -static SENSOR_DEVICE_ATTR(temp39_input, S_IRUGO, - applesmc_show_temperature, NULL, 38); -static SENSOR_DEVICE_ATTR(temp40_input, S_IRUGO, - applesmc_show_temperature, NULL, 39); - -static struct attribute *temperature_attributes[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp4_input.dev_attr.attr, - &sensor_dev_attr_temp5_input.dev_attr.attr, - &sensor_dev_attr_temp6_input.dev_attr.attr, - &sensor_dev_attr_temp7_input.dev_attr.attr, - &sensor_dev_attr_temp8_input.dev_attr.attr, - &sensor_dev_attr_temp9_input.dev_attr.attr, - &sensor_dev_attr_temp10_input.dev_attr.attr, - &sensor_dev_attr_temp11_input.dev_attr.attr, - &sensor_dev_attr_temp12_input.dev_attr.attr, - &sensor_dev_attr_temp13_input.dev_attr.attr, - &sensor_dev_attr_temp14_input.dev_attr.attr, - &sensor_dev_attr_temp15_input.dev_attr.attr, - &sensor_dev_attr_temp16_input.dev_attr.attr, - &sensor_dev_attr_temp17_input.dev_attr.attr, - &sensor_dev_attr_temp18_input.dev_attr.attr, - &sensor_dev_attr_temp19_input.dev_attr.attr, - &sensor_dev_attr_temp20_input.dev_attr.attr, - &sensor_dev_attr_temp21_input.dev_attr.attr, - &sensor_dev_attr_temp22_input.dev_attr.attr, - &sensor_dev_attr_temp23_input.dev_attr.attr, - &sensor_dev_attr_temp24_input.dev_attr.attr, - &sensor_dev_attr_temp25_input.dev_attr.attr, - &sensor_dev_attr_temp26_input.dev_attr.attr, - &sensor_dev_attr_temp27_input.dev_attr.attr, - &sensor_dev_attr_temp28_input.dev_attr.attr, - &sensor_dev_attr_temp29_input.dev_attr.attr, - &sensor_dev_attr_temp30_input.dev_attr.attr, - &sensor_dev_attr_temp31_input.dev_attr.attr, - &sensor_dev_attr_temp32_input.dev_attr.attr, - &sensor_dev_attr_temp33_input.dev_attr.attr, - &sensor_dev_attr_temp34_input.dev_attr.attr, - &sensor_dev_attr_temp35_input.dev_attr.attr, - &sensor_dev_attr_temp36_input.dev_attr.attr, - &sensor_dev_attr_temp37_input.dev_attr.attr, - &sensor_dev_attr_temp38_input.dev_attr.attr, - &sensor_dev_attr_temp39_input.dev_attr.attr, - &sensor_dev_attr_temp40_input.dev_attr.attr, - NULL +static struct applesmc_node_group temp_group[] = { + { "temp%d_label", applesmc_show_sensor_label }, + { "temp%d_input", applesmc_show_temperature }, + { } }; -static const struct attribute_group temperature_attributes_group = - { .attrs = temperature_attributes }; - /* Module stuff */ /* - * applesmc_dmi_match - found a match. return one, short-circuiting the hunt. + * applesmc_destroy_nodes - remove files and free associated memory */ -static int applesmc_dmi_match(const struct dmi_system_id *id) +static void applesmc_destroy_nodes(struct applesmc_node_group *groups) { - int i = 0; - struct dmi_match_data* dmi_data = id->driver_data; - printk(KERN_INFO "applesmc: %s detected:\n", id->ident); - applesmc_accelerometer = dmi_data->accelerometer; - printk(KERN_INFO "applesmc: - Model %s accelerometer\n", - applesmc_accelerometer ? "with" : "without"); - applesmc_light = dmi_data->light; - printk(KERN_INFO "applesmc: - Model %s light sensors and backlight\n", - applesmc_light ? "with" : "without"); - - applesmc_temperature_set = dmi_data->temperature_set; - while (temperature_sensors_sets[applesmc_temperature_set][i] != NULL) - i++; - printk(KERN_INFO "applesmc: - Model with %d temperature sensors\n", i); - return 1; + struct applesmc_node_group *grp; + struct applesmc_dev_attr *node; + + for (grp = groups; grp->nodes; grp++) { + for (node = grp->nodes; node->sda.dev_attr.attr.name; node++) + sysfs_remove_file(&pdev->dev.kobj, + &node->sda.dev_attr.attr); + kfree(grp->nodes); + grp->nodes = NULL; + } +} + +/* + * applesmc_create_nodes - create a two-dimensional group of sysfs files + */ +static int applesmc_create_nodes(struct applesmc_node_group *groups, int num) +{ + struct applesmc_node_group *grp; + struct applesmc_dev_attr *node; + struct attribute *attr; + int ret, i; + + for (grp = groups; grp->format; grp++) { + grp->nodes = kcalloc(num + 1, sizeof(*node), GFP_KERNEL); + if (!grp->nodes) { + ret = -ENOMEM; + goto out; + } + for (i = 0; i < num; i++) { + node = &grp->nodes[i]; + sprintf(node->name, grp->format, i + 1); + node->sda.index = (grp->option << 16) | (i & 0xffff); + node->sda.dev_attr.show = grp->show; + node->sda.dev_attr.store = grp->store; + attr = &node->sda.dev_attr.attr; + sysfs_attr_init(attr); + attr->name = node->name; + attr->mode = S_IRUGO | (grp->store ? S_IWUSR : 0); + ret = sysfs_create_file(&pdev->dev.kobj, attr); + if (ret) { + attr->name = NULL; + goto out; + } + } + } + + return 0; +out: + applesmc_destroy_nodes(groups); + return ret; } /* Create accelerometer ressources */ @@ -1262,8 +1145,10 @@ static int applesmc_create_accelerometer(void) struct input_dev *idev; int ret; - ret = sysfs_create_group(&pdev->dev.kobj, - &accelerometer_attributes_group); + if (!smcreg.has_accelerometer) + return 0; + + ret = applesmc_create_nodes(accelerometer_group, 1); if (ret) goto out; @@ -1300,148 +1185,98 @@ out_idev: input_free_polled_device(applesmc_idev); out_sysfs: - sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group); + applesmc_destroy_nodes(accelerometer_group); out: - printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret); + pr_warn("driver init failed (ret=%d)!\n", ret); return ret; } /* Release all ressources used by the accelerometer */ static void applesmc_release_accelerometer(void) { + if (!smcreg.has_accelerometer) + return; input_unregister_polled_device(applesmc_idev); input_free_polled_device(applesmc_idev); - sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group); + applesmc_destroy_nodes(accelerometer_group); } -static __initdata struct dmi_match_data applesmc_dmi_data[] = { -/* MacBook Pro: accelerometer, backlight and temperature set 0 */ - { .accelerometer = 1, .light = 1, .temperature_set = 0 }, -/* MacBook2: accelerometer and temperature set 1 */ - { .accelerometer = 1, .light = 0, .temperature_set = 1 }, -/* MacBook: accelerometer and temperature set 2 */ - { .accelerometer = 1, .light = 0, .temperature_set = 2 }, -/* MacMini: temperature set 3 */ - { .accelerometer = 0, .light = 0, .temperature_set = 3 }, -/* MacPro: temperature set 4 */ - { .accelerometer = 0, .light = 0, .temperature_set = 4 }, -/* iMac: temperature set 5 */ - { .accelerometer = 0, .light = 0, .temperature_set = 5 }, -/* MacBook3, MacBook4: accelerometer and temperature set 6 */ - { .accelerometer = 1, .light = 0, .temperature_set = 6 }, -/* MacBook Air: accelerometer, backlight and temperature set 7 */ - { .accelerometer = 1, .light = 1, .temperature_set = 7 }, -/* MacBook Pro 4: accelerometer, backlight and temperature set 8 */ - { .accelerometer = 1, .light = 1, .temperature_set = 8 }, -/* MacBook Pro 3: accelerometer, backlight and temperature set 9 */ - { .accelerometer = 1, .light = 1, .temperature_set = 9 }, -/* iMac 5: light sensor only, temperature set 10 */ - { .accelerometer = 0, .light = 0, .temperature_set = 10 }, -/* MacBook 5: accelerometer, backlight and temperature set 11 */ - { .accelerometer = 1, .light = 1, .temperature_set = 11 }, -/* MacBook Pro 5: accelerometer, backlight and temperature set 12 */ - { .accelerometer = 1, .light = 1, .temperature_set = 12 }, -/* iMac 8: light sensor only, temperature set 13 */ - { .accelerometer = 0, .light = 0, .temperature_set = 13 }, -/* iMac 6: light sensor only, temperature set 14 */ - { .accelerometer = 0, .light = 0, .temperature_set = 14 }, -/* MacBook Air 2,1: accelerometer, backlight and temperature set 15 */ - { .accelerometer = 1, .light = 1, .temperature_set = 15 }, -/* MacPro3,1: temperature set 16 */ - { .accelerometer = 0, .light = 0, .temperature_set = 16 }, -}; +static int applesmc_create_light_sensor(void) +{ + if (!smcreg.num_light_sensors) + return 0; + return applesmc_create_nodes(light_sensor_group, 1); +} -/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1". - * So we need to put "Apple MacBook Pro" before "Apple MacBook". */ +static void applesmc_release_light_sensor(void) +{ + if (!smcreg.num_light_sensors) + return; + applesmc_destroy_nodes(light_sensor_group); +} + +static int applesmc_create_key_backlight(void) +{ + if (!smcreg.has_key_backlight) + return 0; + applesmc_led_wq = create_singlethread_workqueue("applesmc-led"); + if (!applesmc_led_wq) + return -ENOMEM; + return led_classdev_register(&pdev->dev, &applesmc_backlight); +} + +static void applesmc_release_key_backlight(void) +{ + if (!smcreg.has_key_backlight) + return; + led_classdev_unregister(&applesmc_backlight); + destroy_workqueue(applesmc_led_wq); +} + +static int applesmc_dmi_match(const struct dmi_system_id *id) +{ + return 1; +} + +/* + * Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1". + * So we need to put "Apple MacBook Pro" before "Apple MacBook". + */ static __initdata struct dmi_system_id applesmc_whitelist[] = { - { applesmc_dmi_match, "Apple MacBook Air 2", { - DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), - DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir2") }, - &applesmc_dmi_data[15]}, { applesmc_dmi_match, "Apple MacBook Air", { DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") }, - &applesmc_dmi_data[7]}, - { applesmc_dmi_match, "Apple MacBook Pro 5", { - DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), - DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5") }, - &applesmc_dmi_data[12]}, - { applesmc_dmi_match, "Apple MacBook Pro 4", { - DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), - DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4") }, - &applesmc_dmi_data[8]}, - { applesmc_dmi_match, "Apple MacBook Pro 3", { - DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), - DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3") }, - &applesmc_dmi_data[9]}, + }, { applesmc_dmi_match, "Apple MacBook Pro", { - DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), - DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") }, - &applesmc_dmi_data[0]}, - { applesmc_dmi_match, "Apple MacBook (v2)", { - DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), - DMI_MATCH(DMI_PRODUCT_NAME,"MacBook2") }, - &applesmc_dmi_data[1]}, - { applesmc_dmi_match, "Apple MacBook (v3)", { - DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), - DMI_MATCH(DMI_PRODUCT_NAME,"MacBook3") }, - &applesmc_dmi_data[6]}, - { applesmc_dmi_match, "Apple MacBook 4", { - DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), - DMI_MATCH(DMI_PRODUCT_NAME, "MacBook4") }, - &applesmc_dmi_data[6]}, - { applesmc_dmi_match, "Apple MacBook 5", { DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), - DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5") }, - &applesmc_dmi_data[11]}, + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro") }, + }, { applesmc_dmi_match, "Apple MacBook", { - DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), - DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") }, - &applesmc_dmi_data[2]}, + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBook") }, + }, { applesmc_dmi_match, "Apple Macmini", { - DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), - DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") }, - &applesmc_dmi_data[3]}, - { applesmc_dmi_match, "Apple MacPro2", { - DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), - DMI_MATCH(DMI_PRODUCT_NAME,"MacPro2") }, - &applesmc_dmi_data[4]}, - { applesmc_dmi_match, "Apple MacPro3", { DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), - DMI_MATCH(DMI_PRODUCT_NAME, "MacPro3") }, - &applesmc_dmi_data[16]}, + DMI_MATCH(DMI_PRODUCT_NAME, "Macmini") }, + }, { applesmc_dmi_match, "Apple MacPro", { DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), DMI_MATCH(DMI_PRODUCT_NAME, "MacPro") }, - &applesmc_dmi_data[4]}, - { applesmc_dmi_match, "Apple iMac 8", { - DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), - DMI_MATCH(DMI_PRODUCT_NAME, "iMac8") }, - &applesmc_dmi_data[13]}, - { applesmc_dmi_match, "Apple iMac 6", { - DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), - DMI_MATCH(DMI_PRODUCT_NAME, "iMac6") }, - &applesmc_dmi_data[14]}, - { applesmc_dmi_match, "Apple iMac 5", { - DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), - DMI_MATCH(DMI_PRODUCT_NAME, "iMac5") }, - &applesmc_dmi_data[10]}, + }, { applesmc_dmi_match, "Apple iMac", { - DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), - DMI_MATCH(DMI_PRODUCT_NAME,"iMac") }, - &applesmc_dmi_data[5]}, + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "iMac") }, + }, { .ident = NULL } }; static int __init applesmc_init(void) { int ret; - int count; - int i; if (!dmi_check_system(applesmc_whitelist)) { - printk(KERN_WARNING "applesmc: supported laptop not found!\n"); + pr_warn("supported laptop not found!\n"); ret = -ENODEV; goto out; } @@ -1463,93 +1298,34 @@ static int __init applesmc_init(void) goto out_driver; } - ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_name.attr); + /* create register cache */ + ret = applesmc_init_smcreg(); if (ret) goto out_device; - /* Create key enumeration sysfs files */ - ret = sysfs_create_group(&pdev->dev.kobj, &key_enumeration_group); + ret = applesmc_create_nodes(info_group, 1); if (ret) - goto out_name; - - /* create fan files */ - count = applesmc_get_fan_count(); - if (count < 0) { - printk(KERN_ERR "applesmc: Cannot get the number of fans.\n"); - } else { - printk(KERN_INFO "applesmc: %d fans found.\n", count); - - switch (count) { - default: - printk(KERN_WARNING "applesmc: More than 4 fans found," - " but at most 4 fans are supported" - " by the driver.\n"); - case 4: - ret = sysfs_create_group(&pdev->dev.kobj, - &fan_attribute_groups[3]); - if (ret) - goto out_key_enumeration; - case 3: - ret = sysfs_create_group(&pdev->dev.kobj, - &fan_attribute_groups[2]); - if (ret) - goto out_key_enumeration; - case 2: - ret = sysfs_create_group(&pdev->dev.kobj, - &fan_attribute_groups[1]); - if (ret) - goto out_key_enumeration; - case 1: - ret = sysfs_create_group(&pdev->dev.kobj, - &fan_attribute_groups[0]); - if (ret) - goto out_fan_1; - case 0: - ; - } - } + goto out_smcreg; - for (i = 0; - temperature_sensors_sets[applesmc_temperature_set][i] != NULL; - i++) { - if (temperature_attributes[i] == NULL) { - printk(KERN_ERR "applesmc: More temperature sensors " - "in temperature_sensors_sets (at least %i)" - "than available sysfs files in " - "temperature_attributes (%i), please report " - "this bug.\n", i, i-1); - goto out_temperature; - } - ret = sysfs_create_file(&pdev->dev.kobj, - temperature_attributes[i]); - if (ret) - goto out_temperature; - } + ret = applesmc_create_nodes(fan_group, smcreg.fan_count); + if (ret) + goto out_info; - if (applesmc_accelerometer) { - ret = applesmc_create_accelerometer(); - if (ret) - goto out_temperature; - } + ret = applesmc_create_nodes(temp_group, smcreg.index_count); + if (ret) + goto out_fans; - if (applesmc_light) { - /* Add light sensor file */ - ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_light.attr); - if (ret) - goto out_accelerometer; + ret = applesmc_create_accelerometer(); + if (ret) + goto out_temperature; - /* Create the workqueue */ - applesmc_led_wq = create_singlethread_workqueue("applesmc-led"); - if (!applesmc_led_wq) { - ret = -ENOMEM; - goto out_light_sysfs; - } + ret = applesmc_create_light_sensor(); + if (ret) + goto out_accelerometer; - /* register as a led device */ - ret = led_classdev_register(&pdev->dev, &applesmc_backlight); - if (ret < 0) - goto out_light_wq; - } + ret = applesmc_create_key_backlight(); + if (ret) + goto out_light_sysfs; hwmon_dev = hwmon_device_register(&pdev->dev); if (IS_ERR(hwmon_dev)) { @@ -1557,31 +1333,22 @@ static int __init applesmc_init(void) goto out_light_ledclass; } - printk(KERN_INFO "applesmc: driver successfully loaded.\n"); - return 0; out_light_ledclass: - if (applesmc_light) - led_classdev_unregister(&applesmc_backlight); -out_light_wq: - if (applesmc_light) - destroy_workqueue(applesmc_led_wq); + applesmc_release_key_backlight(); out_light_sysfs: - if (applesmc_light) - sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr); + applesmc_release_light_sensor(); out_accelerometer: - if (applesmc_accelerometer) - applesmc_release_accelerometer(); + applesmc_release_accelerometer(); out_temperature: - sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group); - sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[0]); -out_fan_1: - sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[1]); -out_key_enumeration: - sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group); -out_name: - sysfs_remove_file(&pdev->dev.kobj, &dev_attr_name.attr); + applesmc_destroy_nodes(temp_group); +out_fans: + applesmc_destroy_nodes(fan_group); +out_info: + applesmc_destroy_nodes(info_group); +out_smcreg: + applesmc_destroy_smcreg(); out_device: platform_device_unregister(pdev); out_driver: @@ -1589,30 +1356,23 @@ out_driver: out_region: release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS); out: - printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret); + pr_warn("driver init failed (ret=%d)!\n", ret); return ret; } static void __exit applesmc_exit(void) { hwmon_device_unregister(hwmon_dev); - if (applesmc_light) { - led_classdev_unregister(&applesmc_backlight); - destroy_workqueue(applesmc_led_wq); - sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr); - } - if (applesmc_accelerometer) - applesmc_release_accelerometer(); - sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group); - sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[0]); - sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[1]); - sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group); - sysfs_remove_file(&pdev->dev.kobj, &dev_attr_name.attr); + applesmc_release_key_backlight(); + applesmc_release_light_sensor(); + applesmc_release_accelerometer(); + applesmc_destroy_nodes(temp_group); + applesmc_destroy_nodes(fan_group); + applesmc_destroy_nodes(info_group); + applesmc_destroy_smcreg(); platform_device_unregister(pdev); platform_driver_unregister(&applesmc_driver); release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS); - - printk(KERN_INFO "applesmc: driver unloaded.\n"); } module_init(applesmc_init); diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c index 7dada559b3a..f96063680e5 100644 --- a/drivers/hwmon/asb100.c +++ b/drivers/hwmon/asb100.c @@ -1,40 +1,42 @@ /* - asb100.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - - Copyright (C) 2004 Mark M. Hoffman <mhoffman@lightlink.com> - - (derived from w83781d.c) - - Copyright (C) 1998 - 2003 Frodo Looijaard <frodol@dds.nl>, - Philip Edelbrock <phil@netroedge.com>, and - Mark Studebaker <mdsxyz123@yahoo.com> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ + * asb100.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * + * Copyright (C) 2004 Mark M. Hoffman <mhoffman@lightlink.com> + * + * (derived from w83781d.c) + * + * Copyright (C) 1998 - 2003 Frodo Looijaard <frodol@dds.nl>, + * Philip Edelbrock <phil@netroedge.com>, and + * Mark Studebaker <mdsxyz123@yahoo.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ /* - This driver supports the hardware sensor chips: Asus ASB100 and - ASB100-A "BACH". - - ASB100-A supports pwm1, while plain ASB100 does not. There is no known - way for the driver to tell which one is there. - - Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA - asb100 7 3 1 4 0x31 0x0694 yes no -*/ + * This driver supports the hardware sensor chips: Asus ASB100 and + * ASB100-A "BACH". + * + * ASB100-A supports pwm1, while plain ASB100 does not. There is no known + * way for the driver to tell which one is there. + * + * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA + * asb100 7 3 1 4 0x31 0x0694 yes no + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/slab.h> @@ -53,8 +55,8 @@ static const unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END }; static unsigned short force_subclients[4]; module_param_array(force_subclients, short, NULL, 0); -MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " - "{bus, clientaddr, subclientaddr1, subclientaddr2}"); +MODULE_PARM_DESC(force_subclients, + "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}"); /* Voltage IN registers 0-6 */ #define ASB100_REG_IN(nr) (0x20 + (nr)) @@ -97,18 +99,22 @@ static const u16 asb100_reg_temp_hyst[] = {0, 0x3a, 0x153, 0x253, 0x19}; /* bit 7 -> enable, bits 0-3 -> duty cycle */ #define ASB100_REG_PWM1 0x59 -/* CONVERSIONS - Rounding and limit checking is only done on the TO_REG variants. */ +/* + * CONVERSIONS + * Rounding and limit checking is only done on the TO_REG variants. + */ /* These constants are a guess, consistent w/ w83781d */ -#define ASB100_IN_MIN ( 0) -#define ASB100_IN_MAX (4080) +#define ASB100_IN_MIN 0 +#define ASB100_IN_MAX 4080 -/* IN: 1/1000 V (0V to 4.08V) - REG: 16mV/bit */ +/* + * IN: 1/1000 V (0V to 4.08V) + * REG: 16mV/bit + */ static u8 IN_TO_REG(unsigned val) { - unsigned nval = SENSORS_LIMIT(val, ASB100_IN_MIN, ASB100_IN_MAX); + unsigned nval = clamp_val(val, ASB100_IN_MIN, ASB100_IN_MAX); return (nval + 8) / 16; } @@ -123,25 +129,27 @@ static u8 FAN_TO_REG(long rpm, int div) return 0; if (rpm == 0) return 255; - rpm = SENSORS_LIMIT(rpm, 1, 1000000); - return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254); + rpm = clamp_val(rpm, 1, 1000000); + return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254); } static int FAN_FROM_REG(u8 val, int div) { - return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div); + return val == 0 ? -1 : val == 255 ? 0 : 1350000 / (val * div); } /* These constants are a guess, consistent w/ w83781d */ -#define ASB100_TEMP_MIN (-128000) -#define ASB100_TEMP_MAX ( 127000) +#define ASB100_TEMP_MIN -128000 +#define ASB100_TEMP_MAX 127000 -/* TEMP: 0.001C/bit (-128C to +127C) - REG: 1C/bit, two's complement */ +/* + * TEMP: 0.001C/bit (-128C to +127C) + * REG: 1C/bit, two's complement + */ static u8 TEMP_TO_REG(long temp) { - int ntemp = SENSORS_LIMIT(temp, ASB100_TEMP_MIN, ASB100_TEMP_MAX); - ntemp += (ntemp<0 ? -500 : 500); + int ntemp = clamp_val(temp, ASB100_TEMP_MIN, ASB100_TEMP_MAX); + ntemp += (ntemp < 0 ? -500 : 500); return (u8)(ntemp / 1000); } @@ -150,11 +158,13 @@ static int TEMP_FROM_REG(u8 reg) return (s8)reg * 1000; } -/* PWM: 0 - 255 per sensors documentation - REG: (6.25% duty cycle per bit) */ +/* + * PWM: 0 - 255 per sensors documentation + * REG: (6.25% duty cycle per bit) + */ static u8 ASB100_PWM_TO_REG(int pwm) { - pwm = SENSORS_LIMIT(pwm, 0, 255); + pwm = clamp_val(pwm, 0, 255); return (u8)(pwm / 16); } @@ -165,16 +175,20 @@ static int ASB100_PWM_FROM_REG(u8 reg) #define DIV_FROM_REG(val) (1 << (val)) -/* FAN DIV: 1, 2, 4, or 8 (defaults to 2) - REG: 0, 1, 2, or 3 (respectively) (defaults to 1) */ +/* + * FAN DIV: 1, 2, 4, or 8 (defaults to 2) + * REG: 0, 1, 2, or 3 (respectively) (defaults to 1) + */ static u8 DIV_TO_REG(long val) { - return val==8 ? 3 : val==4 ? 2 : val==1 ? 0 : 1; + return val == 8 ? 3 : val == 4 ? 2 : val == 1 ? 0 : 1; } -/* For each registered client, we need to keep some data in memory. That - data is pointed to by client->data. The structure itself is - dynamically allocated, at the same time the client itself is allocated. */ +/* + * For each registered client, we need to keep some data in memory. That + * data is pointed to by client->data. The structure itself is + * dynamically allocated, at the same time the client itself is allocated. + */ struct asb100_data { struct device *hwmon_dev; struct mutex lock; @@ -251,8 +265,10 @@ static ssize_t set_in_##reg(struct device *dev, struct device_attribute *attr, \ int nr = to_sensor_dev_attr(attr)->index; \ struct i2c_client *client = to_i2c_client(dev); \ struct asb100_data *data = i2c_get_clientdata(client); \ - unsigned long val = simple_strtoul(buf, NULL, 10); \ - \ + unsigned long val; \ + int err = kstrtoul(buf, 10, &val); \ + if (err) \ + return err; \ mutex_lock(&data->update_lock); \ data->in_##reg[nr] = IN_TO_REG(val); \ asb100_write_value(client, ASB100_REG_IN_##REG(nr), \ @@ -313,7 +329,12 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct asb100_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); @@ -322,10 +343,12 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, return count; } -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan divisor. This follows the principle of - least surprise; the user doesn't expect the fan minimum to change just - because the divisor changed. */ +/* + * Note: we save and restore the fan minimum here, because its value is + * determined in part by the fan divisor. This follows the principle of + * least surprise; the user doesn't expect the fan minimum to change just + * because the divisor changed. + */ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -333,8 +356,13 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, struct i2c_client *client = to_i2c_client(dev); struct asb100_data *data = i2c_get_clientdata(client); unsigned long min; - unsigned long val = simple_strtoul(buf, NULL, 10); int reg; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); @@ -419,8 +447,10 @@ static ssize_t set_##reg(struct device *dev, struct device_attribute *attr, \ int nr = to_sensor_dev_attr(attr)->index; \ struct i2c_client *client = to_i2c_client(dev); \ struct asb100_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ + long val; \ + int err = kstrtol(buf, 10, &val); \ + if (err) \ + return err; \ mutex_lock(&data->update_lock); \ switch (nr) { \ case 1: case 2: \ @@ -474,7 +504,13 @@ static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct asb100_data *data = dev_get_drvdata(dev); - data->vrm = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + data->vrm = val; return count; } @@ -522,7 +558,12 @@ static ssize_t set_pwm1(struct device *dev, struct device_attribute *attr, { struct i2c_client *client = to_i2c_client(dev); struct asb100_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->pwm &= 0x80; /* keep the enable bit */ @@ -544,7 +585,12 @@ static ssize_t set_pwm_enable1(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct asb100_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->pwm &= 0x0f; /* keep the duty cycle bits */ @@ -643,8 +689,8 @@ static int asb100_detect_subclients(struct i2c_client *client) for (i = 2; i <= 3; i++) { if (force_subclients[i] < 0x48 || force_subclients[i] > 0x4f) { - dev_err(&client->dev, "invalid subclient " - "address %d; must be 0x48-0x4f\n", + dev_err(&client->dev, + "invalid subclient address %d; must be 0x48-0x4f\n", force_subclients[i]); err = -ENODEV; goto ERROR_SC_2; @@ -662,24 +708,27 @@ static int asb100_detect_subclients(struct i2c_client *client) } if (sc_addr[0] == sc_addr[1]) { - dev_err(&client->dev, "duplicate addresses 0x%x " - "for subclients\n", sc_addr[0]); + dev_err(&client->dev, + "duplicate addresses 0x%x for subclients\n", + sc_addr[0]); err = -ENODEV; goto ERROR_SC_2; } data->lm75[0] = i2c_new_dummy(adapter, sc_addr[0]); if (!data->lm75[0]) { - dev_err(&client->dev, "subclient %d registration " - "at address 0x%x failed.\n", 1, sc_addr[0]); + dev_err(&client->dev, + "subclient %d registration at address 0x%x failed.\n", + 1, sc_addr[0]); err = -ENOMEM; goto ERROR_SC_2; } data->lm75[1] = i2c_new_dummy(adapter, sc_addr[1]); if (!data->lm75[1]) { - dev_err(&client->dev, "subclient %d registration " - "at address 0x%x failed.\n", 2, sc_addr[1]); + dev_err(&client->dev, + "subclient %d registration at address 0x%x failed.\n", + 2, sc_addr[1]); err = -ENOMEM; goto ERROR_SC_3; } @@ -701,8 +750,7 @@ static int asb100_detect(struct i2c_client *client, int val1, val2; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - pr_debug("asb100.o: detect failed, " - "smbus byte data not supported!\n"); + pr_debug("detect failed, smbus byte data not supported!\n"); return -ENODEV; } @@ -715,7 +763,7 @@ static int asb100_detect(struct i2c_client *client, (((!(val1 & 0x80)) && (val2 != 0x94)) || /* Check for ASB100 ID (high byte ) */ ((val1 & 0x80) && (val2 != 0x06)))) { - pr_debug("asb100: detect failed, bad chip id 0x%02x!\n", val2); + pr_debug("detect failed, bad chip id 0x%02x!\n", val2); return -ENODEV; } @@ -742,12 +790,10 @@ static int asb100_probe(struct i2c_client *client, int err; struct asb100_data *data; - data = kzalloc(sizeof(struct asb100_data), GFP_KERNEL); - if (!data) { - pr_debug("asb100.o: probe failed, kzalloc failed!\n"); - err = -ENOMEM; - goto ERROR0; - } + data = devm_kzalloc(&client->dev, sizeof(struct asb100_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); mutex_init(&data->lock); @@ -756,7 +802,7 @@ static int asb100_probe(struct i2c_client *client, /* Attach secondary lm75 clients */ err = asb100_detect_subclients(client); if (err) - goto ERROR1; + return err; /* Initialize the chip */ asb100_init_client(client); @@ -767,7 +813,8 @@ static int asb100_probe(struct i2c_client *client, data->fan_min[2] = asb100_read_value(client, ASB100_REG_FAN_MIN(2)); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&client->dev.kobj, &asb100_group))) + err = sysfs_create_group(&client->dev.kobj, &asb100_group); + if (err) goto ERROR3; data->hwmon_dev = hwmon_device_register(&client->dev); @@ -783,9 +830,6 @@ ERROR4: ERROR3: i2c_unregister_device(data->lm75[1]); i2c_unregister_device(data->lm75[0]); -ERROR1: - kfree(data); -ERROR0: return err; } @@ -799,13 +843,13 @@ static int asb100_remove(struct i2c_client *client) i2c_unregister_device(data->lm75[1]); i2c_unregister_device(data->lm75[0]); - kfree(data); - return 0; } -/* The SMBus locks itself, usually, but nothing may access the chip between - bank switches. */ +/* + * The SMBus locks itself, usually, but nothing may access the chip between + * bank switches. + */ static int asb100_read_value(struct i2c_client *client, u16 reg) { struct asb100_data *data = i2c_get_clientdata(client); @@ -828,17 +872,17 @@ static int asb100_read_value(struct i2c_client *client, u16 reg) /* convert from ISA to LM75 I2C addresses */ switch (reg & 0xff) { case 0x50: /* TEMP */ - res = swab16(i2c_smbus_read_word_data(cl, 0)); + res = i2c_smbus_read_word_swapped(cl, 0); break; case 0x52: /* CONFIG */ res = i2c_smbus_read_byte_data(cl, 1); break; case 0x53: /* HYST */ - res = swab16(i2c_smbus_read_word_data(cl, 2)); + res = i2c_smbus_read_word_swapped(cl, 2); break; case 0x55: /* MAX */ default: - res = swab16(i2c_smbus_read_word_data(cl, 3)); + res = i2c_smbus_read_word_swapped(cl, 3); break; } } @@ -876,10 +920,10 @@ static void asb100_write_value(struct i2c_client *client, u16 reg, u16 value) i2c_smbus_write_byte_data(cl, 1, value & 0xff); break; case 0x53: /* HYST */ - i2c_smbus_write_word_data(cl, 2, swab16(value)); + i2c_smbus_write_word_swapped(cl, 2, value); break; case 0x55: /* MAX */ - i2c_smbus_write_word_data(cl, 3, swab16(value)); + i2c_smbus_write_word_swapped(cl, 3, value); break; } } @@ -970,19 +1014,8 @@ static struct asb100_data *asb100_update_device(struct device *dev) return data; } -static int __init asb100_init(void) -{ - return i2c_add_driver(&asb100_driver); -} - -static void __exit asb100_exit(void) -{ - i2c_del_driver(&asb100_driver); -} +module_i2c_driver(asb100_driver); MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); MODULE_DESCRIPTION("ASB100 Bach driver"); MODULE_LICENSE("GPL"); - -module_init(asb100_init); -module_exit(asb100_exit); diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c new file mode 100644 index 00000000000..71463689d16 --- /dev/null +++ b/drivers/hwmon/asc7621.c @@ -0,0 +1,1247 @@ +/* + * asc7621.c - Part of lm_sensors, Linux kernel modules for hardware monitoring + * Copyright (c) 2007, 2010 George Joseph <george.joseph@fairview5.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { + 0x2c, 0x2d, 0x2e, I2C_CLIENT_END +}; + +enum asc7621_type { + asc7621, + asc7621a +}; + +#define INTERVAL_HIGH (HZ + HZ / 2) +#define INTERVAL_LOW (1 * 60 * HZ) +#define PRI_NONE 0 +#define PRI_LOW 1 +#define PRI_HIGH 2 +#define FIRST_CHIP asc7621 +#define LAST_CHIP asc7621a + +struct asc7621_chip { + char *name; + enum asc7621_type chip_type; + u8 company_reg; + u8 company_id; + u8 verstep_reg; + u8 verstep_id; + const unsigned short *addresses; +}; + +static struct asc7621_chip asc7621_chips[] = { + { + .name = "asc7621", + .chip_type = asc7621, + .company_reg = 0x3e, + .company_id = 0x61, + .verstep_reg = 0x3f, + .verstep_id = 0x6c, + .addresses = normal_i2c, + }, + { + .name = "asc7621a", + .chip_type = asc7621a, + .company_reg = 0x3e, + .company_id = 0x61, + .verstep_reg = 0x3f, + .verstep_id = 0x6d, + .addresses = normal_i2c, + }, +}; + +/* + * Defines the highest register to be used, not the count. + * The actual count will probably be smaller because of gaps + * in the implementation (unused register locations). + * This define will safely set the array size of both the parameter + * and data arrays. + * This comes from the data sheet register description table. + */ +#define LAST_REGISTER 0xff + +struct asc7621_data { + struct i2c_client client; + struct device *class_dev; + struct mutex update_lock; + int valid; /* !=0 if following fields are valid */ + unsigned long last_high_reading; /* In jiffies */ + unsigned long last_low_reading; /* In jiffies */ + /* + * Registers we care about occupy the corresponding index + * in the array. Registers we don't care about are left + * at 0. + */ + u8 reg[LAST_REGISTER + 1]; +}; + +/* + * Macro to get the parent asc7621_param structure + * from a sensor_device_attribute passed into the + * show/store functions. + */ +#define to_asc7621_param(_sda) \ + container_of(_sda, struct asc7621_param, sda) + +/* + * Each parameter to be retrieved needs an asc7621_param structure + * allocated. It contains the sensor_device_attribute structure + * and the control info needed to retrieve the value from the register map. + */ +struct asc7621_param { + struct sensor_device_attribute sda; + u8 priority; + u8 msb[3]; + u8 lsb[3]; + u8 mask[3]; + u8 shift[3]; +}; + +/* + * This is the map that ultimately indicates whether we'll be + * retrieving a register value or not, and at what frequency. + */ +static u8 asc7621_register_priorities[255]; + +static struct asc7621_data *asc7621_update_device(struct device *dev); + +static inline u8 read_byte(struct i2c_client *client, u8 reg) +{ + int res = i2c_smbus_read_byte_data(client, reg); + if (res < 0) { + dev_err(&client->dev, + "Unable to read from register 0x%02x.\n", reg); + return 0; + } + return res & 0xff; +} + +static inline int write_byte(struct i2c_client *client, u8 reg, u8 data) +{ + int res = i2c_smbus_write_byte_data(client, reg, data); + if (res < 0) { + dev_err(&client->dev, + "Unable to write value 0x%02x to register 0x%02x.\n", + data, reg); + } + return res; +} + +/* + * Data Handlers + * Each function handles the formatting, storage + * and retrieval of like parameters. + */ + +#define SETUP_SHOW_DATA_PARAM(d, a) \ + struct sensor_device_attribute *sda = to_sensor_dev_attr(a); \ + struct asc7621_data *data = asc7621_update_device(d); \ + struct asc7621_param *param = to_asc7621_param(sda) + +#define SETUP_STORE_DATA_PARAM(d, a) \ + struct sensor_device_attribute *sda = to_sensor_dev_attr(a); \ + struct i2c_client *client = to_i2c_client(d); \ + struct asc7621_data *data = i2c_get_clientdata(client); \ + struct asc7621_param *param = to_asc7621_param(sda) + +/* + * u8 is just what it sounds like...an unsigned byte with no + * special formatting. + */ +static ssize_t show_u8(struct device *dev, struct device_attribute *attr, + char *buf) +{ + SETUP_SHOW_DATA_PARAM(dev, attr); + + return sprintf(buf, "%u\n", data->reg[param->msb[0]]); +} + +static ssize_t store_u8(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + SETUP_STORE_DATA_PARAM(dev, attr); + long reqval; + + if (kstrtol(buf, 10, &reqval)) + return -EINVAL; + + reqval = clamp_val(reqval, 0, 255); + + mutex_lock(&data->update_lock); + data->reg[param->msb[0]] = reqval; + write_byte(client, param->msb[0], reqval); + mutex_unlock(&data->update_lock); + return count; +} + +/* + * Many of the config values occupy only a few bits of a register. + */ +static ssize_t show_bitmask(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_DATA_PARAM(dev, attr); + + return sprintf(buf, "%u\n", + (data->reg[param->msb[0]] >> param-> + shift[0]) & param->mask[0]); +} + +static ssize_t store_bitmask(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + SETUP_STORE_DATA_PARAM(dev, attr); + long reqval; + u8 currval; + + if (kstrtol(buf, 10, &reqval)) + return -EINVAL; + + reqval = clamp_val(reqval, 0, param->mask[0]); + + reqval = (reqval & param->mask[0]) << param->shift[0]; + + mutex_lock(&data->update_lock); + currval = read_byte(client, param->msb[0]); + reqval |= (currval & ~(param->mask[0] << param->shift[0])); + data->reg[param->msb[0]] = reqval; + write_byte(client, param->msb[0], reqval); + mutex_unlock(&data->update_lock); + return count; +} + +/* + * 16 bit fan rpm values + * reported by the device as the number of 11.111us periods (90khz) + * between full fan rotations. Therefore... + * RPM = (90000 * 60) / register value + */ +static ssize_t show_fan16(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_DATA_PARAM(dev, attr); + u16 regval; + + mutex_lock(&data->update_lock); + regval = (data->reg[param->msb[0]] << 8) | data->reg[param->lsb[0]]; + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%u\n", + (regval == 0 ? -1 : (regval) == + 0xffff ? 0 : 5400000 / regval)); +} + +static ssize_t store_fan16(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + SETUP_STORE_DATA_PARAM(dev, attr); + long reqval; + + if (kstrtol(buf, 10, &reqval)) + return -EINVAL; + + /* + * If a minimum RPM of zero is requested, then we set the register to + * 0xffff. This value allows the fan to be stopped completely without + * generating an alarm. + */ + reqval = + (reqval <= 0 ? 0xffff : clamp_val(5400000 / reqval, 0, 0xfffe)); + + mutex_lock(&data->update_lock); + data->reg[param->msb[0]] = (reqval >> 8) & 0xff; + data->reg[param->lsb[0]] = reqval & 0xff; + write_byte(client, param->msb[0], data->reg[param->msb[0]]); + write_byte(client, param->lsb[0], data->reg[param->lsb[0]]); + mutex_unlock(&data->update_lock); + + return count; +} + +/* + * Voltages are scaled in the device so that the nominal voltage + * is 3/4ths of the 0-255 range (i.e. 192). + * If all voltages are 'normal' then all voltage registers will + * read 0xC0. + * + * The data sheet provides us with the 3/4 scale value for each voltage + * which is stored in in_scaling. The sda->index parameter value provides + * the index into in_scaling. + * + * NOTE: The chip expects the first 2 inputs be 2.5 and 2.25 volts + * respectively. That doesn't mean that's what the motherboard provides. :) + */ + +static int asc7621_in_scaling[] = { + 2500, 2250, 3300, 5000, 12000 +}; + +static ssize_t show_in10(struct device *dev, struct device_attribute *attr, + char *buf) +{ + SETUP_SHOW_DATA_PARAM(dev, attr); + u16 regval; + u8 nr = sda->index; + + mutex_lock(&data->update_lock); + regval = (data->reg[param->msb[0]] << 8) | (data->reg[param->lsb[0]]); + mutex_unlock(&data->update_lock); + + /* The LSB value is a 2-bit scaling of the MSB's LSbit value. */ + regval = (regval >> 6) * asc7621_in_scaling[nr] / (0xc0 << 2); + + return sprintf(buf, "%u\n", regval); +} + +/* 8 bit voltage values (the mins and maxs) */ +static ssize_t show_in8(struct device *dev, struct device_attribute *attr, + char *buf) +{ + SETUP_SHOW_DATA_PARAM(dev, attr); + u8 nr = sda->index; + + return sprintf(buf, "%u\n", + ((data->reg[param->msb[0]] * + asc7621_in_scaling[nr]) / 0xc0)); +} + +static ssize_t store_in8(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + SETUP_STORE_DATA_PARAM(dev, attr); + long reqval; + u8 nr = sda->index; + + if (kstrtol(buf, 10, &reqval)) + return -EINVAL; + + reqval = clamp_val(reqval, 0, 0xffff); + + reqval = reqval * 0xc0 / asc7621_in_scaling[nr]; + + reqval = clamp_val(reqval, 0, 0xff); + + mutex_lock(&data->update_lock); + data->reg[param->msb[0]] = reqval; + write_byte(client, param->msb[0], reqval); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_temp8(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_DATA_PARAM(dev, attr); + + return sprintf(buf, "%d\n", ((s8) data->reg[param->msb[0]]) * 1000); +} + +static ssize_t store_temp8(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + SETUP_STORE_DATA_PARAM(dev, attr); + long reqval; + s8 temp; + + if (kstrtol(buf, 10, &reqval)) + return -EINVAL; + + reqval = clamp_val(reqval, -127000, 127000); + + temp = reqval / 1000; + + mutex_lock(&data->update_lock); + data->reg[param->msb[0]] = temp; + write_byte(client, param->msb[0], temp); + mutex_unlock(&data->update_lock); + return count; +} + +/* + * Temperatures that occupy 2 bytes always have the whole + * number of degrees in the MSB with some part of the LSB + * indicating fractional degrees. + */ + +/* mmmmmmmm.llxxxxxx */ +static ssize_t show_temp10(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_DATA_PARAM(dev, attr); + u8 msb, lsb; + int temp; + + mutex_lock(&data->update_lock); + msb = data->reg[param->msb[0]]; + lsb = (data->reg[param->lsb[0]] >> 6) & 0x03; + temp = (((s8) msb) * 1000) + (lsb * 250); + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", temp); +} + +/* mmmmmm.ll */ +static ssize_t show_temp62(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_DATA_PARAM(dev, attr); + u8 regval = data->reg[param->msb[0]]; + int temp = ((s8) (regval & 0xfc) * 1000) + ((regval & 0x03) * 250); + + return sprintf(buf, "%d\n", temp); +} + +static ssize_t store_temp62(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + SETUP_STORE_DATA_PARAM(dev, attr); + long reqval, i, f; + s8 temp; + + if (kstrtol(buf, 10, &reqval)) + return -EINVAL; + + reqval = clamp_val(reqval, -32000, 31750); + i = reqval / 1000; + f = reqval - (i * 1000); + temp = i << 2; + temp |= f / 250; + + mutex_lock(&data->update_lock); + data->reg[param->msb[0]] = temp; + write_byte(client, param->msb[0], temp); + mutex_unlock(&data->update_lock); + return count; +} + +/* + * The aSC7621 doesn't provide an "auto_point2". Instead, you + * specify the auto_point1 and a range. To keep with the sysfs + * hwmon specs, we synthesize the auto_point_2 from them. + */ + +static u32 asc7621_range_map[] = { + 2000, 2500, 3330, 4000, 5000, 6670, 8000, 10000, + 13330, 16000, 20000, 26670, 32000, 40000, 53330, 80000, +}; + +static ssize_t show_ap2_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_DATA_PARAM(dev, attr); + long auto_point1; + u8 regval; + int temp; + + mutex_lock(&data->update_lock); + auto_point1 = ((s8) data->reg[param->msb[1]]) * 1000; + regval = + ((data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0]); + temp = auto_point1 + asc7621_range_map[clamp_val(regval, 0, 15)]; + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", temp); + +} + +static ssize_t store_ap2_temp(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + SETUP_STORE_DATA_PARAM(dev, attr); + long reqval, auto_point1; + int i; + u8 currval, newval = 0; + + if (kstrtol(buf, 10, &reqval)) + return -EINVAL; + + mutex_lock(&data->update_lock); + auto_point1 = data->reg[param->msb[1]] * 1000; + reqval = clamp_val(reqval, auto_point1 + 2000, auto_point1 + 80000); + + for (i = ARRAY_SIZE(asc7621_range_map) - 1; i >= 0; i--) { + if (reqval >= auto_point1 + asc7621_range_map[i]) { + newval = i; + break; + } + } + + newval = (newval & param->mask[0]) << param->shift[0]; + currval = read_byte(client, param->msb[0]); + newval |= (currval & ~(param->mask[0] << param->shift[0])); + data->reg[param->msb[0]] = newval; + write_byte(client, param->msb[0], newval); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t show_pwm_ac(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_DATA_PARAM(dev, attr); + u8 config, altbit, regval; + u8 map[] = { + 0x01, 0x02, 0x04, 0x1f, 0x00, 0x06, 0x07, 0x10, + 0x08, 0x0f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f + }; + + mutex_lock(&data->update_lock); + config = (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0]; + altbit = (data->reg[param->msb[1]] >> param->shift[1]) & param->mask[1]; + regval = config | (altbit << 3); + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%u\n", map[clamp_val(regval, 0, 15)]); +} + +static ssize_t store_pwm_ac(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + SETUP_STORE_DATA_PARAM(dev, attr); + unsigned long reqval; + u8 currval, config, altbit, newval; + u16 map[] = { + 0x04, 0x00, 0x01, 0xff, 0x02, 0xff, 0x05, 0x06, + 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, + 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, + }; + + if (kstrtoul(buf, 10, &reqval)) + return -EINVAL; + + if (reqval > 31) + return -EINVAL; + + reqval = map[reqval]; + if (reqval == 0xff) + return -EINVAL; + + config = reqval & 0x07; + altbit = (reqval >> 3) & 0x01; + + config = (config & param->mask[0]) << param->shift[0]; + altbit = (altbit & param->mask[1]) << param->shift[1]; + + mutex_lock(&data->update_lock); + currval = read_byte(client, param->msb[0]); + newval = config | (currval & ~(param->mask[0] << param->shift[0])); + newval = altbit | (newval & ~(param->mask[1] << param->shift[1])); + data->reg[param->msb[0]] = newval; + write_byte(client, param->msb[0], newval); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t show_pwm_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_DATA_PARAM(dev, attr); + u8 config, altbit, minoff, val, newval; + + mutex_lock(&data->update_lock); + config = (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0]; + altbit = (data->reg[param->msb[1]] >> param->shift[1]) & param->mask[1]; + minoff = (data->reg[param->msb[2]] >> param->shift[2]) & param->mask[2]; + mutex_unlock(&data->update_lock); + + val = config | (altbit << 3); + newval = 0; + + if (val == 3 || val >= 10) + newval = 255; + else if (val == 4) + newval = 0; + else if (val == 7) + newval = 1; + else if (minoff == 1) + newval = 2; + else + newval = 3; + + return sprintf(buf, "%u\n", newval); +} + +static ssize_t store_pwm_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + SETUP_STORE_DATA_PARAM(dev, attr); + long reqval; + u8 currval, config, altbit, newval, minoff = 255; + + if (kstrtol(buf, 10, &reqval)) + return -EINVAL; + + switch (reqval) { + case 0: + newval = 0x04; + break; + case 1: + newval = 0x07; + break; + case 2: + newval = 0x00; + minoff = 1; + break; + case 3: + newval = 0x00; + minoff = 0; + break; + case 255: + newval = 0x03; + break; + default: + return -EINVAL; + } + + config = newval & 0x07; + altbit = (newval >> 3) & 0x01; + + mutex_lock(&data->update_lock); + config = (config & param->mask[0]) << param->shift[0]; + altbit = (altbit & param->mask[1]) << param->shift[1]; + currval = read_byte(client, param->msb[0]); + newval = config | (currval & ~(param->mask[0] << param->shift[0])); + newval = altbit | (newval & ~(param->mask[1] << param->shift[1])); + data->reg[param->msb[0]] = newval; + write_byte(client, param->msb[0], newval); + if (minoff < 255) { + minoff = (minoff & param->mask[2]) << param->shift[2]; + currval = read_byte(client, param->msb[2]); + newval = + minoff | (currval & ~(param->mask[2] << param->shift[2])); + data->reg[param->msb[2]] = newval; + write_byte(client, param->msb[2], newval); + } + mutex_unlock(&data->update_lock); + return count; +} + +static u32 asc7621_pwm_freq_map[] = { + 10, 15, 23, 30, 38, 47, 62, 94, + 23000, 24000, 25000, 26000, 27000, 28000, 29000, 30000 +}; + +static ssize_t show_pwm_freq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_DATA_PARAM(dev, attr); + u8 regval = + (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0]; + + regval = clamp_val(regval, 0, 15); + + return sprintf(buf, "%u\n", asc7621_pwm_freq_map[regval]); +} + +static ssize_t store_pwm_freq(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + SETUP_STORE_DATA_PARAM(dev, attr); + unsigned long reqval; + u8 currval, newval = 255; + int i; + + if (kstrtoul(buf, 10, &reqval)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(asc7621_pwm_freq_map); i++) { + if (reqval == asc7621_pwm_freq_map[i]) { + newval = i; + break; + } + } + if (newval == 255) + return -EINVAL; + + newval = (newval & param->mask[0]) << param->shift[0]; + + mutex_lock(&data->update_lock); + currval = read_byte(client, param->msb[0]); + newval |= (currval & ~(param->mask[0] << param->shift[0])); + data->reg[param->msb[0]] = newval; + write_byte(client, param->msb[0], newval); + mutex_unlock(&data->update_lock); + return count; +} + +static u32 asc7621_pwm_auto_spinup_map[] = { + 0, 100, 250, 400, 700, 1000, 2000, 4000 +}; + +static ssize_t show_pwm_ast(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_DATA_PARAM(dev, attr); + u8 regval = + (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0]; + + regval = clamp_val(regval, 0, 7); + + return sprintf(buf, "%u\n", asc7621_pwm_auto_spinup_map[regval]); + +} + +static ssize_t store_pwm_ast(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + SETUP_STORE_DATA_PARAM(dev, attr); + long reqval; + u8 currval, newval = 255; + u32 i; + + if (kstrtol(buf, 10, &reqval)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(asc7621_pwm_auto_spinup_map); i++) { + if (reqval == asc7621_pwm_auto_spinup_map[i]) { + newval = i; + break; + } + } + if (newval == 255) + return -EINVAL; + + newval = (newval & param->mask[0]) << param->shift[0]; + + mutex_lock(&data->update_lock); + currval = read_byte(client, param->msb[0]); + newval |= (currval & ~(param->mask[0] << param->shift[0])); + data->reg[param->msb[0]] = newval; + write_byte(client, param->msb[0], newval); + mutex_unlock(&data->update_lock); + return count; +} + +static u32 asc7621_temp_smoothing_time_map[] = { + 35000, 17600, 11800, 7000, 4400, 3000, 1600, 800 +}; + +static ssize_t show_temp_st(struct device *dev, + struct device_attribute *attr, char *buf) +{ + SETUP_SHOW_DATA_PARAM(dev, attr); + u8 regval = + (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0]; + regval = clamp_val(regval, 0, 7); + + return sprintf(buf, "%u\n", asc7621_temp_smoothing_time_map[regval]); +} + +static ssize_t store_temp_st(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + SETUP_STORE_DATA_PARAM(dev, attr); + long reqval; + u8 currval, newval = 255; + u32 i; + + if (kstrtol(buf, 10, &reqval)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(asc7621_temp_smoothing_time_map); i++) { + if (reqval == asc7621_temp_smoothing_time_map[i]) { + newval = i; + break; + } + } + + if (newval == 255) + return -EINVAL; + + newval = (newval & param->mask[0]) << param->shift[0]; + + mutex_lock(&data->update_lock); + currval = read_byte(client, param->msb[0]); + newval |= (currval & ~(param->mask[0] << param->shift[0])); + data->reg[param->msb[0]] = newval; + write_byte(client, param->msb[0], newval); + mutex_unlock(&data->update_lock); + return count; +} + +/* + * End of data handlers + * + * These defines do nothing more than make the table easier + * to read when wrapped at column 80. + */ + +/* + * Creates a variable length array inititalizer. + * VAA(1,3,5,7) would produce {1,3,5,7} + */ +#define VAA(args...) {args} + +#define PREAD(name, n, pri, rm, rl, m, s, r) \ + {.sda = SENSOR_ATTR(name, S_IRUGO, show_##r, NULL, n), \ + .priority = pri, .msb[0] = rm, .lsb[0] = rl, .mask[0] = m, \ + .shift[0] = s,} + +#define PWRITE(name, n, pri, rm, rl, m, s, r) \ + {.sda = SENSOR_ATTR(name, S_IRUGO | S_IWUSR, show_##r, store_##r, n), \ + .priority = pri, .msb[0] = rm, .lsb[0] = rl, .mask[0] = m, \ + .shift[0] = s,} + +/* + * PWRITEM assumes that the initializers for the .msb, .lsb, .mask and .shift + * were created using the VAA macro. + */ +#define PWRITEM(name, n, pri, rm, rl, m, s, r) \ + {.sda = SENSOR_ATTR(name, S_IRUGO | S_IWUSR, show_##r, store_##r, n), \ + .priority = pri, .msb = rm, .lsb = rl, .mask = m, .shift = s,} + +static struct asc7621_param asc7621_params[] = { + PREAD(in0_input, 0, PRI_HIGH, 0x20, 0x13, 0, 0, in10), + PREAD(in1_input, 1, PRI_HIGH, 0x21, 0x18, 0, 0, in10), + PREAD(in2_input, 2, PRI_HIGH, 0x22, 0x11, 0, 0, in10), + PREAD(in3_input, 3, PRI_HIGH, 0x23, 0x12, 0, 0, in10), + PREAD(in4_input, 4, PRI_HIGH, 0x24, 0x14, 0, 0, in10), + + PWRITE(in0_min, 0, PRI_LOW, 0x44, 0, 0, 0, in8), + PWRITE(in1_min, 1, PRI_LOW, 0x46, 0, 0, 0, in8), + PWRITE(in2_min, 2, PRI_LOW, 0x48, 0, 0, 0, in8), + PWRITE(in3_min, 3, PRI_LOW, 0x4a, 0, 0, 0, in8), + PWRITE(in4_min, 4, PRI_LOW, 0x4c, 0, 0, 0, in8), + + PWRITE(in0_max, 0, PRI_LOW, 0x45, 0, 0, 0, in8), + PWRITE(in1_max, 1, PRI_LOW, 0x47, 0, 0, 0, in8), + PWRITE(in2_max, 2, PRI_LOW, 0x49, 0, 0, 0, in8), + PWRITE(in3_max, 3, PRI_LOW, 0x4b, 0, 0, 0, in8), + PWRITE(in4_max, 4, PRI_LOW, 0x4d, 0, 0, 0, in8), + + PREAD(in0_alarm, 0, PRI_HIGH, 0x41, 0, 0x01, 0, bitmask), + PREAD(in1_alarm, 1, PRI_HIGH, 0x41, 0, 0x01, 1, bitmask), + PREAD(in2_alarm, 2, PRI_HIGH, 0x41, 0, 0x01, 2, bitmask), + PREAD(in3_alarm, 3, PRI_HIGH, 0x41, 0, 0x01, 3, bitmask), + PREAD(in4_alarm, 4, PRI_HIGH, 0x42, 0, 0x01, 0, bitmask), + + PREAD(fan1_input, 0, PRI_HIGH, 0x29, 0x28, 0, 0, fan16), + PREAD(fan2_input, 1, PRI_HIGH, 0x2b, 0x2a, 0, 0, fan16), + PREAD(fan3_input, 2, PRI_HIGH, 0x2d, 0x2c, 0, 0, fan16), + PREAD(fan4_input, 3, PRI_HIGH, 0x2f, 0x2e, 0, 0, fan16), + + PWRITE(fan1_min, 0, PRI_LOW, 0x55, 0x54, 0, 0, fan16), + PWRITE(fan2_min, 1, PRI_LOW, 0x57, 0x56, 0, 0, fan16), + PWRITE(fan3_min, 2, PRI_LOW, 0x59, 0x58, 0, 0, fan16), + PWRITE(fan4_min, 3, PRI_LOW, 0x5b, 0x5a, 0, 0, fan16), + + PREAD(fan1_alarm, 0, PRI_HIGH, 0x42, 0, 0x01, 2, bitmask), + PREAD(fan2_alarm, 1, PRI_HIGH, 0x42, 0, 0x01, 3, bitmask), + PREAD(fan3_alarm, 2, PRI_HIGH, 0x42, 0, 0x01, 4, bitmask), + PREAD(fan4_alarm, 3, PRI_HIGH, 0x42, 0, 0x01, 5, bitmask), + + PREAD(temp1_input, 0, PRI_HIGH, 0x25, 0x10, 0, 0, temp10), + PREAD(temp2_input, 1, PRI_HIGH, 0x26, 0x15, 0, 0, temp10), + PREAD(temp3_input, 2, PRI_HIGH, 0x27, 0x16, 0, 0, temp10), + PREAD(temp4_input, 3, PRI_HIGH, 0x33, 0x17, 0, 0, temp10), + PREAD(temp5_input, 4, PRI_HIGH, 0xf7, 0xf6, 0, 0, temp10), + PREAD(temp6_input, 5, PRI_HIGH, 0xf9, 0xf8, 0, 0, temp10), + PREAD(temp7_input, 6, PRI_HIGH, 0xfb, 0xfa, 0, 0, temp10), + PREAD(temp8_input, 7, PRI_HIGH, 0xfd, 0xfc, 0, 0, temp10), + + PWRITE(temp1_min, 0, PRI_LOW, 0x4e, 0, 0, 0, temp8), + PWRITE(temp2_min, 1, PRI_LOW, 0x50, 0, 0, 0, temp8), + PWRITE(temp3_min, 2, PRI_LOW, 0x52, 0, 0, 0, temp8), + PWRITE(temp4_min, 3, PRI_LOW, 0x34, 0, 0, 0, temp8), + + PWRITE(temp1_max, 0, PRI_LOW, 0x4f, 0, 0, 0, temp8), + PWRITE(temp2_max, 1, PRI_LOW, 0x51, 0, 0, 0, temp8), + PWRITE(temp3_max, 2, PRI_LOW, 0x53, 0, 0, 0, temp8), + PWRITE(temp4_max, 3, PRI_LOW, 0x35, 0, 0, 0, temp8), + + PREAD(temp1_alarm, 0, PRI_HIGH, 0x41, 0, 0x01, 4, bitmask), + PREAD(temp2_alarm, 1, PRI_HIGH, 0x41, 0, 0x01, 5, bitmask), + PREAD(temp3_alarm, 2, PRI_HIGH, 0x41, 0, 0x01, 6, bitmask), + PREAD(temp4_alarm, 3, PRI_HIGH, 0x43, 0, 0x01, 0, bitmask), + + PWRITE(temp1_source, 0, PRI_LOW, 0x02, 0, 0x07, 4, bitmask), + PWRITE(temp2_source, 1, PRI_LOW, 0x02, 0, 0x07, 0, bitmask), + PWRITE(temp3_source, 2, PRI_LOW, 0x03, 0, 0x07, 4, bitmask), + PWRITE(temp4_source, 3, PRI_LOW, 0x03, 0, 0x07, 0, bitmask), + + PWRITE(temp1_smoothing_enable, 0, PRI_LOW, 0x62, 0, 0x01, 3, bitmask), + PWRITE(temp2_smoothing_enable, 1, PRI_LOW, 0x63, 0, 0x01, 7, bitmask), + PWRITE(temp3_smoothing_enable, 2, PRI_LOW, 0x63, 0, 0x01, 3, bitmask), + PWRITE(temp4_smoothing_enable, 3, PRI_LOW, 0x3c, 0, 0x01, 3, bitmask), + + PWRITE(temp1_smoothing_time, 0, PRI_LOW, 0x62, 0, 0x07, 0, temp_st), + PWRITE(temp2_smoothing_time, 1, PRI_LOW, 0x63, 0, 0x07, 4, temp_st), + PWRITE(temp3_smoothing_time, 2, PRI_LOW, 0x63, 0, 0x07, 0, temp_st), + PWRITE(temp4_smoothing_time, 3, PRI_LOW, 0x3c, 0, 0x07, 0, temp_st), + + PWRITE(temp1_auto_point1_temp_hyst, 0, PRI_LOW, 0x6d, 0, 0x0f, 4, + bitmask), + PWRITE(temp2_auto_point1_temp_hyst, 1, PRI_LOW, 0x6d, 0, 0x0f, 0, + bitmask), + PWRITE(temp3_auto_point1_temp_hyst, 2, PRI_LOW, 0x6e, 0, 0x0f, 4, + bitmask), + PWRITE(temp4_auto_point1_temp_hyst, 3, PRI_LOW, 0x6e, 0, 0x0f, 0, + bitmask), + + PREAD(temp1_auto_point2_temp_hyst, 0, PRI_LOW, 0x6d, 0, 0x0f, 4, + bitmask), + PREAD(temp2_auto_point2_temp_hyst, 1, PRI_LOW, 0x6d, 0, 0x0f, 0, + bitmask), + PREAD(temp3_auto_point2_temp_hyst, 2, PRI_LOW, 0x6e, 0, 0x0f, 4, + bitmask), + PREAD(temp4_auto_point2_temp_hyst, 3, PRI_LOW, 0x6e, 0, 0x0f, 0, + bitmask), + + PWRITE(temp1_auto_point1_temp, 0, PRI_LOW, 0x67, 0, 0, 0, temp8), + PWRITE(temp2_auto_point1_temp, 1, PRI_LOW, 0x68, 0, 0, 0, temp8), + PWRITE(temp3_auto_point1_temp, 2, PRI_LOW, 0x69, 0, 0, 0, temp8), + PWRITE(temp4_auto_point1_temp, 3, PRI_LOW, 0x3b, 0, 0, 0, temp8), + + PWRITEM(temp1_auto_point2_temp, 0, PRI_LOW, VAA(0x5f, 0x67), VAA(0), + VAA(0x0f), VAA(4), ap2_temp), + PWRITEM(temp2_auto_point2_temp, 1, PRI_LOW, VAA(0x60, 0x68), VAA(0), + VAA(0x0f), VAA(4), ap2_temp), + PWRITEM(temp3_auto_point2_temp, 2, PRI_LOW, VAA(0x61, 0x69), VAA(0), + VAA(0x0f), VAA(4), ap2_temp), + PWRITEM(temp4_auto_point2_temp, 3, PRI_LOW, VAA(0x3c, 0x3b), VAA(0), + VAA(0x0f), VAA(4), ap2_temp), + + PWRITE(temp1_crit, 0, PRI_LOW, 0x6a, 0, 0, 0, temp8), + PWRITE(temp2_crit, 1, PRI_LOW, 0x6b, 0, 0, 0, temp8), + PWRITE(temp3_crit, 2, PRI_LOW, 0x6c, 0, 0, 0, temp8), + PWRITE(temp4_crit, 3, PRI_LOW, 0x3d, 0, 0, 0, temp8), + + PWRITE(temp5_enable, 4, PRI_LOW, 0x0e, 0, 0x01, 0, bitmask), + PWRITE(temp6_enable, 5, PRI_LOW, 0x0e, 0, 0x01, 1, bitmask), + PWRITE(temp7_enable, 6, PRI_LOW, 0x0e, 0, 0x01, 2, bitmask), + PWRITE(temp8_enable, 7, PRI_LOW, 0x0e, 0, 0x01, 3, bitmask), + + PWRITE(remote1_offset, 0, PRI_LOW, 0x1c, 0, 0, 0, temp62), + PWRITE(remote2_offset, 1, PRI_LOW, 0x1d, 0, 0, 0, temp62), + + PWRITE(pwm1, 0, PRI_HIGH, 0x30, 0, 0, 0, u8), + PWRITE(pwm2, 1, PRI_HIGH, 0x31, 0, 0, 0, u8), + PWRITE(pwm3, 2, PRI_HIGH, 0x32, 0, 0, 0, u8), + + PWRITE(pwm1_invert, 0, PRI_LOW, 0x5c, 0, 0x01, 4, bitmask), + PWRITE(pwm2_invert, 1, PRI_LOW, 0x5d, 0, 0x01, 4, bitmask), + PWRITE(pwm3_invert, 2, PRI_LOW, 0x5e, 0, 0x01, 4, bitmask), + + PWRITEM(pwm1_enable, 0, PRI_LOW, VAA(0x5c, 0x5c, 0x62), VAA(0, 0, 0), + VAA(0x07, 0x01, 0x01), VAA(5, 3, 5), pwm_enable), + PWRITEM(pwm2_enable, 1, PRI_LOW, VAA(0x5d, 0x5d, 0x62), VAA(0, 0, 0), + VAA(0x07, 0x01, 0x01), VAA(5, 3, 6), pwm_enable), + PWRITEM(pwm3_enable, 2, PRI_LOW, VAA(0x5e, 0x5e, 0x62), VAA(0, 0, 0), + VAA(0x07, 0x01, 0x01), VAA(5, 3, 7), pwm_enable), + + PWRITEM(pwm1_auto_channels, 0, PRI_LOW, VAA(0x5c, 0x5c), VAA(0, 0), + VAA(0x07, 0x01), VAA(5, 3), pwm_ac), + PWRITEM(pwm2_auto_channels, 1, PRI_LOW, VAA(0x5d, 0x5d), VAA(0, 0), + VAA(0x07, 0x01), VAA(5, 3), pwm_ac), + PWRITEM(pwm3_auto_channels, 2, PRI_LOW, VAA(0x5e, 0x5e), VAA(0, 0), + VAA(0x07, 0x01), VAA(5, 3), pwm_ac), + + PWRITE(pwm1_auto_point1_pwm, 0, PRI_LOW, 0x64, 0, 0, 0, u8), + PWRITE(pwm2_auto_point1_pwm, 1, PRI_LOW, 0x65, 0, 0, 0, u8), + PWRITE(pwm3_auto_point1_pwm, 2, PRI_LOW, 0x66, 0, 0, 0, u8), + + PWRITE(pwm1_auto_point2_pwm, 0, PRI_LOW, 0x38, 0, 0, 0, u8), + PWRITE(pwm2_auto_point2_pwm, 1, PRI_LOW, 0x39, 0, 0, 0, u8), + PWRITE(pwm3_auto_point2_pwm, 2, PRI_LOW, 0x3a, 0, 0, 0, u8), + + PWRITE(pwm1_freq, 0, PRI_LOW, 0x5f, 0, 0x0f, 0, pwm_freq), + PWRITE(pwm2_freq, 1, PRI_LOW, 0x60, 0, 0x0f, 0, pwm_freq), + PWRITE(pwm3_freq, 2, PRI_LOW, 0x61, 0, 0x0f, 0, pwm_freq), + + PREAD(pwm1_auto_zone_assigned, 0, PRI_LOW, 0, 0, 0x03, 2, bitmask), + PREAD(pwm2_auto_zone_assigned, 1, PRI_LOW, 0, 0, 0x03, 4, bitmask), + PREAD(pwm3_auto_zone_assigned, 2, PRI_LOW, 0, 0, 0x03, 6, bitmask), + + PWRITE(pwm1_auto_spinup_time, 0, PRI_LOW, 0x5c, 0, 0x07, 0, pwm_ast), + PWRITE(pwm2_auto_spinup_time, 1, PRI_LOW, 0x5d, 0, 0x07, 0, pwm_ast), + PWRITE(pwm3_auto_spinup_time, 2, PRI_LOW, 0x5e, 0, 0x07, 0, pwm_ast), + + PWRITE(peci_enable, 0, PRI_LOW, 0x40, 0, 0x01, 4, bitmask), + PWRITE(peci_avg, 0, PRI_LOW, 0x36, 0, 0x07, 0, bitmask), + PWRITE(peci_domain, 0, PRI_LOW, 0x36, 0, 0x01, 3, bitmask), + PWRITE(peci_legacy, 0, PRI_LOW, 0x36, 0, 0x01, 4, bitmask), + PWRITE(peci_diode, 0, PRI_LOW, 0x0e, 0, 0x07, 4, bitmask), + PWRITE(peci_4domain, 0, PRI_LOW, 0x0e, 0, 0x01, 4, bitmask), + +}; + +static struct asc7621_data *asc7621_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct asc7621_data *data = i2c_get_clientdata(client); + int i; + +/* + * The asc7621 chips guarantee consistent reads of multi-byte values + * regardless of the order of the reads. No special logic is needed + * so we can just read the registers in whatever order they appear + * in the asc7621_params array. + */ + + mutex_lock(&data->update_lock); + + /* Read all the high priority registers */ + + if (!data->valid || + time_after(jiffies, data->last_high_reading + INTERVAL_HIGH)) { + + for (i = 0; i < ARRAY_SIZE(asc7621_register_priorities); i++) { + if (asc7621_register_priorities[i] == PRI_HIGH) { + data->reg[i] = + i2c_smbus_read_byte_data(client, i) & 0xff; + } + } + data->last_high_reading = jiffies; + } /* last_reading */ + + /* Read all the low priority registers. */ + + if (!data->valid || + time_after(jiffies, data->last_low_reading + INTERVAL_LOW)) { + + for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) { + if (asc7621_register_priorities[i] == PRI_LOW) { + data->reg[i] = + i2c_smbus_read_byte_data(client, i) & 0xff; + } + } + data->last_low_reading = jiffies; + } /* last_reading */ + + data->valid = 1; + + mutex_unlock(&data->update_lock); + + return data; +} + +/* + * Standard detection and initialization below + * + * Helper function that checks if an address is valid + * for a particular chip. + */ + +static inline int valid_address_for_chip(int chip_type, int address) +{ + int i; + + for (i = 0; asc7621_chips[chip_type].addresses[i] != I2C_CLIENT_END; + i++) { + if (asc7621_chips[chip_type].addresses[i] == address) + return 1; + } + return 0; +} + +static void asc7621_init_client(struct i2c_client *client) +{ + int value; + + /* Warn if part was not "READY" */ + + value = read_byte(client, 0x40); + + if (value & 0x02) { + dev_err(&client->dev, + "Client (%d,0x%02x) config is locked.\n", + i2c_adapter_id(client->adapter), client->addr); + } + if (!(value & 0x04)) { + dev_err(&client->dev, "Client (%d,0x%02x) is not ready.\n", + i2c_adapter_id(client->adapter), client->addr); + } + +/* + * Start monitoring + * + * Try to clear LOCK, Set START, save everything else + */ + value = (value & ~0x02) | 0x01; + write_byte(client, 0x40, value & 0xff); + +} + +static int +asc7621_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct asc7621_data *data; + int i, err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + data = devm_kzalloc(&client->dev, sizeof(struct asc7621_data), + GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* Initialize the asc7621 chip */ + asc7621_init_client(client); + + /* Create the sysfs entries */ + for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) { + err = + device_create_file(&client->dev, + &(asc7621_params[i].sda.dev_attr)); + if (err) + goto exit_remove; + } + + data->class_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_remove; + } + + return 0; + +exit_remove: + for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) { + device_remove_file(&client->dev, + &(asc7621_params[i].sda.dev_attr)); + } + + return err; +} + +static int asc7621_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + int company, verstep, chip_index; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + for (chip_index = FIRST_CHIP; chip_index <= LAST_CHIP; chip_index++) { + + if (!valid_address_for_chip(chip_index, client->addr)) + continue; + + company = read_byte(client, + asc7621_chips[chip_index].company_reg); + verstep = read_byte(client, + asc7621_chips[chip_index].verstep_reg); + + if (company == asc7621_chips[chip_index].company_id && + verstep == asc7621_chips[chip_index].verstep_id) { + strlcpy(info->type, asc7621_chips[chip_index].name, + I2C_NAME_SIZE); + + dev_info(&adapter->dev, "Matched %s at 0x%02x\n", + asc7621_chips[chip_index].name, client->addr); + return 0; + } + } + + return -ENODEV; +} + +static int asc7621_remove(struct i2c_client *client) +{ + struct asc7621_data *data = i2c_get_clientdata(client); + int i; + + hwmon_device_unregister(data->class_dev); + + for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) { + device_remove_file(&client->dev, + &(asc7621_params[i].sda.dev_attr)); + } + + return 0; +} + +static const struct i2c_device_id asc7621_id[] = { + {"asc7621", asc7621}, + {"asc7621a", asc7621a}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, asc7621_id); + +static struct i2c_driver asc7621_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "asc7621", + }, + .probe = asc7621_probe, + .remove = asc7621_remove, + .id_table = asc7621_id, + .detect = asc7621_detect, + .address_list = normal_i2c, +}; + +static int __init sm_asc7621_init(void) +{ + int i, j; +/* + * Collect all the registers needed into a single array. + * This way, if a register isn't actually used for anything, + * we don't retrieve it. + */ + + for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) { + for (j = 0; j < ARRAY_SIZE(asc7621_params[i].msb); j++) + asc7621_register_priorities[asc7621_params[i].msb[j]] = + asc7621_params[i].priority; + for (j = 0; j < ARRAY_SIZE(asc7621_params[i].lsb); j++) + asc7621_register_priorities[asc7621_params[i].lsb[j]] = + asc7621_params[i].priority; + } + return i2c_add_driver(&asc7621_driver); +} + +static void __exit sm_asc7621_exit(void) +{ + i2c_del_driver(&asc7621_driver); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("George Joseph"); +MODULE_DESCRIPTION("Andigilog aSC7621 and aSC7621a driver"); + +module_init(sm_asc7621_init); +module_exit(sm_asc7621_exit); diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index 028284f544e..ae208f61219 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -5,21 +5,44 @@ * See COPYING in the top level directory of the kernel tree. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/debugfs.h> #include <linux/kernel.h> #include <linux/hwmon.h> #include <linux/list.h> #include <linux/module.h> +#include <linux/slab.h> +#include <linux/dmi.h> +#include <linux/jiffies.h> +#include <linux/err.h> +#include <linux/acpi.h> -#include <acpi/acpi.h> -#include <acpi/acpixf.h> -#include <acpi/acpi_drivers.h> -#include <acpi/acpi_bus.h> +#define ATK_HID "ATK0110" +static bool new_if; +module_param(new_if, bool, 0); +MODULE_PARM_DESC(new_if, "Override detection heuristic and force the use of the new ATK0110 interface"); -#define ATK_HID "ATK0110" +static const struct dmi_system_id __initconst atk_force_new_if[] = { + { + /* Old interface has broken MCH temp monitoring */ + .ident = "Asus Sabertooth X58", + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "SABERTOOTH X58") + } + }, { + /* Old interface reads the same sensor for fan0 and fan1 */ + .ident = "Asus M5A78L", + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "M5A78L") + } + }, + { } +}; -/* Minimum time between readings, enforced in order to avoid +/* + * Minimum time between readings, enforced in order to avoid * hogging the CPU. */ #define CACHE_TIME HZ @@ -91,7 +114,7 @@ struct atk_data { acpi_handle rtmp_handle; acpi_handle rvlt_handle; acpi_handle rfan_handle; - /* new inteface */ + /* new interface */ acpi_handle enumerate_handle; acpi_handle read_handle; acpi_handle write_handle; @@ -142,7 +165,8 @@ struct atk_sensor_data { char const *acpi_name; }; -/* Return buffer format: +/* + * Return buffer format: * [0-3] "value" is valid flag * [4-7] value * [8- ] unknown stuff on newer mobos @@ -161,7 +185,7 @@ struct atk_acpi_input_buf { }; static int atk_add(struct acpi_device *device); -static int atk_remove(struct acpi_device *device, int type); +static int atk_remove(struct acpi_device *device); static void atk_print_sensor(struct atk_data *data, union acpi_object *obj); static int atk_read_value(struct atk_sensor_data *sensor, u64 *value); static void atk_free_sensors(struct atk_data *data); @@ -249,6 +273,7 @@ static struct device_attribute atk_name_attr = static void atk_init_attribute(struct device_attribute *attr, char *name, sysfs_show_func show) { + sysfs_attr_init(&attr->attr); attr->attr.name = name; attr->attr.mode = 0444; attr->show = show; @@ -290,7 +315,8 @@ static union acpi_object *atk_get_pack_member(struct atk_data *data, } -/* New package format is: +/* + * New package format is: * - flag (int) * class - used for de-muxing the request to the correct GITn * type (volt, temp, fan) @@ -593,7 +619,8 @@ static int atk_read_value_new(struct atk_sensor_data *sensor, u64 *value) buf = (struct atk_acpi_ret_buffer *)obj->buffer.pointer; if (buf->flags == 0) { - /* The reading is not valid, possible causes: + /* + * The reading is not valid, possible causes: * - sensor failure * - enumeration was FUBAR (and we didn't notice) */ @@ -654,6 +681,7 @@ static int atk_debugfs_gitm_get(void *p, u64 *val) else err = -EIO; + ACPI_FREE(ret); return err; } @@ -761,6 +789,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) @@ -930,7 +959,6 @@ static int atk_add_sensor(struct atk_data *data, union acpi_object *obj) return 1; out: - kfree(sensor->acpi_name); kfree(sensor); return err; } @@ -1289,12 +1317,16 @@ static int atk_probe_if(struct atk_data *data) dev_dbg(dev, "method " METHOD_WRITE " not found: %s\n", acpi_format_exception(status)); - /* Check for hwmon methods: first check "old" style methods; note that + /* + * Check for hwmon methods: first check "old" style methods; note that * both may be present: in this case we stick to the old interface; * analysis of multiple DSDTs indicates that when both interfaces * are present the new one (GGRP/GITM) is not functional. */ - if (data->rtmp_handle && data->rvlt_handle && data->rfan_handle) + if (new_if) + dev_info(dev, "Overriding interface detection\n"); + if (data->rtmp_handle && + data->rvlt_handle && data->rfan_handle && !new_if) data->old_interface = true; else if (data->enumerate_handle && data->read_handle && data->write_handle) @@ -1379,7 +1411,7 @@ out: return err; } -static int atk_remove(struct acpi_device *device, int type) +static int atk_remove(struct acpi_device *device) { struct atk_data *data = device->driver_data; dev_dbg(&device->dev, "removing...\n"); @@ -1406,9 +1438,18 @@ static int __init atk0110_init(void) { int ret; + /* Make sure it's safe to access the device through ACPI */ + if (!acpi_resources_are_enforced()) { + pr_err("Resources not safely usable due to acpi_enforce_resources kernel parameter\n"); + return -EBUSY; + } + + if (dmi_check_system(atk_force_new_if)) + new_if = true; + ret = acpi_bus_register_driver(&atk_driver); if (ret) - pr_info("atk: acpi_bus_register_driver failed: %d\n", ret); + pr_info("acpi_bus_register_driver failed: %d\n", ret); return ret; } diff --git a/drivers/hwmon/atxp1.c b/drivers/hwmon/atxp1.c index 94cadc19f0c..2ae8a304b5e 100644 --- a/drivers/hwmon/atxp1.c +++ b/drivers/hwmon/atxp1.c @@ -1,22 +1,22 @@ /* - atxp1.c - kernel module for setting CPU VID and general purpose - I/Os using the Attansic ATXP1 chip. - - 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. - -*/ + * atxp1.c - kernel module for setting CPU VID and general purpose + * I/Os using the Attansic ATXP1 chip. + * + * 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/init.h> @@ -28,6 +28,7 @@ #include <linux/err.h> #include <linux/mutex.h> #include <linux/sysfs.h> +#include <linux/slab.h> MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("System voltages control via Attansic ATXP1"); @@ -44,30 +45,6 @@ MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>"); static const unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END }; -static int atxp1_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int atxp1_remove(struct i2c_client *client); -static struct atxp1_data * atxp1_update_device(struct device *dev); -static int atxp1_detect(struct i2c_client *client, struct i2c_board_info *info); - -static const struct i2c_device_id atxp1_id[] = { - { "atxp1", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, atxp1_id); - -static struct i2c_driver atxp1_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "atxp1", - }, - .probe = atxp1_probe, - .remove = atxp1_remove, - .id_table = atxp1_id, - .detect = atxp1_detect, - .address_list = normal_i2c, -}; - struct atxp1_data { struct device *hwmon_dev; struct mutex update_lock; @@ -82,7 +59,7 @@ struct atxp1_data { u8 vrm; /* Detected CPU VRM */ }; -static struct atxp1_data * atxp1_update_device(struct device *dev) +static struct atxp1_data *atxp1_update_device(struct device *dev) { struct i2c_client *client; struct atxp1_data *data; @@ -96,7 +73,8 @@ static struct atxp1_data * atxp1_update_device(struct device *dev) /* Update local register data */ data->reg.vid = i2c_smbus_read_byte_data(client, ATXP1_VID); - data->reg.cpu_vid = i2c_smbus_read_byte_data(client, ATXP1_CVID); + data->reg.cpu_vid = i2c_smbus_read_byte_data(client, + ATXP1_CVID); data->reg.gpio1 = i2c_smbus_read_byte_data(client, ATXP1_GPIO1); data->reg.gpio2 = i2c_smbus_read_byte_data(client, ATXP1_GPIO2); @@ -105,45 +83,55 @@ static struct atxp1_data * atxp1_update_device(struct device *dev) mutex_unlock(&data->update_lock); - return(data); + return data; } /* sys file functions for cpu0_vid */ -static ssize_t atxp1_showvcore(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t atxp1_showvcore(struct device *dev, + struct device_attribute *attr, char *buf) { int size; struct atxp1_data *data; data = atxp1_update_device(dev); - size = sprintf(buf, "%d\n", vid_from_reg(data->reg.vid & ATXP1_VIDMASK, data->vrm)); + size = sprintf(buf, "%d\n", vid_from_reg(data->reg.vid & ATXP1_VIDMASK, + data->vrm)); return size; } -static ssize_t atxp1_storevcore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t atxp1_storevcore(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { struct atxp1_data *data; struct i2c_client *client; int vid, cvid; - unsigned int vcore; + unsigned long vcore; + int err; client = to_i2c_client(dev); data = atxp1_update_device(dev); - vcore = simple_strtoul(buf, NULL, 10); + err = kstrtoul(buf, 10, &vcore); + if (err) + return err; + vcore /= 25; vcore *= 25; /* Calculate VID */ vid = vid_to_reg(vcore, data->vrm); - if (vid < 0) { dev_err(dev, "VID calculation failed.\n"); - return -1; + return vid; } - /* If output enabled, use control register value. Otherwise original CPU VID */ + /* + * If output enabled, use control register value. + * Otherwise original CPU VID + */ if (data->reg.vid & ATXP1_VIDENA) cvid = data->reg.vid & ATXP1_VIDMASK; else @@ -153,18 +141,17 @@ static ssize_t atxp1_storevcore(struct device *dev, struct device_attribute *att if (vid == cvid) return count; - dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n", vcore, vid); + dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n", (int)vcore, vid); /* Write every 25 mV step to increase stability */ if (cvid > vid) { - for (; cvid >= vid; cvid--) { - i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA); - } - } - else { - for (; cvid <= vid; cvid++) { - i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA); - } + for (; cvid >= vid; cvid--) + i2c_smbus_write_byte_data(client, + ATXP1_VID, cvid | ATXP1_VIDENA); + } else { + for (; cvid <= vid; cvid++) + i2c_smbus_write_byte_data(client, + ATXP1_VID, cvid | ATXP1_VIDENA); } data->valid = 0; @@ -172,13 +159,16 @@ static ssize_t atxp1_storevcore(struct device *dev, struct device_attribute *att return count; } -/* CPU core reference voltage - unit: millivolt -*/ -static DEVICE_ATTR(cpu0_vid, S_IRUGO | S_IWUSR, atxp1_showvcore, atxp1_storevcore); +/* + * CPU core reference voltage + * unit: millivolt + */ +static DEVICE_ATTR(cpu0_vid, S_IRUGO | S_IWUSR, atxp1_showvcore, + atxp1_storevcore); /* sys file functions for GPIO1 */ -static ssize_t atxp1_showgpio1(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t atxp1_showgpio1(struct device *dev, + struct device_attribute *attr, char *buf) { int size; struct atxp1_data *data; @@ -190,21 +180,26 @@ static ssize_t atxp1_showgpio1(struct device *dev, struct device_attribute *attr return size; } -static ssize_t atxp1_storegpio1(struct device *dev, struct device_attribute *attr, const char*buf, size_t count) +static ssize_t atxp1_storegpio1(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) { struct atxp1_data *data; struct i2c_client *client; - unsigned int value; + unsigned long value; + int err; client = to_i2c_client(dev); data = atxp1_update_device(dev); - value = simple_strtoul(buf, NULL, 16); + err = kstrtoul(buf, 16, &value); + if (err) + return err; value &= ATXP1_GPIO1MASK; if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) { - dev_info(dev, "Writing 0x%x to GPIO1.\n", value); + dev_info(dev, "Writing 0x%x to GPIO1.\n", (unsigned int)value); i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value); @@ -214,13 +209,15 @@ static ssize_t atxp1_storegpio1(struct device *dev, struct device_attribute *att return count; } -/* GPIO1 data register - unit: Four bit as hex (e.g. 0x0f) -*/ +/* + * GPIO1 data register + * unit: Four bit as hex (e.g. 0x0f) + */ static DEVICE_ATTR(gpio1, S_IRUGO | S_IWUSR, atxp1_showgpio1, atxp1_storegpio1); /* sys file functions for GPIO2 */ -static ssize_t atxp1_showgpio2(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t atxp1_showgpio2(struct device *dev, + struct device_attribute *attr, char *buf) { int size; struct atxp1_data *data; @@ -232,19 +229,22 @@ static ssize_t atxp1_showgpio2(struct device *dev, struct device_attribute *attr return size; } -static ssize_t atxp1_storegpio2(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t atxp1_storegpio2(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct atxp1_data *data; - struct i2c_client *client; - unsigned int value; - - client = to_i2c_client(dev); - data = atxp1_update_device(dev); + struct atxp1_data *data = atxp1_update_device(dev); + struct i2c_client *client = to_i2c_client(dev); + unsigned long value; + int err; - value = simple_strtoul(buf, NULL, 16) & 0xff; + err = kstrtoul(buf, 16, &value); + if (err) + return err; + value &= 0xff; if (value != data->reg.gpio2) { - dev_info(dev, "Writing 0x%x to GPIO1.\n", value); + dev_info(dev, "Writing 0x%x to GPIO1.\n", (unsigned int)value); i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value); @@ -254,9 +254,10 @@ static ssize_t atxp1_storegpio2(struct device *dev, struct device_attribute *att return count; } -/* GPIO2 data register - unit: Eight bit as hex (e.g. 0xff) -*/ +/* + * GPIO2 data register + * unit: Eight bit as hex (e.g. 0xff) + */ static DEVICE_ATTR(gpio2, S_IRUGO | S_IWUSR, atxp1_showgpio2, atxp1_storegpio2); static struct attribute *atxp1_attributes[] = { @@ -289,8 +290,10 @@ static int atxp1_detect(struct i2c_client *new_client, (i2c_smbus_read_byte_data(new_client, 0xff) == 0))) return -ENODEV; - /* No vendor ID, now checking if registers 0x10,0x11 (non-existent) - * showing the same as register 0x00 */ + /* + * No vendor ID, now checking if registers 0x10,0x11 (non-existent) + * showing the same as register 0x00 + */ temp = i2c_smbus_read_byte_data(new_client, 0x00); if (!((i2c_smbus_read_byte_data(new_client, 0x10) == temp) && @@ -317,23 +320,21 @@ static int atxp1_probe(struct i2c_client *new_client, struct atxp1_data *data; int err; - data = kzalloc(sizeof(struct atxp1_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&new_client->dev, sizeof(struct atxp1_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; /* Get VRM */ data->vrm = vid_which_vrm(); i2c_set_clientdata(new_client, data); - data->valid = 0; - mutex_init(&data->update_lock); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&new_client->dev.kobj, &atxp1_group))) - goto exit_free; + err = sysfs_create_group(&new_client->dev.kobj, &atxp1_group); + if (err) + return err; data->hwmon_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->hwmon_dev)) { @@ -348,33 +349,35 @@ static int atxp1_probe(struct i2c_client *new_client, exit_remove_files: sysfs_remove_group(&new_client->dev.kobj, &atxp1_group); -exit_free: - kfree(data); -exit: return err; }; static int atxp1_remove(struct i2c_client *client) { - struct atxp1_data * data = i2c_get_clientdata(client); + struct atxp1_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &atxp1_group); - kfree(data); - return 0; }; -static int __init atxp1_init(void) -{ - return i2c_add_driver(&atxp1_driver); +static const struct i2c_device_id atxp1_id[] = { + { "atxp1", 0 }, + { } }; +MODULE_DEVICE_TABLE(i2c, atxp1_id); -static void __exit atxp1_exit(void) -{ - i2c_del_driver(&atxp1_driver); +static struct i2c_driver atxp1_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "atxp1", + }, + .probe = atxp1_probe, + .remove = atxp1_remove, + .id_table = atxp1_id, + .detect = atxp1_detect, + .address_list = normal_i2c, }; -module_init(atxp1_init); -module_exit(atxp1_exit); +module_i2c_driver(atxp1_driver); diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 2d7bceeed0b..d76f0b70c6e 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -20,8 +20,9 @@ * 02110-1301 USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> -#include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/jiffies.h> @@ -33,127 +34,209 @@ #include <linux/list.h> #include <linux/platform_device.h> #include <linux/cpu.h> +#include <linux/smp.h> +#include <linux/moduleparam.h> #include <linux/pci.h> #include <asm/msr.h> #include <asm/processor.h> +#include <asm/cpu_device_id.h> #define DRVNAME "coretemp" -typedef enum { SHOW_TEMP, SHOW_TJMAX, SHOW_TTARGET, SHOW_LABEL, - SHOW_NAME } SHOW; - /* - * Functions declaration + * force_tjmax only matters when TjMax can't be read from the CPU itself. + * When set, it replaces the driver's suboptimal heuristic. */ +static int force_tjmax; +module_param_named(tjmax, force_tjmax, int, 0444); +MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); + +#define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */ +#define NUM_REAL_CORES 32 /* Number of Real cores per cpu */ +#define CORETEMP_NAME_LENGTH 19 /* String Length of attrs */ +#define MAX_CORE_ATTRS 4 /* Maximum no of basic attrs */ +#define TOTAL_ATTRS (MAX_CORE_ATTRS + 1) +#define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO) + +#define TO_PHYS_ID(cpu) (cpu_data(cpu).phys_proc_id) +#define TO_CORE_ID(cpu) (cpu_data(cpu).cpu_core_id) +#define TO_ATTR_NO(cpu) (TO_CORE_ID(cpu) + BASE_SYSFS_ATTR_NO) + +#ifdef CONFIG_SMP +#define for_each_sibling(i, cpu) for_each_cpu(i, cpu_sibling_mask(cpu)) +#else +#define for_each_sibling(i, cpu) for (i = 0; false; ) +#endif -static struct coretemp_data *coretemp_update_device(struct device *dev); - -struct coretemp_data { - struct device *hwmon_dev; - struct mutex update_lock; - const char *name; - u32 id; - char valid; /* zero until following fields are valid */ - unsigned long last_updated; /* in jiffies */ +/* + * Per-Core Temperature Data + * @last_updated: The time when the current temperature value was updated + * earlier (in jiffies). + * @cpu_core_id: The CPU Core from which temperature values should be read + * This value is passed as "id" field to rdmsr/wrmsr functions. + * @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS, + * from where the temperature values should be read. + * @attr_size: Total number of pre-core attrs displayed in the sysfs. + * @is_pkg_data: If this is 1, the temp_data holds pkgtemp data. + * Otherwise, temp_data holds coretemp data. + * @valid: If this is 1, the current temperature is valid. + */ +struct temp_data { int temp; - int tjmax; int ttarget; - u8 alarm; + int tjmax; + unsigned long last_updated; + unsigned int cpu; + u32 cpu_core_id; + u32 status_reg; + int attr_size; + bool is_pkg_data; + bool valid; + struct sensor_device_attribute sd_attrs[TOTAL_ATTRS]; + char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH]; + struct attribute *attrs[TOTAL_ATTRS + 1]; + struct attribute_group attr_group; + struct mutex update_lock; }; -/* - * Sysfs stuff - */ +/* Platform Data per Physical CPU */ +struct platform_data { + struct device *hwmon_dev; + u16 phys_proc_id; + struct temp_data *core_data[MAX_CORE_DATA]; + struct device_attribute name_attr; +}; + +struct pdev_entry { + struct list_head list; + struct platform_device *pdev; + u16 phys_proc_id; +}; + +static LIST_HEAD(pdev_list); +static DEFINE_MUTEX(pdev_list_mutex); -static ssize_t show_name(struct device *dev, struct device_attribute - *devattr, char *buf) +static ssize_t show_label(struct device *dev, + struct device_attribute *devattr, char *buf) { - int ret; struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct coretemp_data *data = dev_get_drvdata(dev); + struct platform_data *pdata = dev_get_drvdata(dev); + struct temp_data *tdata = pdata->core_data[attr->index]; - if (attr->index == SHOW_NAME) - ret = sprintf(buf, "%s\n", data->name); - else /* show label */ - ret = sprintf(buf, "Core %d\n", data->id); - return ret; + if (tdata->is_pkg_data) + return sprintf(buf, "Physical id %u\n", pdata->phys_proc_id); + + return sprintf(buf, "Core %u\n", tdata->cpu_core_id); +} + +static ssize_t show_crit_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + u32 eax, edx; + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct platform_data *pdata = dev_get_drvdata(dev); + struct temp_data *tdata = pdata->core_data[attr->index]; + + rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); + + return sprintf(buf, "%d\n", (eax >> 5) & 1); +} + +static ssize_t show_tjmax(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct platform_data *pdata = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", pdata->core_data[attr->index]->tjmax); } -static ssize_t show_alarm(struct device *dev, struct device_attribute - *devattr, char *buf) +static ssize_t show_ttarget(struct device *dev, + struct device_attribute *devattr, char *buf) { - struct coretemp_data *data = coretemp_update_device(dev); - /* read the Out-of-spec log, never clear */ - return sprintf(buf, "%d\n", data->alarm); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct platform_data *pdata = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", pdata->core_data[attr->index]->ttarget); } static ssize_t show_temp(struct device *dev, - struct device_attribute *devattr, char *buf) + struct device_attribute *devattr, char *buf) { + u32 eax, edx; struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct coretemp_data *data = coretemp_update_device(dev); - int err; + struct platform_data *pdata = dev_get_drvdata(dev); + struct temp_data *tdata = pdata->core_data[attr->index]; + + mutex_lock(&tdata->update_lock); + + /* Check whether the time interval has elapsed */ + if (!tdata->valid || time_after(jiffies, tdata->last_updated + HZ)) { + rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); + /* + * Ignore the valid bit. In all observed cases the register + * value is either low or zero if the valid bit is 0. + * Return it instead of reporting an error which doesn't + * really help at all. + */ + tdata->temp = tdata->tjmax - ((eax >> 16) & 0x7f) * 1000; + tdata->valid = 1; + tdata->last_updated = jiffies; + } - if (attr->index == SHOW_TEMP) - err = data->valid ? sprintf(buf, "%d\n", data->temp) : -EAGAIN; - else if (attr->index == SHOW_TJMAX) - err = sprintf(buf, "%d\n", data->tjmax); - else - err = sprintf(buf, "%d\n", data->ttarget); - return err; + mutex_unlock(&tdata->update_lock); + return sprintf(buf, "%d\n", tdata->temp); } -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, - SHOW_TEMP); -static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, NULL, - SHOW_TJMAX); -static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, NULL, - SHOW_TTARGET); -static DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL); -static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL); -static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, SHOW_NAME); - -static struct attribute *coretemp_attributes[] = { - &sensor_dev_attr_name.dev_attr.attr, - &sensor_dev_attr_temp1_label.dev_attr.attr, - &dev_attr_temp1_crit_alarm.attr, - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp1_crit.dev_attr.attr, - NULL +struct tjmax_pci { + unsigned int device; + int tjmax; }; -static const struct attribute_group coretemp_group = { - .attrs = coretemp_attributes, +static const struct tjmax_pci tjmax_pci_table[] = { + { 0x0708, 110000 }, /* CE41x0 (Sodaville ) */ + { 0x0c72, 102000 }, /* Atom S1240 (Centerton) */ + { 0x0c73, 95000 }, /* Atom S1220 (Centerton) */ + { 0x0c75, 95000 }, /* Atom S1260 (Centerton) */ }; -static struct coretemp_data *coretemp_update_device(struct device *dev) -{ - struct coretemp_data *data = dev_get_drvdata(dev); - - mutex_lock(&data->update_lock); +struct tjmax { + char const *id; + int tjmax; +}; - if (!data->valid || time_after(jiffies, data->last_updated + HZ)) { - u32 eax, edx; +static const struct tjmax tjmax_table[] = { + { "CPU 230", 100000 }, /* Model 0x1c, stepping 2 */ + { "CPU 330", 125000 }, /* Model 0x1c, stepping 2 */ +}; - data->valid = 0; - rdmsr_on_cpu(data->id, MSR_IA32_THERM_STATUS, &eax, &edx); - data->alarm = (eax >> 5) & 1; - /* update only if data has been valid */ - if (eax & 0x80000000) { - data->temp = data->tjmax - (((eax >> 16) - & 0x7f) * 1000); - data->valid = 1; - } else { - dev_dbg(dev, "Temperature data invalid (0x%x)\n", eax); - } - data->last_updated = jiffies; - } +struct tjmax_model { + u8 model; + u8 mask; + int tjmax; +}; - mutex_unlock(&data->update_lock); - return data; -} +#define ANY 0xff + +static const struct tjmax_model tjmax_model_table[] = { + { 0x1c, 10, 100000 }, /* D4xx, K4xx, N4xx, D5xx, K5xx, N5xx */ + { 0x1c, ANY, 90000 }, /* Z5xx, N2xx, possibly others + * Note: Also matches 230 and 330, + * which are covered by tjmax_table + */ + { 0x26, ANY, 90000 }, /* Atom Tunnel Creek (Exx), Lincroft (Z6xx) + * Note: TjMax for E6xxT is 110C, but CPU type + * is undetectable by software + */ + { 0x27, ANY, 90000 }, /* Atom Medfield (Z2460) */ + { 0x35, ANY, 90000 }, /* Atom Clover Trail/Cloverview (Z27x0) */ + { 0x36, ANY, 100000 }, /* Atom Cedar Trail/Cedarview (N2xxx, D2xxx) + * Also matches S12x0 (stepping 9), covered by + * PCI table + */ +}; -static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) +static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) { /* The 100C is default for both mobile and non mobile CPUs */ @@ -162,39 +245,46 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device * int usemsr_ee = 1; int err; u32 eax, edx; - struct pci_dev *host_bridge; + int i; + struct pci_dev *host_bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0)); + + /* + * Explicit tjmax table entries override heuristics. + * First try PCI host bridge IDs, followed by model ID strings + * and model/stepping information. + */ + if (host_bridge && host_bridge->vendor == PCI_VENDOR_ID_INTEL) { + for (i = 0; i < ARRAY_SIZE(tjmax_pci_table); i++) { + if (host_bridge->device == tjmax_pci_table[i].device) + return tjmax_pci_table[i].tjmax; + } + } - /* Early chips have no MSR for TjMax */ + for (i = 0; i < ARRAY_SIZE(tjmax_table); i++) { + if (strstr(c->x86_model_id, tjmax_table[i].id)) + return tjmax_table[i].tjmax; + } - if ((c->x86_model == 0xf) && (c->x86_mask < 4)) { - usemsr_ee = 0; + for (i = 0; i < ARRAY_SIZE(tjmax_model_table); i++) { + const struct tjmax_model *tm = &tjmax_model_table[i]; + if (c->x86_model == tm->model && + (tm->mask == ANY || c->x86_mask == tm->mask)) + return tm->tjmax; } - /* Atom CPUs */ + /* Early chips have no MSR for TjMax */ - if (c->x86_model == 0x1c) { + if (c->x86_model == 0xf && c->x86_mask < 4) usemsr_ee = 0; - host_bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0)); - - if (host_bridge && host_bridge->vendor == PCI_VENDOR_ID_INTEL - && (host_bridge->device == 0xa000 /* NM10 based nettop */ - || host_bridge->device == 0xa010)) /* NM10 based netbook */ - tjmax = 100000; - else - tjmax = 90000; - - pci_dev_put(host_bridge); - } - - if ((c->x86_model > 0xe) && (usemsr_ee)) { + if (c->x86_model > 0xe && usemsr_ee) { u8 platform_id; - /* Now we can detect the mobile CPU using Intel provided table - http://softwarecommunity.intel.com/Wiki/Mobility/720.htm - For Core2 cores, check MSR 0x17, bit 28 1 = Mobile CPU - */ - + /* + * Now we can detect the mobile CPU using Intel provided table + * http://softwarecommunity.intel.com/Wiki/Mobility/720.htm + * For Core2 cores, check MSR 0x17, bit 28 1 = Mobile CPU + */ err = rdmsr_safe_on_cpu(id, 0x17, &eax, &edx); if (err) { dev_warn(dev, @@ -202,20 +292,26 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device * " CPU\n"); usemsr_ee = 0; } else if (c->x86_model < 0x17 && !(eax & 0x10000000)) { - /* Trust bit 28 up to Penryn, I could not find any - documentation on that; if you happen to know - someone at Intel please ask */ + /* + * Trust bit 28 up to Penryn, I could not find any + * documentation on that; if you happen to know + * someone at Intel please ask + */ usemsr_ee = 0; } else { /* Platform ID bits 52:50 (EDX starts at bit 32) */ platform_id = (edx >> 18) & 0x7; - /* Mobile Penryn CPU seems to be platform ID 7 or 5 - (guesswork) */ - if ((c->x86_model == 0x17) && - ((platform_id == 5) || (platform_id == 7))) { - /* If MSR EE bit is set, set it to 90 degrees C, - otherwise 105 degrees C */ + /* + * Mobile Penryn CPU seems to be platform ID 7 or 5 + * (guesswork) + */ + if (c->x86_model == 0x17 && + (platform_id == 5 || platform_id == 7)) { + /* + * If MSR EE bit is set, set it to 90 degrees C, + * otherwise 105 degrees C + */ tjmax_ee = 90000; tjmax = 105000; } @@ -223,119 +319,278 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device * } if (usemsr_ee) { - err = rdmsr_safe_on_cpu(id, 0xee, &eax, &edx); if (err) { dev_warn(dev, "Unable to access MSR 0xEE, for Tjmax, left" - " at default"); + " at default\n"); } else if (eax & 0x40000000) { tjmax = tjmax_ee; } - /* if we dont use msr EE it means we are desktop CPU (with exeception - of Atom) */ } else if (tjmax == 100000) { + /* + * If we don't use msr EE it means we are desktop CPU + * (with exeception of Atom) + */ dev_warn(dev, "Using relative temperature scale!\n"); } return tjmax; } -static int __devinit coretemp_probe(struct platform_device *pdev) +static bool cpu_has_tjmax(struct cpuinfo_x86 *c) +{ + u8 model = c->x86_model; + + return model > 0xe && + model != 0x1c && + model != 0x26 && + model != 0x27 && + model != 0x35 && + model != 0x36; +} + +static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) { - struct coretemp_data *data; - struct cpuinfo_x86 *c = &cpu_data(pdev->id); int err; u32 eax, edx; + u32 val; - if (!(data = kzalloc(sizeof(struct coretemp_data), GFP_KERNEL))) { - err = -ENOMEM; - dev_err(&pdev->dev, "Out of memory\n"); - goto exit; + /* + * A new feature of current Intel(R) processors, the + * IA32_TEMPERATURE_TARGET contains the TjMax value + */ + err = rdmsr_safe_on_cpu(id, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); + if (err) { + if (cpu_has_tjmax(c)) + dev_warn(dev, "Unable to read TjMax from CPU %u\n", id); + } else { + val = (eax >> 16) & 0xff; + /* + * If the TjMax is not plausible, an assumption + * will be used + */ + if (val) { + dev_dbg(dev, "TjMax is %d degrees C\n", val); + return val * 1000; + } } - data->id = pdev->id; - data->name = "coretemp"; - mutex_init(&data->update_lock); + if (force_tjmax) { + dev_notice(dev, "TjMax forced to %d degrees C by user\n", + force_tjmax); + return force_tjmax * 1000; + } - /* test if we can access the THERM_STATUS MSR */ - err = rdmsr_safe_on_cpu(data->id, MSR_IA32_THERM_STATUS, &eax, &edx); - if (err) { - dev_err(&pdev->dev, - "Unable to access THERM_STATUS MSR, giving up\n"); - goto exit_free; + /* + * An assumption is made for early CPUs and unreadable MSR. + * NOTE: the calculated value may not be correct. + */ + return adjust_tjmax(c, id, dev); +} + +static int create_core_attrs(struct temp_data *tdata, struct device *dev, + int attr_no) +{ + int i; + static ssize_t (*const rd_ptr[TOTAL_ATTRS]) (struct device *dev, + struct device_attribute *devattr, char *buf) = { + show_label, show_crit_alarm, show_temp, show_tjmax, + show_ttarget }; + static const char *const names[TOTAL_ATTRS] = { + "temp%d_label", "temp%d_crit_alarm", + "temp%d_input", "temp%d_crit", + "temp%d_max" }; + + for (i = 0; i < tdata->attr_size; i++) { + snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i], + attr_no); + sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr); + tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i]; + tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO; + tdata->sd_attrs[i].dev_attr.show = rd_ptr[i]; + tdata->sd_attrs[i].index = attr_no; + tdata->attrs[i] = &tdata->sd_attrs[i].dev_attr.attr; } + tdata->attr_group.attrs = tdata->attrs; + return sysfs_create_group(&dev->kobj, &tdata->attr_group); +} - /* Check if we have problem with errata AE18 of Core processors: - Readings might stop update when processor visited too deep sleep, - fixed for stepping D0 (6EC). - */ - - 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 = -ENODEV; - dev_err(&pdev->dev, - "Errata AE18 not fixed, update BIOS or " - "microcode of the CPU!\n"); - goto exit_free; - } + +static int chk_ucode_version(unsigned int cpu) +{ + struct cpuinfo_x86 *c = &cpu_data(cpu); + + /* + * Check if we have problem with errata AE18 of Core processors: + * Readings might stop update when processor visited too deep sleep, + * fixed for stepping D0 (6EC). + */ + if (c->x86_model == 0xe && c->x86_mask < 0xc && c->microcode < 0x39) { + pr_err("Errata AE18 not fixed, update BIOS or microcode of the CPU!\n"); + return -ENODEV; } + return 0; +} - data->tjmax = adjust_tjmax(c, data->id, &pdev->dev); - platform_set_drvdata(pdev, data); +static struct platform_device *coretemp_get_pdev(unsigned int cpu) +{ + u16 phys_proc_id = TO_PHYS_ID(cpu); + struct pdev_entry *p; - /* read the still undocumented IA32_TEMPERATURE_TARGET it exists - on older CPUs but not in this register, Atoms don't have it either */ + mutex_lock(&pdev_list_mutex); - if ((c->x86_model > 0xe) && (c->x86_model != 0x1c)) { - err = rdmsr_safe_on_cpu(data->id, 0x1a2, &eax, &edx); - if (err) { - dev_warn(&pdev->dev, "Unable to read" - " IA32_TEMPERATURE_TARGET MSR\n"); - } else { - data->ttarget = data->tjmax - - (((eax >> 8) & 0xff) * 1000); - err = device_create_file(&pdev->dev, - &sensor_dev_attr_temp1_max.dev_attr); - if (err) - goto exit_free; + list_for_each_entry(p, &pdev_list, list) + if (p->phys_proc_id == phys_proc_id) { + mutex_unlock(&pdev_list_mutex); + return p->pdev; } - } - if ((err = sysfs_create_group(&pdev->dev.kobj, &coretemp_group))) - goto exit_dev; + mutex_unlock(&pdev_list_mutex); + return NULL; +} + +static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag) +{ + struct temp_data *tdata; + + tdata = kzalloc(sizeof(struct temp_data), GFP_KERNEL); + if (!tdata) + return NULL; + + tdata->status_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_STATUS : + MSR_IA32_THERM_STATUS; + tdata->is_pkg_data = pkg_flag; + tdata->cpu = cpu; + tdata->cpu_core_id = TO_CORE_ID(cpu); + tdata->attr_size = MAX_CORE_ATTRS; + mutex_init(&tdata->update_lock); + return tdata; +} + +static int create_core_data(struct platform_device *pdev, unsigned int cpu, + int pkg_flag) +{ + struct temp_data *tdata; + struct platform_data *pdata = platform_get_drvdata(pdev); + struct cpuinfo_x86 *c = &cpu_data(cpu); + u32 eax, edx; + int err, attr_no; + + /* + * Find attr number for sysfs: + * We map the attr number to core id of the CPU + * The attr number is always core id + 2 + * The Pkgtemp will always show up as temp1_*, if available + */ + attr_no = pkg_flag ? 1 : TO_ATTR_NO(cpu); + + if (attr_no > MAX_CORE_DATA - 1) + return -ERANGE; + + /* + * Provide a single set of attributes for all HT siblings of a core + * to avoid duplicate sensors (the processor ID and core ID of all + * HT siblings of a core are the same). + * Skip if a HT sibling of this core is already registered. + * This is not an error. + */ + if (pdata->core_data[attr_no] != NULL) + return 0; + + tdata = init_temp_data(cpu, pkg_flag); + if (!tdata) + return -ENOMEM; + + /* Test if we can access the status register */ + err = rdmsr_safe_on_cpu(cpu, tdata->status_reg, &eax, &edx); + if (err) + goto exit_free; - data->hwmon_dev = hwmon_device_register(&pdev->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - dev_err(&pdev->dev, "Class registration failed (%d)\n", - err); - goto exit_class; + /* We can access status register. Get Critical Temperature */ + tdata->tjmax = get_tjmax(c, cpu, &pdev->dev); + + /* + * Read the still undocumented bits 8:15 of IA32_TEMPERATURE_TARGET. + * The target temperature is available on older CPUs but not in this + * register. Atoms don't have the register at all. + */ + if (c->x86_model > 0xe && c->x86_model != 0x1c) { + err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, + &eax, &edx); + if (!err) { + tdata->ttarget + = tdata->tjmax - ((eax >> 8) & 0xff) * 1000; + tdata->attr_size++; + } } - return 0; + pdata->core_data[attr_no] = tdata; + + /* Create sysfs interfaces */ + err = create_core_attrs(tdata, pdata->hwmon_dev, attr_no); + if (err) + goto exit_free; -exit_class: - sysfs_remove_group(&pdev->dev.kobj, &coretemp_group); -exit_dev: - device_remove_file(&pdev->dev, &sensor_dev_attr_temp1_max.dev_attr); + return 0; exit_free: - kfree(data); -exit: + pdata->core_data[attr_no] = NULL; + kfree(tdata); return err; } -static int __devexit coretemp_remove(struct platform_device *pdev) +static void coretemp_add_core(unsigned int cpu, int pkg_flag) +{ + struct platform_device *pdev = coretemp_get_pdev(cpu); + int err; + + if (!pdev) + return; + + err = create_core_data(pdev, cpu, pkg_flag); + if (err) + dev_err(&pdev->dev, "Adding Core %u failed\n", cpu); +} + +static void coretemp_remove_core(struct platform_data *pdata, + int indx) +{ + struct temp_data *tdata = pdata->core_data[indx]; + + /* Remove the sysfs attributes */ + sysfs_remove_group(&pdata->hwmon_dev->kobj, &tdata->attr_group); + + kfree(pdata->core_data[indx]); + pdata->core_data[indx] = NULL; +} + +static int coretemp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct platform_data *pdata; + + /* Initialize the per-package data structures */ + pdata = devm_kzalloc(dev, sizeof(struct platform_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->phys_proc_id = pdev->id; + platform_set_drvdata(pdev, pdata); + + pdata->hwmon_dev = devm_hwmon_device_register_with_groups(dev, DRVNAME, + pdata, NULL); + return PTR_ERR_OR_ZERO(pdata->hwmon_dev); +} + +static int coretemp_remove(struct platform_device *pdev) { - struct coretemp_data *data = platform_get_drvdata(pdev); + struct platform_data *pdata = platform_get_drvdata(pdev); + int i; + + for (i = MAX_CORE_DATA - 1; i >= 0; --i) + if (pdata->core_data[i]) + coretemp_remove_core(pdata, i); - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&pdev->dev.kobj, &coretemp_group); - device_remove_file(&pdev->dev, &sensor_dev_attr_temp1_max.dev_attr); - platform_set_drvdata(pdev, NULL); - kfree(data); return 0; } @@ -345,28 +600,21 @@ static struct platform_driver coretemp_driver = { .name = DRVNAME, }, .probe = coretemp_probe, - .remove = __devexit_p(coretemp_remove), -}; - -struct pdev_entry { - struct list_head list; - struct platform_device *pdev; - unsigned int cpu; + .remove = coretemp_remove, }; -static LIST_HEAD(pdev_list); -static DEFINE_MUTEX(pdev_list_mutex); - -static int __cpuinit coretemp_device_add(unsigned int cpu) +static int coretemp_device_add(unsigned int cpu) { int err; struct platform_device *pdev; struct pdev_entry *pdev_entry; - pdev = platform_device_alloc(DRVNAME, cpu); + mutex_lock(&pdev_list_mutex); + + pdev = platform_device_alloc(DRVNAME, TO_PHYS_ID(cpu)); if (!pdev) { err = -ENOMEM; - printk(KERN_ERR DRVNAME ": Device allocation failed\n"); + pr_err("Device allocation failed\n"); goto exit; } @@ -378,14 +626,13 @@ static int __cpuinit coretemp_device_add(unsigned int cpu) err = platform_device_add(pdev); if (err) { - printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", - err); + pr_err("Device addition failed (%d)\n", err); goto exit_device_free; } pdev_entry->pdev = pdev; - pdev_entry->cpu = cpu; - mutex_lock(&pdev_list_mutex); + pdev_entry->phys_proc_id = pdev->id; + list_add_tail(&pdev_entry->list, &pdev_list); mutex_unlock(&pdev_list_mutex); @@ -396,25 +643,131 @@ exit_device_free: exit_device_put: platform_device_put(pdev); exit: + mutex_unlock(&pdev_list_mutex); return err; } -#ifdef CONFIG_HOTPLUG_CPU static void coretemp_device_remove(unsigned int cpu) { struct pdev_entry *p, *n; + u16 phys_proc_id = TO_PHYS_ID(cpu); + mutex_lock(&pdev_list_mutex); list_for_each_entry_safe(p, n, &pdev_list, list) { - if (p->cpu == cpu) { - platform_device_unregister(p->pdev); - list_del(&p->list); - kfree(p); - } + if (p->phys_proc_id != phys_proc_id) + continue; + platform_device_unregister(p->pdev); + list_del(&p->list); + kfree(p); } mutex_unlock(&pdev_list_mutex); } -static int __cpuinit coretemp_cpu_callback(struct notifier_block *nfb, +static bool is_any_core_online(struct platform_data *pdata) +{ + int i; + + /* Find online cores, except pkgtemp data */ + for (i = MAX_CORE_DATA - 1; i >= 0; --i) { + if (pdata->core_data[i] && + !pdata->core_data[i]->is_pkg_data) { + return true; + } + } + return false; +} + +static void get_core_online(unsigned int cpu) +{ + struct cpuinfo_x86 *c = &cpu_data(cpu); + struct platform_device *pdev = coretemp_get_pdev(cpu); + int err; + + /* + * CPUID.06H.EAX[0] indicates whether the CPU has thermal + * sensors. We check this bit only, all the early CPUs + * without thermal sensors will be filtered out. + */ + if (!cpu_has(c, X86_FEATURE_DTHERM)) + return; + + if (!pdev) { + /* Check the microcode version of the CPU */ + if (chk_ucode_version(cpu)) + return; + + /* + * Alright, we have DTS support. + * We are bringing the _first_ core in this pkg + * online. So, initialize per-pkg data structures and + * then bring this core online. + */ + err = coretemp_device_add(cpu); + if (err) + return; + /* + * Check whether pkgtemp support is available. + * If so, add interfaces for pkgtemp. + */ + if (cpu_has(c, X86_FEATURE_PTS)) + coretemp_add_core(cpu, 1); + } + /* + * Physical CPU device already exists. + * So, just add interfaces for this core. + */ + coretemp_add_core(cpu, 0); +} + +static void put_core_offline(unsigned int cpu) +{ + int i, indx; + struct platform_data *pdata; + struct platform_device *pdev = coretemp_get_pdev(cpu); + + /* If the physical CPU device does not exist, just return */ + if (!pdev) + return; + + pdata = platform_get_drvdata(pdev); + + indx = TO_ATTR_NO(cpu); + + /* The core id is too big, just return */ + if (indx > MAX_CORE_DATA - 1) + return; + + if (pdata->core_data[indx] && pdata->core_data[indx]->cpu == cpu) + coretemp_remove_core(pdata, indx); + + /* + * If a HT sibling of a core is taken offline, but another HT sibling + * of the same core is still online, register the alternate sibling. + * This ensures that exactly one set of attributes is provided as long + * as at least one HT sibling of a core is online. + */ + for_each_sibling(i, cpu) { + if (i != cpu) { + get_core_online(i); + /* + * Display temperature sensor data for one HT sibling + * per core only, so abort the loop after one such + * sibling has been found. + */ + break; + } + } + /* + * If all cores in this pkg are offline, remove the device. + * coretemp_device_remove calls unregister_platform_device, + * which in turn calls coretemp_remove. This removes the + * pkgtemp entry and does other clean ups. + */ + if (!is_any_core_online(pdata)) + coretemp_device_remove(cpu); +} + +static int coretemp_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { unsigned int cpu = (unsigned long) hcpu; @@ -422,10 +775,10 @@ static int __cpuinit coretemp_cpu_callback(struct notifier_block *nfb, switch (action) { case CPU_ONLINE: case CPU_DOWN_FAILED: - coretemp_device_add(cpu); + get_core_online(cpu); break; case CPU_DOWN_PREPARE: - coretemp_device_remove(cpu); + put_core_offline(cpu); break; } return NOTIFY_OK; @@ -434,66 +787,49 @@ static int __cpuinit coretemp_cpu_callback(struct notifier_block *nfb, static struct notifier_block coretemp_cpu_notifier __refdata = { .notifier_call = coretemp_cpu_callback, }; -#endif /* !CONFIG_HOTPLUG_CPU */ + +static const struct x86_cpu_id __initconst coretemp_ids[] = { + { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_DTHERM }, + {} +}; +MODULE_DEVICE_TABLE(x86cpu, coretemp_ids); static int __init coretemp_init(void) { - int i, err = -ENODEV; - struct pdev_entry *p, *n; + int i, err; - /* quick check if we run Intel */ - if (cpu_data(0).x86_vendor != X86_VENDOR_INTEL) - goto exit; + /* + * CPUID.06H.EAX[0] indicates whether the CPU has thermal + * sensors. We check this bit only, all the early CPUs + * without thermal sensors will be filtered out. + */ + if (!x86_match_cpu(coretemp_ids)) + return -ENODEV; err = platform_driver_register(&coretemp_driver); if (err) goto exit; - for_each_online_cpu(i) { - struct cpuinfo_x86 *c = &cpu_data(i); - - /* check if family 6, models 0xe (Pentium M DC), - 0xf (Core 2 DC 65nm), 0x16 (Core 2 SC 65nm), - 0x17 (Penryn 45nm), 0x1a (Nehalem), 0x1c (Atom), - 0x1e (Lynnfield) */ - if ((c->cpuid_level < 0) || (c->x86 != 0x6) || - !((c->x86_model == 0xe) || (c->x86_model == 0xf) || - (c->x86_model == 0x16) || (c->x86_model == 0x17) || - (c->x86_model == 0x1a) || (c->x86_model == 0x1c) || - (c->x86_model == 0x1e))) { - - /* supported CPU not found, but report the unknown - family 6 CPU */ - if ((c->x86 == 0x6) && (c->x86_model > 0xf)) - printk(KERN_WARNING DRVNAME ": Unknown CPU " - "model %x\n", c->x86_model); - continue; - } + cpu_notifier_register_begin(); + for_each_online_cpu(i) + get_core_online(i); - err = coretemp_device_add(i); - if (err) - goto exit_devices_unreg; - } +#ifndef CONFIG_HOTPLUG_CPU if (list_empty(&pdev_list)) { + cpu_notifier_register_done(); err = -ENODEV; goto exit_driver_unreg; } - -#ifdef CONFIG_HOTPLUG_CPU - register_hotcpu_notifier(&coretemp_cpu_notifier); #endif + + __register_hotcpu_notifier(&coretemp_cpu_notifier); + cpu_notifier_register_done(); 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(&coretemp_driver); +#endif exit: return err; } @@ -501,9 +837,9 @@ exit: static void __exit coretemp_exit(void) { struct pdev_entry *p, *n; -#ifdef CONFIG_HOTPLUG_CPU - unregister_hotcpu_notifier(&coretemp_cpu_notifier); -#endif + + cpu_notifier_register_begin(); + __unregister_hotcpu_notifier(&coretemp_cpu_notifier); mutex_lock(&pdev_list_mutex); list_for_each_entry_safe(p, n, &pdev_list, list) { platform_device_unregister(p->pdev); @@ -511,6 +847,7 @@ static void __exit coretemp_exit(void) kfree(p); } mutex_unlock(&pdev_list_mutex); + cpu_notifier_register_done(); platform_driver_unregister(&coretemp_driver); } diff --git a/drivers/hwmon/da9052-hwmon.c b/drivers/hwmon/da9052-hwmon.c new file mode 100644 index 00000000000..d14ab3c45da --- /dev/null +++ b/drivers/hwmon/da9052-hwmon.c @@ -0,0 +1,330 @@ +/* + * HWMON Driver for Dialog DA9052 + * + * Copyright(c) 2012 Dialog Semiconductor Ltd. + * + * Author: David Dajun Chen <dchen@diasemi.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. + * + */ + +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/platform_device.h> + +#include <linux/mfd/da9052/da9052.h> +#include <linux/mfd/da9052/reg.h> + +struct da9052_hwmon { + struct da9052 *da9052; + struct device *class_device; + struct mutex hwmon_lock; +}; + +static const char * const input_names[] = { + [DA9052_ADC_VDDOUT] = "VDDOUT", + [DA9052_ADC_ICH] = "CHARGING CURRENT", + [DA9052_ADC_TBAT] = "BATTERY TEMP", + [DA9052_ADC_VBAT] = "BATTERY VOLTAGE", + [DA9052_ADC_IN4] = "ADC IN4", + [DA9052_ADC_IN5] = "ADC IN5", + [DA9052_ADC_IN6] = "ADC IN6", + [DA9052_ADC_TJUNC] = "BATTERY JUNCTION TEMP", + [DA9052_ADC_VBBAT] = "BACK-UP BATTERY VOLTAGE", +}; + +/* Conversion function for VDDOUT and VBAT */ +static inline int volt_reg_to_mv(int value) +{ + return DIV_ROUND_CLOSEST(value * 2000, 1023) + 2500; +} + +/* Conversion function for ADC channels 4, 5 and 6 */ +static inline int input_reg_to_mv(int value) +{ + return DIV_ROUND_CLOSEST(value * 2500, 1023); +} + +/* Conversion function for VBBAT */ +static inline int vbbat_reg_to_mv(int value) +{ + return DIV_ROUND_CLOSEST(value * 5000, 1023); +} + +static inline int da9052_enable_vddout_channel(struct da9052 *da9052) +{ + return da9052_reg_update(da9052, DA9052_ADC_CONT_REG, + DA9052_ADCCONT_AUTOVDDEN, + DA9052_ADCCONT_AUTOVDDEN); +} + +static inline int da9052_disable_vddout_channel(struct da9052 *da9052) +{ + return da9052_reg_update(da9052, DA9052_ADC_CONT_REG, + DA9052_ADCCONT_AUTOVDDEN, 0); +} + +static ssize_t da9052_read_vddout(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct da9052_hwmon *hwmon = dev_get_drvdata(dev); + int ret, vdd; + + mutex_lock(&hwmon->hwmon_lock); + + ret = da9052_enable_vddout_channel(hwmon->da9052); + if (ret < 0) + goto hwmon_err; + + vdd = da9052_reg_read(hwmon->da9052, DA9052_VDD_RES_REG); + if (vdd < 0) { + ret = vdd; + goto hwmon_err_release; + } + + ret = da9052_disable_vddout_channel(hwmon->da9052); + if (ret < 0) + goto hwmon_err; + + mutex_unlock(&hwmon->hwmon_lock); + return sprintf(buf, "%d\n", volt_reg_to_mv(vdd)); + +hwmon_err_release: + da9052_disable_vddout_channel(hwmon->da9052); +hwmon_err: + mutex_unlock(&hwmon->hwmon_lock); + return ret; +} + +static ssize_t da9052_read_ich(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct da9052_hwmon *hwmon = dev_get_drvdata(dev); + int ret; + + ret = da9052_reg_read(hwmon->da9052, DA9052_ICHG_AV_REG); + if (ret < 0) + return ret; + + /* Equivalent to 3.9mA/bit in register ICHG_AV */ + return sprintf(buf, "%d\n", DIV_ROUND_CLOSEST(ret * 39, 10)); +} + +static ssize_t da9052_read_tbat(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct da9052_hwmon *hwmon = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", da9052_adc_read_temp(hwmon->da9052)); +} + +static ssize_t da9052_read_vbat(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct da9052_hwmon *hwmon = dev_get_drvdata(dev); + int ret; + + ret = da9052_adc_manual_read(hwmon->da9052, DA9052_ADC_VBAT); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", volt_reg_to_mv(ret)); +} + +static ssize_t da9052_read_misc_channel(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct da9052_hwmon *hwmon = dev_get_drvdata(dev); + int channel = to_sensor_dev_attr(devattr)->index; + int ret; + + ret = da9052_adc_manual_read(hwmon->da9052, channel); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", input_reg_to_mv(ret)); +} + +static ssize_t da9052_read_tjunc(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct da9052_hwmon *hwmon = dev_get_drvdata(dev); + int tjunc; + int toffset; + + tjunc = da9052_reg_read(hwmon->da9052, DA9052_TJUNC_RES_REG); + if (tjunc < 0) + return tjunc; + + toffset = da9052_reg_read(hwmon->da9052, DA9052_T_OFFSET_REG); + if (toffset < 0) + return toffset; + + /* + * Degrees celsius = 1.708 * (TJUNC_RES - T_OFFSET) - 108.8 + * T_OFFSET is a trim value used to improve accuracy of the result + */ + return sprintf(buf, "%d\n", 1708 * (tjunc - toffset) - 108800); +} + +static ssize_t da9052_read_vbbat(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct da9052_hwmon *hwmon = dev_get_drvdata(dev); + int ret; + + ret = da9052_adc_manual_read(hwmon->da9052, DA9052_ADC_VBBAT); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", vbbat_reg_to_mv(ret)); +} + +static ssize_t da9052_hwmon_show_name(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + return sprintf(buf, "da9052\n"); +} + +static ssize_t show_label(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + return sprintf(buf, "%s\n", + input_names[to_sensor_dev_attr(devattr)->index]); +} + +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, da9052_read_vddout, NULL, + DA9052_ADC_VDDOUT); +static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_label, NULL, + DA9052_ADC_VDDOUT); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, da9052_read_vbat, NULL, + DA9052_ADC_VBAT); +static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, + DA9052_ADC_VBAT); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, da9052_read_misc_channel, NULL, + DA9052_ADC_IN4); +static SENSOR_DEVICE_ATTR(in4_label, S_IRUGO, show_label, NULL, + DA9052_ADC_IN4); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, da9052_read_misc_channel, NULL, + DA9052_ADC_IN5); +static SENSOR_DEVICE_ATTR(in5_label, S_IRUGO, show_label, NULL, + DA9052_ADC_IN5); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, da9052_read_misc_channel, NULL, + DA9052_ADC_IN6); +static SENSOR_DEVICE_ATTR(in6_label, S_IRUGO, show_label, NULL, + DA9052_ADC_IN6); +static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, da9052_read_vbbat, NULL, + DA9052_ADC_VBBAT); +static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL, + DA9052_ADC_VBBAT); + +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, da9052_read_ich, NULL, + DA9052_ADC_ICH); +static SENSOR_DEVICE_ATTR(curr1_label, S_IRUGO, show_label, NULL, + DA9052_ADC_ICH); + +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, da9052_read_tbat, NULL, + DA9052_ADC_TBAT); +static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL, + DA9052_ADC_TBAT); +static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, da9052_read_tjunc, NULL, + DA9052_ADC_TJUNC); +static SENSOR_DEVICE_ATTR(temp8_label, S_IRUGO, show_label, NULL, + DA9052_ADC_TJUNC); + +static DEVICE_ATTR(name, S_IRUGO, da9052_hwmon_show_name, NULL); + +static struct attribute *da9052_attr[] = { + &dev_attr_name.attr, + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_label.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_label.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_label.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in5_label.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in6_label.dev_attr.attr, + &sensor_dev_attr_in9_input.dev_attr.attr, + &sensor_dev_attr_in9_label.dev_attr.attr, + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr1_label.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_label.dev_attr.attr, + &sensor_dev_attr_temp8_input.dev_attr.attr, + &sensor_dev_attr_temp8_label.dev_attr.attr, + NULL +}; + +static const struct attribute_group da9052_attr_group = {.attrs = da9052_attr}; + +static int da9052_hwmon_probe(struct platform_device *pdev) +{ + struct da9052_hwmon *hwmon; + int ret; + + hwmon = devm_kzalloc(&pdev->dev, sizeof(struct da9052_hwmon), + GFP_KERNEL); + if (!hwmon) + return -ENOMEM; + + mutex_init(&hwmon->hwmon_lock); + hwmon->da9052 = dev_get_drvdata(pdev->dev.parent); + + platform_set_drvdata(pdev, hwmon); + + ret = sysfs_create_group(&pdev->dev.kobj, &da9052_attr_group); + if (ret) + goto err_mem; + + hwmon->class_device = hwmon_device_register(&pdev->dev); + if (IS_ERR(hwmon->class_device)) { + ret = PTR_ERR(hwmon->class_device); + goto err_sysfs; + } + + return 0; + +err_sysfs: + sysfs_remove_group(&pdev->dev.kobj, &da9052_attr_group); +err_mem: + return ret; +} + +static int da9052_hwmon_remove(struct platform_device *pdev) +{ + struct da9052_hwmon *hwmon = platform_get_drvdata(pdev); + + hwmon_device_unregister(hwmon->class_device); + sysfs_remove_group(&pdev->dev.kobj, &da9052_attr_group); + + return 0; +} + +static struct platform_driver da9052_hwmon_driver = { + .probe = da9052_hwmon_probe, + .remove = da9052_hwmon_remove, + .driver = { + .name = "da9052-hwmon", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(da9052_hwmon_driver); + +MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); +MODULE_DESCRIPTION("DA9052 HWMON driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9052-hwmon"); diff --git a/drivers/hwmon/da9055-hwmon.c b/drivers/hwmon/da9055-hwmon.c new file mode 100644 index 00000000000..35eb7738d71 --- /dev/null +++ b/drivers/hwmon/da9055-hwmon.c @@ -0,0 +1,332 @@ +/* + * HWMON Driver for Dialog DA9055 + * + * Copyright(c) 2012 Dialog Semiconductor Ltd. + * + * Author: David Dajun Chen <dchen@diasemi.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. + * + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/completion.h> + +#include <linux/mfd/da9055/core.h> +#include <linux/mfd/da9055/reg.h> + +#define DA9055_ADCIN_DIV 102 +#define DA9055_VSYS_DIV 85 + +#define DA9055_ADC_VSYS 0 +#define DA9055_ADC_ADCIN1 1 +#define DA9055_ADC_ADCIN2 2 +#define DA9055_ADC_ADCIN3 3 +#define DA9055_ADC_TJUNC 4 + +struct da9055_hwmon { + struct da9055 *da9055; + struct device *class_device; + struct mutex hwmon_lock; + struct mutex irq_lock; + struct completion done; +}; + +static const char * const input_names[] = { + [DA9055_ADC_VSYS] = "VSYS", + [DA9055_ADC_ADCIN1] = "ADC IN1", + [DA9055_ADC_ADCIN2] = "ADC IN2", + [DA9055_ADC_ADCIN3] = "ADC IN3", + [DA9055_ADC_TJUNC] = "CHIP TEMP", +}; + +static const u8 chan_mux[DA9055_ADC_TJUNC + 1] = { + [DA9055_ADC_VSYS] = DA9055_ADC_MUX_VSYS, + [DA9055_ADC_ADCIN1] = DA9055_ADC_MUX_ADCIN1, + [DA9055_ADC_ADCIN2] = DA9055_ADC_MUX_ADCIN2, + [DA9055_ADC_ADCIN3] = DA9055_ADC_MUX_ADCIN3, + [DA9055_ADC_TJUNC] = DA9055_ADC_MUX_T_SENSE, +}; + +static int da9055_adc_manual_read(struct da9055_hwmon *hwmon, + unsigned char channel) +{ + int ret; + unsigned short calc_data; + unsigned short data; + unsigned char mux_sel; + struct da9055 *da9055 = hwmon->da9055; + + if (channel > DA9055_ADC_TJUNC) + return -EINVAL; + + mutex_lock(&hwmon->irq_lock); + + /* Selects desired MUX for manual conversion */ + mux_sel = chan_mux[channel] | DA9055_ADC_MAN_CONV; + + ret = da9055_reg_write(da9055, DA9055_REG_ADC_MAN, mux_sel); + if (ret < 0) + goto err; + + /* Wait for an interrupt */ + if (!wait_for_completion_timeout(&hwmon->done, + msecs_to_jiffies(500))) { + dev_err(da9055->dev, + "timeout waiting for ADC conversion interrupt\n"); + ret = -ETIMEDOUT; + goto err; + } + + ret = da9055_reg_read(da9055, DA9055_REG_ADC_RES_H); + if (ret < 0) + goto err; + + calc_data = (unsigned short)ret; + data = calc_data << 2; + + ret = da9055_reg_read(da9055, DA9055_REG_ADC_RES_L); + if (ret < 0) + goto err; + + calc_data = (unsigned short)(ret & DA9055_ADC_LSB_MASK); + data |= calc_data; + + ret = data; + +err: + mutex_unlock(&hwmon->irq_lock); + return ret; +} + +static irqreturn_t da9055_auxadc_irq(int irq, void *irq_data) +{ + struct da9055_hwmon *hwmon = irq_data; + + complete(&hwmon->done); + + return IRQ_HANDLED; +} + +/* Conversion function for VSYS and ADCINx */ +static inline int volt_reg_to_mv(int value, int channel) +{ + if (channel == DA9055_ADC_VSYS) + return DIV_ROUND_CLOSEST(value * 1000, DA9055_VSYS_DIV) + 2500; + else + return DIV_ROUND_CLOSEST(value * 1000, DA9055_ADCIN_DIV); +} + +static int da9055_enable_auto_mode(struct da9055 *da9055, int channel) +{ + + return da9055_reg_update(da9055, DA9055_REG_ADC_CONT, 1 << channel, + 1 << channel); + +} + +static int da9055_disable_auto_mode(struct da9055 *da9055, int channel) +{ + + return da9055_reg_update(da9055, DA9055_REG_ADC_CONT, 1 << channel, 0); +} + +static ssize_t da9055_read_auto_ch(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct da9055_hwmon *hwmon = dev_get_drvdata(dev); + int ret, adc; + int channel = to_sensor_dev_attr(devattr)->index; + + mutex_lock(&hwmon->hwmon_lock); + + ret = da9055_enable_auto_mode(hwmon->da9055, channel); + if (ret < 0) + goto hwmon_err; + + usleep_range(10000, 10500); + + adc = da9055_reg_read(hwmon->da9055, DA9055_REG_VSYS_RES + channel); + if (adc < 0) { + ret = adc; + goto hwmon_err_release; + } + + ret = da9055_disable_auto_mode(hwmon->da9055, channel); + if (ret < 0) + goto hwmon_err; + + mutex_unlock(&hwmon->hwmon_lock); + + return sprintf(buf, "%d\n", volt_reg_to_mv(adc, channel)); + +hwmon_err_release: + da9055_disable_auto_mode(hwmon->da9055, channel); +hwmon_err: + mutex_unlock(&hwmon->hwmon_lock); + return ret; +} + +static ssize_t da9055_read_tjunc(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct da9055_hwmon *hwmon = dev_get_drvdata(dev); + int tjunc; + int toffset; + + tjunc = da9055_adc_manual_read(hwmon, DA9055_ADC_TJUNC); + if (tjunc < 0) + return tjunc; + + toffset = da9055_reg_read(hwmon->da9055, DA9055_REG_T_OFFSET); + if (toffset < 0) + return toffset; + + /* + * Degrees celsius = -0.4084 * (ADC_RES - T_OFFSET) + 307.6332 + * T_OFFSET is a trim value used to improve accuracy of the result + */ + return sprintf(buf, "%d\n", DIV_ROUND_CLOSEST(-4084 * (tjunc - toffset) + + 3076332, 10000)); +} + +static ssize_t da9055_hwmon_show_name(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + return sprintf(buf, "da9055\n"); +} + +static ssize_t show_label(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + return sprintf(buf, "%s\n", + input_names[to_sensor_dev_attr(devattr)->index]); +} + +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, da9055_read_auto_ch, NULL, + DA9055_ADC_VSYS); +static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_label, NULL, + DA9055_ADC_VSYS); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, da9055_read_auto_ch, NULL, + DA9055_ADC_ADCIN1); +static SENSOR_DEVICE_ATTR(in1_label, S_IRUGO, show_label, NULL, + DA9055_ADC_ADCIN1); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, da9055_read_auto_ch, NULL, + DA9055_ADC_ADCIN2); +static SENSOR_DEVICE_ATTR(in2_label, S_IRUGO, show_label, NULL, + DA9055_ADC_ADCIN2); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, da9055_read_auto_ch, NULL, + DA9055_ADC_ADCIN3); +static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, + DA9055_ADC_ADCIN3); + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, da9055_read_tjunc, NULL, + DA9055_ADC_TJUNC); +static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, + DA9055_ADC_TJUNC); + +static DEVICE_ATTR(name, S_IRUGO, da9055_hwmon_show_name, NULL); + +static struct attribute *da9055_attr[] = { + &dev_attr_name.attr, + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_label.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_label.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_label.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_label.dev_attr.attr, + + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_label.dev_attr.attr, + NULL +}; + +static const struct attribute_group da9055_attr_group = {.attrs = da9055_attr}; + +static int da9055_hwmon_probe(struct platform_device *pdev) +{ + struct da9055_hwmon *hwmon; + int hwmon_irq, ret; + + hwmon = devm_kzalloc(&pdev->dev, sizeof(struct da9055_hwmon), + GFP_KERNEL); + if (!hwmon) + return -ENOMEM; + + mutex_init(&hwmon->hwmon_lock); + mutex_init(&hwmon->irq_lock); + + init_completion(&hwmon->done); + hwmon->da9055 = dev_get_drvdata(pdev->dev.parent); + + platform_set_drvdata(pdev, hwmon); + + hwmon_irq = platform_get_irq_byname(pdev, "HWMON"); + if (hwmon_irq < 0) + return hwmon_irq; + + ret = devm_request_threaded_irq(&pdev->dev, hwmon_irq, + NULL, da9055_auxadc_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "adc-irq", hwmon); + if (ret != 0) { + dev_err(hwmon->da9055->dev, "DA9055 ADC IRQ failed ret=%d\n", + ret); + return ret; + } + + ret = sysfs_create_group(&pdev->dev.kobj, &da9055_attr_group); + if (ret) + return ret; + + hwmon->class_device = hwmon_device_register(&pdev->dev); + if (IS_ERR(hwmon->class_device)) { + ret = PTR_ERR(hwmon->class_device); + goto err; + } + + return 0; + +err: + sysfs_remove_group(&pdev->dev.kobj, &da9055_attr_group); + return ret; +} + +static int da9055_hwmon_remove(struct platform_device *pdev) +{ + struct da9055_hwmon *hwmon = platform_get_drvdata(pdev); + + sysfs_remove_group(&pdev->dev.kobj, &da9055_attr_group); + hwmon_device_unregister(hwmon->class_device); + + return 0; +} + +static struct platform_driver da9055_hwmon_driver = { + .probe = da9055_hwmon_probe, + .remove = da9055_hwmon_remove, + .driver = { + .name = "da9055-hwmon", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(da9055_hwmon_driver); + +MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); +MODULE_DESCRIPTION("DA9055 HWMON driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9055-hwmon"); diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c index 823dd28a902..4ae3fff13f4 100644 --- a/drivers/hwmon/dme1737.c +++ b/drivers/hwmon/dme1737.c @@ -1,12 +1,14 @@ /* - * dme1737.c - Driver for the SMSC DME1737, Asus A8000, SMSC SCH311x and - * SCH5027 Super-I/O chips integrated hardware monitoring features. - * Copyright (c) 2007, 2008 Juerg Haefliger <juergh@gmail.com> + * dme1737.c - Driver for the SMSC DME1737, Asus A8000, SMSC SCH311x, SCH5027, + * and SCH5127 Super-I/O chips integrated hardware monitoring + * features. + * Copyright (c) 2007, 2008, 2009, 2010 Juerg Haefliger <juergh@gmail.com> * * This driver is an I2C/ISA hybrid, meaning that it uses the I2C bus to access * the chip registers if a DME1737, A8000, or SCH5027 is found and the ISA bus - * if a SCH311x chip is found. Both types of chips have very similar hardware - * monitoring capabilities but differ in the way they can be accessed. + * if a SCH311x or SCH5127 chip is found. Both types of chips have very + * similar hardware monitoring capabilities but differ in the way they can be + * accessed. * * 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 @@ -23,6 +25,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> @@ -41,7 +45,7 @@ static struct platform_device *pdev; /* Module load parameters */ -static int force_start; +static bool force_start; module_param(force_start, bool, 0); MODULE_PARM_DESC(force_start, "Force the chip to start monitoring inputs"); @@ -49,15 +53,17 @@ static unsigned short force_id; module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); -static int probe_all_addr; +static bool probe_all_addr; module_param(probe_all_addr, bool, 0); -MODULE_PARM_DESC(probe_all_addr, "Include probing of non-standard LPC " - "addresses"); +MODULE_PARM_DESC(probe_all_addr, + "Include probing of non-standard LPC addresses"); /* Addresses to scan */ static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END}; -enum chips { dme1737, sch5027, sch311x }; +enum chips { dme1737, sch5027, sch311x, sch5127 }; + +#define DO_REPORT "Please report to the driver maintainer." /* --------------------------------------------------------------------- * Registers @@ -73,15 +79,17 @@ enum chips { dme1737, sch5027, sch311x }; * in4 +12V * in5 VTR (+3.3V stby) * in6 Vbat + * in7 Vtrip (sch5127 only) * * --------------------------------------------------------------------- */ -/* Voltages (in) numbered 0-6 (ix) */ -#define DME1737_REG_IN(ix) ((ix) < 5 ? 0x20 + (ix) \ - : 0x94 + (ix)) -#define DME1737_REG_IN_MIN(ix) ((ix) < 5 ? 0x44 + (ix) * 2 \ +/* Voltages (in) numbered 0-7 (ix) */ +#define DME1737_REG_IN(ix) ((ix) < 5 ? 0x20 + (ix) : \ + (ix) < 7 ? 0x94 + (ix) : \ + 0x1f) +#define DME1737_REG_IN_MIN(ix) ((ix) < 5 ? 0x44 + (ix) * 2 \ : 0x91 + (ix) * 2) -#define DME1737_REG_IN_MAX(ix) ((ix) < 5 ? 0x45 + (ix) * 2 \ +#define DME1737_REG_IN_MAX(ix) ((ix) < 5 ? 0x45 + (ix) * 2 \ : 0x92 + (ix) * 2) /* Temperatures (temp) numbered 0-2 (ix) */ @@ -91,16 +99,19 @@ enum chips { dme1737, sch5027, sch311x }; #define DME1737_REG_TEMP_OFFSET(ix) ((ix) == 0 ? 0x1f \ : 0x1c + (ix)) -/* Voltage and temperature LSBs +/* + * Voltage and temperature LSBs * The LSBs (4 bits each) are stored in 5 registers with the following layouts: * IN_TEMP_LSB(0) = [in5, in6] * IN_TEMP_LSB(1) = [temp3, temp1] * IN_TEMP_LSB(2) = [in4, temp2] * IN_TEMP_LSB(3) = [in3, in0] - * IN_TEMP_LSB(4) = [in2, in1] */ + * IN_TEMP_LSB(4) = [in2, in1] + * IN_TEMP_LSB(5) = [res, in7] + */ #define DME1737_REG_IN_TEMP_LSB(ix) (0x84 + (ix)) -static const u8 DME1737_REG_IN_LSB[] = {3, 4, 4, 3, 2, 0, 0}; -static const u8 DME1737_REG_IN_LSB_SHL[] = {4, 4, 0, 0, 0, 0, 4}; +static const u8 DME1737_REG_IN_LSB[] = {3, 4, 4, 3, 2, 0, 0, 5}; +static const u8 DME1737_REG_IN_LSB_SHL[] = {4, 4, 0, 0, 0, 0, 4, 4}; static const u8 DME1737_REG_TEMP_LSB[] = {1, 2, 1}; static const u8 DME1737_REG_TEMP_LSB_SHL[] = {4, 4, 0}; @@ -120,28 +131,34 @@ static const u8 DME1737_REG_TEMP_LSB_SHL[] = {4, 4, 0}; #define DME1737_REG_PWM_MIN(ix) (0x64 + (ix)) /* only for pwm[0-2] */ #define DME1737_REG_PWM_FREQ(ix) ((ix) < 3 ? 0x5f + (ix) \ : 0xa3 + (ix)) -/* The layout of the ramp rate registers is different from the other pwm +/* + * The layout of the ramp rate registers is different from the other pwm * registers. The bits for the 3 PWMs are stored in 2 registers: * PWM_RR(0) = [OFF3, OFF2, OFF1, RES, RR1E, RR1-2, RR1-1, RR1-0] - * PWM_RR(1) = [RR2E, RR2-2, RR2-1, RR2-0, RR3E, RR3-2, RR3-1, RR3-0] */ + * PWM_RR(1) = [RR2E, RR2-2, RR2-1, RR2-0, RR3E, RR3-2, RR3-1, RR3-0] + */ #define DME1737_REG_PWM_RR(ix) (0x62 + (ix)) /* only for pwm[0-2] */ /* Thermal zones 0-2 */ #define DME1737_REG_ZONE_LOW(ix) (0x67 + (ix)) #define DME1737_REG_ZONE_ABS(ix) (0x6a + (ix)) -/* The layout of the hysteresis registers is different from the other zone +/* + * The layout of the hysteresis registers is different from the other zone * registers. The bits for the 3 zones are stored in 2 registers: * ZONE_HYST(0) = [H1-3, H1-2, H1-1, H1-0, H2-3, H2-2, H2-1, H2-0] - * ZONE_HYST(1) = [H3-3, H3-2, H3-1, H3-0, RES, RES, RES, RES] */ + * ZONE_HYST(1) = [H3-3, H3-2, H3-1, H3-0, RES, RES, RES, RES] + */ #define DME1737_REG_ZONE_HYST(ix) (0x6d + (ix)) -/* Alarm registers and bit mapping +/* + * Alarm registers and bit mapping * The 3 8-bit alarm registers will be concatenated to a single 32-bit - * alarm value [0, ALARM3, ALARM2, ALARM1]. */ + * alarm value [0, ALARM3, ALARM2, ALARM1]. + */ #define DME1737_REG_ALARM1 0x41 #define DME1737_REG_ALARM2 0x42 #define DME1737_REG_ALARM3 0x83 -static const u8 DME1737_BIT_ALARM_IN[] = {0, 1, 2, 3, 8, 16, 17}; +static const u8 DME1737_BIT_ALARM_IN[] = {0, 1, 2, 3, 8, 16, 17, 18}; static const u8 DME1737_BIT_ALARM_TEMP[] = {4, 5, 6}; static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23}; @@ -164,10 +181,30 @@ static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23}; #define DME1737_VERSTEP_MASK 0xf8 #define SCH311X_DEVICE 0x8c #define SCH5027_VERSTEP 0x69 +#define SCH5127_DEVICE 0x8e + +/* Device ID values (global configuration register index 0x20) */ +#define DME1737_ID_1 0x77 +#define DME1737_ID_2 0x78 +#define SCH3112_ID 0x7c +#define SCH3114_ID 0x7d +#define SCH3116_ID 0x7f +#define SCH5027_ID 0x89 +#define SCH5127_ID 0x86 /* Length of ISA address segment */ #define DME1737_EXTENT 2 +/* chip-dependent features */ +#define HAS_TEMP_OFFSET (1 << 0) /* bit 0 */ +#define HAS_VID (1 << 1) /* bit 1 */ +#define HAS_ZONE3 (1 << 2) /* bit 2 */ +#define HAS_ZONE_HYST (1 << 3) /* bit 3 */ +#define HAS_PWM_MIN (1 << 4) /* bit 4 */ +#define HAS_FAN(ix) (1 << ((ix) + 5)) /* bits 5-10 */ +#define HAS_PWM(ix) (1 << ((ix) + 11)) /* bits 11-16 */ +#define HAS_IN7 (1 << 17) /* bit 17 */ + /* --------------------------------------------------------------------- * Data structures and manipulation thereof * --------------------------------------------------------------------- */ @@ -187,13 +224,12 @@ struct dme1737_data { u8 vid; u8 pwm_rr_en; - u8 has_pwm; - u8 has_fan; + u32 has_features; /* Register values */ - u16 in[7]; - u8 in_min[7]; - u8 in_max[7]; + u16 in[8]; + u8 in_min[8]; + u8 in_max[8]; s16 temp[3]; s8 temp_min[3]; s8 temp_max[3]; @@ -224,13 +260,18 @@ static const int IN_NOMINAL_SCH311x[] = {2500, 1500, 3300, 5000, 12000, 3300, 3300}; static const int IN_NOMINAL_SCH5027[] = {5000, 2250, 3300, 1125, 1125, 3300, 3300}; +static const int IN_NOMINAL_SCH5127[] = {2500, 2250, 3300, 1125, 1125, 3300, + 3300, 1500}; #define IN_NOMINAL(type) ((type) == sch311x ? IN_NOMINAL_SCH311x : \ (type) == sch5027 ? IN_NOMINAL_SCH5027 : \ + (type) == sch5127 ? IN_NOMINAL_SCH5127 : \ IN_NOMINAL_DME1737) -/* Voltage input +/* + * Voltage input * Voltage inputs have 16 bits resolution, limit values have 8 bits - * resolution. */ + * resolution. + */ static inline int IN_FROM_REG(int reg, int nominal, int res) { return (reg * nominal + (3 << (res - 3))) / (3 << (res - 2)); @@ -238,13 +279,15 @@ static inline int IN_FROM_REG(int reg, int nominal, int res) static inline int IN_TO_REG(int val, int nominal) { - return SENSORS_LIMIT((val * 192 + nominal / 2) / nominal, 0, 255); + return clamp_val((val * 192 + nominal / 2) / nominal, 0, 255); } -/* Temperature input +/* + * Temperature input * The register values represent temperatures in 2's complement notation from * -127 degrees C to +127 degrees C. Temp inputs have 16 bits resolution, limit - * values have 8 bits resolution. */ + * values have 8 bits resolution. + */ static inline int TEMP_FROM_REG(int reg, int res) { return (reg * 1000) >> (res - 8); @@ -252,8 +295,7 @@ static inline int TEMP_FROM_REG(int reg, int res) static inline int TEMP_TO_REG(int val) { - return SENSORS_LIMIT((val < 0 ? val - 500 : val + 500) / 1000, - -128, 127); + return clamp_val((val < 0 ? val - 500 : val + 500) / 1000, -128, 127); } /* Temperature range */ @@ -271,18 +313,19 @@ static int TEMP_RANGE_TO_REG(int val, int reg) int i; for (i = 15; i > 0; i--) { - if (val > (TEMP_RANGE[i] + TEMP_RANGE[i - 1] + 1) / 2) { + if (val > (TEMP_RANGE[i] + TEMP_RANGE[i - 1] + 1) / 2) break; - } } return (reg & 0x0f) | (i << 4); } -/* Temperature hysteresis +/* + * Temperature hysteresis * Register layout: * reg[0] = [H1-3, H1-2, H1-1, H1-0, H2-3, H2-2, H2-1, H2-0] - * reg[1] = [H3-3, H3-2, H3-1, H3-0, xxxx, xxxx, xxxx, xxxx] */ + * reg[1] = [H3-3, H3-2, H3-1, H3-0, xxxx, xxxx, xxxx, xxxx] + */ static inline int TEMP_HYST_FROM_REG(int reg, int ix) { return (((ix == 1) ? reg : reg >> 4) & 0x0f) * 1000; @@ -290,7 +333,7 @@ static inline int TEMP_HYST_FROM_REG(int reg, int ix) static inline int TEMP_HYST_TO_REG(int val, int ix, int reg) { - int hyst = SENSORS_LIMIT((val + 500) / 1000, 0, 15); + int hyst = clamp_val((val + 500) / 1000, 0, 15); return (ix == 1) ? (reg & 0xf0) | hyst : (reg & 0x0f) | (hyst << 4); } @@ -298,34 +341,37 @@ static inline int TEMP_HYST_TO_REG(int val, int ix, int reg) /* Fan input RPM */ static inline int FAN_FROM_REG(int reg, int tpc) { - if (tpc) { + if (tpc) return tpc * reg; - } else { + else return (reg == 0 || reg == 0xffff) ? 0 : 90000 * 60 / reg; - } } static inline int FAN_TO_REG(int val, int tpc) { if (tpc) { - return SENSORS_LIMIT(val / tpc, 0, 0xffff); + return clamp_val(val / tpc, 0, 0xffff); } else { return (val <= 0) ? 0xffff : - SENSORS_LIMIT(90000 * 60 / val, 0, 0xfffe); + clamp_val(90000 * 60 / val, 0, 0xfffe); } } -/* Fan TPC (tach pulse count) +/* + * Fan TPC (tach pulse count) * Converts a register value to a TPC multiplier or returns 0 if the tachometer - * is configured in legacy (non-tpc) mode */ + * is configured in legacy (non-tpc) mode + */ static inline int FAN_TPC_FROM_REG(int reg) { return (reg & 0x20) ? 0 : 60 >> (reg & 0x03); } -/* Fan type +/* + * Fan type * The type of a fan is expressed in number of pulses-per-revolution that it - * emits */ + * emits + */ static inline int FAN_TYPE_FROM_REG(int reg) { int edge = (reg >> 1) & 0x03; @@ -349,9 +395,8 @@ static int FAN_MAX_FROM_REG(int reg) int i; for (i = 10; i > 0; i--) { - if (reg == FAN_MAX[i]) { + if (reg == FAN_MAX[i]) break; - } } return 1000 + i * 500; @@ -362,15 +407,15 @@ static int FAN_MAX_TO_REG(int val) int i; for (i = 10; i > 0; i--) { - if (val > (1000 + (i - 1) * 500)) { + if (val > (1000 + (i - 1) * 500)) break; - } } return FAN_MAX[i]; } -/* PWM enable +/* + * PWM enable * Register to enable mapping: * 000: 2 fan on zone 1 auto * 001: 2 fan on zone 2 auto @@ -379,7 +424,8 @@ static int FAN_MAX_TO_REG(int val) * 100: -1 fan disabled * 101: 2 fan on hottest of zones 2,3 auto * 110: 2 fan on hottest of zones 1,2,3 auto - * 111: 1 fan in manual mode */ + * 111: 1 fan in manual mode + */ static inline int PWM_EN_FROM_REG(int reg) { static const int en[] = {2, 2, 2, 0, -1, 2, 2, 1}; @@ -394,7 +440,8 @@ static inline int PWM_EN_TO_REG(int val, int reg) return (reg & 0x1f) | ((en & 0x07) << 5); } -/* PWM auto channels zone +/* + * PWM auto channels zone * Register to auto channels zone mapping (ACZ is a bitfield with bit x * corresponding to zone x+1): * 000: 001 fan on zone 1 auto @@ -404,7 +451,8 @@ static inline int PWM_EN_TO_REG(int val, int reg) * 100: 000 fan disabled * 101: 110 fan on hottest of zones 2,3 auto * 110: 111 fan on hottest of zones 1,2,3 auto - * 111: 000 fan in manual mode */ + * 111: 000 fan in manual mode + */ static inline int PWM_ACZ_FROM_REG(int reg) { static const int acz[] = {1, 2, 4, 0, 0, 6, 7, 0}; @@ -439,19 +487,20 @@ static int PWM_FREQ_TO_REG(int val, int reg) i = 11; } else { for (i = 9; i > 0; i--) { - if (val > (PWM_FREQ[i] + PWM_FREQ[i - 1] + 1) / 2) { + if (val > (PWM_FREQ[i] + PWM_FREQ[i - 1] + 1) / 2) break; - } } } return (reg & 0xf0) | i; } -/* PWM ramp rate +/* + * PWM ramp rate * Register layout: * reg[0] = [OFF3, OFF2, OFF1, RES, RR1-E, RR1-2, RR1-1, RR1-0] - * reg[1] = [RR2-E, RR2-2, RR2-1, RR2-0, RR3-E, RR3-2, RR3-1, RR3-0] */ + * reg[1] = [RR2-E, RR2-2, RR2-1, RR2-0, RR3-E, RR3-2, RR3-1, RR3-0] + */ static const u8 PWM_RR[] = {206, 104, 69, 41, 26, 18, 10, 5}; static inline int PWM_RR_FROM_REG(int reg, int ix) @@ -466,9 +515,8 @@ static int PWM_RR_TO_REG(int val, int ix, int reg) int i; for (i = 0; i < 7; i++) { - if (val > (PWM_RR[i] + PWM_RR[i + 1] + 1) / 2) { + if (val > (PWM_RR[i] + PWM_RR[i + 1] + 1) / 2) break; - } } return (ix == 1) ? (reg & 0x8f) | (i << 4) : (reg & 0xf8) | i; @@ -487,9 +535,11 @@ static inline int PWM_RR_EN_TO_REG(int val, int ix, int reg) return val ? reg | en : reg & ~en; } -/* PWM min/off +/* + * PWM min/off * The PWM min/off bits are part of the PMW ramp rate register 0 (see above for - * the register layout). */ + * the register layout). + */ static inline int PWM_OFF_FROM_REG(int reg, int ix) { return (reg >> (ix + 5)) & 0x01; @@ -518,9 +568,9 @@ static u8 dme1737_read(const struct dme1737_data *data, u8 reg) val = i2c_smbus_read_byte_data(client, reg); if (val < 0) { - dev_warn(&client->dev, "Read from register " - "0x%02x failed! Please report to the driver " - "maintainer.\n", reg); + dev_warn(&client->dev, + "Read from register 0x%02x failed! %s\n", + reg, DO_REPORT); } } else { /* ISA device */ outb(reg, data->addr); @@ -539,9 +589,9 @@ static s32 dme1737_write(const struct dme1737_data *data, u8 reg, u8 val) res = i2c_smbus_write_byte_data(client, reg, val); if (res < 0) { - dev_warn(&client->dev, "Write to register " - "0x%02x failed! Please report to the driver " - "maintainer.\n", reg); + dev_warn(&client->dev, + "Write to register 0x%02x failed! %s\n", + reg, DO_REPORT); } } else { /* ISA device */ outb(reg, data->addr); @@ -555,7 +605,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) { struct dme1737_data *data = dev_get_drvdata(dev); int ix; - u8 lsb[5]; + u8 lsb[6]; mutex_lock(&data->update_lock); @@ -568,16 +618,20 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) /* Sample register contents every 1 sec */ if (time_after(jiffies, data->last_update + HZ) || !data->valid) { - if (data->type == dme1737) { + if (data->has_features & HAS_VID) { data->vid = dme1737_read(data, DME1737_REG_VID) & 0x3f; } /* In (voltage) registers */ for (ix = 0; ix < ARRAY_SIZE(data->in); ix++) { - /* Voltage inputs are stored as 16 bit values even + /* + * Voltage inputs are stored as 16 bit values even * though they have only 12 bits resolution. This is - * to make it consistent with the temp inputs. */ + * to make it consistent with the temp inputs. + */ + if (ix == 7 && !(data->has_features & HAS_IN7)) + continue; data->in[ix] = dme1737_read(data, DME1737_REG_IN(ix)) << 8; data->in_min[ix] = dme1737_read(data, @@ -588,32 +642,40 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) /* Temp registers */ for (ix = 0; ix < ARRAY_SIZE(data->temp); ix++) { - /* Temp inputs are stored as 16 bit values even + /* + * Temp inputs are stored as 16 bit values even * though they have only 12 bits resolution. This is * to take advantage of implicit conversions between * register values (2's complement) and temp values - * (signed decimal). */ + * (signed decimal). + */ data->temp[ix] = dme1737_read(data, DME1737_REG_TEMP(ix)) << 8; data->temp_min[ix] = dme1737_read(data, DME1737_REG_TEMP_MIN(ix)); data->temp_max[ix] = dme1737_read(data, DME1737_REG_TEMP_MAX(ix)); - if (data->type != sch5027) { + if (data->has_features & HAS_TEMP_OFFSET) { data->temp_offset[ix] = dme1737_read(data, DME1737_REG_TEMP_OFFSET(ix)); } } - /* In and temp LSB registers + /* + * In and temp LSB registers * The LSBs are latched when the MSBs are read, so the order in * which the registers are read (MSB first, then LSB) is - * important! */ + * important! + */ for (ix = 0; ix < ARRAY_SIZE(lsb); ix++) { + if (ix == 5 && !(data->has_features & HAS_IN7)) + continue; lsb[ix] = dme1737_read(data, DME1737_REG_IN_TEMP_LSB(ix)); } for (ix = 0; ix < ARRAY_SIZE(data->in); ix++) { + if (ix == 7 && !(data->has_features & HAS_IN7)) + continue; data->in[ix] |= (lsb[DME1737_REG_IN_LSB[ix]] << DME1737_REG_IN_LSB_SHL[ix]) & 0xf0; } @@ -624,11 +686,12 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) /* Fan registers */ for (ix = 0; ix < ARRAY_SIZE(data->fan); ix++) { - /* Skip reading registers if optional fans are not - * present */ - if (!(data->has_fan & (1 << ix))) { + /* + * Skip reading registers if optional fans are not + * present + */ + if (!(data->has_features & HAS_FAN(ix))) continue; - } data->fan[ix] = dme1737_read(data, DME1737_REG_FAN(ix)); data->fan[ix] |= dme1737_read(data, @@ -648,11 +711,12 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) /* PWM registers */ for (ix = 0; ix < ARRAY_SIZE(data->pwm); ix++) { - /* Skip reading registers if optional PWMs are not - * present */ - if (!(data->has_pwm & (1 << ix))) { + /* + * Skip reading registers if optional PWMs are not + * present + */ + if (!(data->has_features & HAS_PWM(ix))) continue; - } data->pwm[ix] = dme1737_read(data, DME1737_REG_PWM(ix)); data->pwm_freq[ix] = dme1737_read(data, @@ -672,12 +736,23 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) /* Thermal zone registers */ for (ix = 0; ix < ARRAY_SIZE(data->zone_low); ix++) { - data->zone_low[ix] = dme1737_read(data, - DME1737_REG_ZONE_LOW(ix)); - data->zone_abs[ix] = dme1737_read(data, - DME1737_REG_ZONE_ABS(ix)); + /* Skip reading registers if zone3 is not present */ + if ((ix == 2) && !(data->has_features & HAS_ZONE3)) + continue; + /* sch5127 zone2 registers are special */ + if ((ix == 1) && (data->type == sch5127)) { + data->zone_low[1] = dme1737_read(data, + DME1737_REG_ZONE_LOW(2)); + data->zone_abs[1] = dme1737_read(data, + DME1737_REG_ZONE_ABS(2)); + } else { + data->zone_low[ix] = dme1737_read(data, + DME1737_REG_ZONE_LOW(ix)); + data->zone_abs[ix] = dme1737_read(data, + DME1737_REG_ZONE_ABS(ix)); + } } - if (data->type != sch5027) { + if (data->has_features & HAS_ZONE_HYST) { for (ix = 0; ix < ARRAY_SIZE(data->zone_hyst); ix++) { data->zone_hyst[ix] = dme1737_read(data, DME1737_REG_ZONE_HYST(ix)); @@ -687,8 +762,10 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) /* Alarm registers */ data->alarms = dme1737_read(data, DME1737_REG_ALARM1); - /* Bit 7 tells us if the other alarm registers are non-zero and - * therefore also need to be read */ + /* + * Bit 7 tells us if the other alarm registers are non-zero and + * therefore also need to be read + */ if (data->alarms & 0x80) { data->alarms |= dme1737_read(data, DME1737_REG_ALARM2) << 8; @@ -696,22 +773,18 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) DME1737_REG_ALARM3) << 16; } - /* The ISA chips require explicit clearing of alarm bits. + /* + * The ISA chips require explicit clearing of alarm bits. * Don't worry, an alarm will come back if the condition - * that causes it still exists */ + * that causes it still exists + */ if (!data->client) { - if (data->alarms & 0xff0000) { - dme1737_write(data, DME1737_REG_ALARM3, - 0xff); - } - if (data->alarms & 0xff00) { - dme1737_write(data, DME1737_REG_ALARM2, - 0xff); - } - if (data->alarms & 0xff) { - dme1737_write(data, DME1737_REG_ALARM1, - 0xff); - } + if (data->alarms & 0xff0000) + dme1737_write(data, DME1737_REG_ALARM3, 0xff); + if (data->alarms & 0xff00) + dme1737_write(data, DME1737_REG_ALARM2, 0xff); + if (data->alarms & 0xff) + dme1737_write(data, DME1737_REG_ALARM1, 0xff); } data->last_update = jiffies; @@ -725,7 +798,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) /* --------------------------------------------------------------------- * Voltage sysfs attributes - * ix = [0-5] + * ix = [0-7] * --------------------------------------------------------------------- */ #define SYS_IN_INPUT 0 @@ -772,7 +845,12 @@ static ssize_t set_in(struct device *dev, struct device_attribute *attr, *sensor_attr_2 = to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; int fn = sensor_attr_2->nr; - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); switch (fn) { @@ -851,7 +929,12 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *attr, *sensor_attr_2 = to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; int fn = sensor_attr_2->nr; - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); switch (fn) { @@ -902,11 +985,10 @@ static ssize_t show_zone(struct device *dev, struct device_attribute *attr, switch (fn) { case SYS_ZONE_AUTO_CHANNELS_TEMP: /* check config2 for non-standard temp-to-zone mapping */ - if ((ix == 1) && (data->config2 & 0x02)) { + if ((ix == 1) && (data->config2 & 0x02)) res = 4; - } else { + else res = 1 << ix; - } break; case SYS_ZONE_AUTO_POINT1_TEMP_HYST: res = TEMP_FROM_REG(data->zone_low[ix], 8) - @@ -939,7 +1021,12 @@ static ssize_t set_zone(struct device *dev, struct device_attribute *attr, *sensor_attr_2 = to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; int fn = sensor_attr_2->nr; - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); switch (fn) { @@ -964,8 +1051,10 @@ static ssize_t set_zone(struct device *dev, struct device_attribute *attr, /* Refresh the cache */ data->zone_low[ix] = dme1737_read(data, DME1737_REG_ZONE_LOW(ix)); - /* Modify the temp range value (which is stored in the upper - * nibble of the pwm_freq register) */ + /* + * Modify the temp range value (which is stored in the upper + * nibble of the pwm_freq register) + */ data->pwm_freq[ix] = TEMP_RANGE_TO_REG(val - TEMP_FROM_REG(data->zone_low[ix], 8), dme1737_read(data, @@ -1045,7 +1134,12 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr, *sensor_attr_2 = to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; int fn = sensor_attr_2->nr; - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); switch (fn) { @@ -1075,8 +1169,8 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr, /* Only valid for fan[1-4] */ if (!(val == 1 || val == 2 || val == 4)) { count = -EINVAL; - dev_warn(dev, "Fan type value %ld not " - "supported. Choose one of 1, 2, or 4.\n", + dev_warn(dev, + "Fan type value %ld not supported. Choose one of 1, 2, or 4.\n", val); goto exit; } @@ -1120,21 +1214,19 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, switch (fn) { case SYS_PWM: - if (PWM_EN_FROM_REG(data->pwm_config[ix]) == 0) { + if (PWM_EN_FROM_REG(data->pwm_config[ix]) == 0) res = 255; - } else { + else res = data->pwm[ix]; - } break; case SYS_PWM_FREQ: res = PWM_FREQ_FROM_REG(data->pwm_freq[ix]); break; case SYS_PWM_ENABLE: - if (ix >= 3) { + if (ix >= 3) res = 1; /* pwm[5-6] hard-wired to manual mode */ - } else { + else res = PWM_EN_FROM_REG(data->pwm_config[ix]); - } break; case SYS_PWM_RAMP_RATE: /* Only valid for pwm[1-3] */ @@ -1142,19 +1234,17 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, break; case SYS_PWM_AUTO_CHANNELS_ZONE: /* Only valid for pwm[1-3] */ - if (PWM_EN_FROM_REG(data->pwm_config[ix]) == 2) { + if (PWM_EN_FROM_REG(data->pwm_config[ix]) == 2) res = PWM_ACZ_FROM_REG(data->pwm_config[ix]); - } else { + else res = data->pwm_acz[ix]; - } break; case SYS_PWM_AUTO_PWM_MIN: /* Only valid for pwm[1-3] */ - if (PWM_OFF_FROM_REG(data->pwm_rr[0], ix)) { + if (PWM_OFF_FROM_REG(data->pwm_rr[0], ix)) res = data->pwm_min[ix]; - } else { + else res = 0; - } break; case SYS_PWM_AUTO_POINT1_PWM: /* Only valid for pwm[1-3] */ @@ -1173,7 +1263,7 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, } static struct attribute *dme1737_pwm_chmod_attr[]; -static void dme1737_chmod_file(struct device*, struct attribute*, mode_t); +static void dme1737_chmod_file(struct device*, struct attribute*, umode_t); static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -1183,12 +1273,17 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, *sensor_attr_2 = to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; int fn = sensor_attr_2->nr; - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); switch (fn) { case SYS_PWM: - data->pwm[ix] = SENSORS_LIMIT(val, 0, 255); + data->pwm[ix] = clamp_val(val, 0, 255); dme1737_write(data, DME1737_REG_PWM(ix), data->pwm[ix]); break; case SYS_PWM_FREQ: @@ -1201,8 +1296,8 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, /* Only valid for pwm[1-3] */ if (val < 0 || val > 2) { count = -EINVAL; - dev_warn(dev, "PWM enable %ld not " - "supported. Choose one of 0, 1, or 2.\n", + dev_warn(dev, + "PWM enable %ld not supported. Choose one of 0, 1, or 2.\n", val); goto exit; } @@ -1257,8 +1352,10 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, /* Change permissions of pwm[ix] to read-only */ dme1737_chmod_file(dev, dme1737_pwm_chmod_attr[ix], S_IRUGO); - /* Turn on auto mode using the saved zone channel - * assignment */ + /* + * Turn on auto mode using the saved zone channel + * assignment + */ data->pwm_config[ix] = PWM_ACZ_TO_REG( data->pwm_acz[ix], data->pwm_config[ix]); @@ -1288,8 +1385,10 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, data->pwm_rr[ix > 0] = PWM_RR_TO_REG(val, ix, data->pwm_rr[ix > 0]); } - /* Enable/disable the feature only if the associated PWM - * output is in automatic mode. */ + /* + * Enable/disable the feature only if the associated PWM + * output is in automatic mode. + */ if (PWM_EN_FROM_REG(data->pwm_config[ix]) == 2) { data->pwm_rr[ix > 0] = PWM_RR_EN_TO_REG(val > 0, ix, data->pwm_rr[ix > 0]); @@ -1302,8 +1401,8 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, if (!(val == 1 || val == 2 || val == 4 || val == 6 || val == 7)) { count = -EINVAL; - dev_warn(dev, "PWM auto channels zone %ld " - "not supported. Choose one of 1, 2, 4, 6, " + dev_warn(dev, + "PWM auto channels zone %ld not supported. Choose one of 1, 2, 4, 6, " "or 7.\n", val); goto exit; } @@ -1311,15 +1410,19 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, data->pwm_config[ix] = dme1737_read(data, DME1737_REG_PWM_CONFIG(ix)); if (PWM_EN_FROM_REG(data->pwm_config[ix]) == 2) { - /* PWM is already in auto mode so update the temp - * channel assignment */ + /* + * PWM is already in auto mode so update the temp + * channel assignment + */ data->pwm_config[ix] = PWM_ACZ_TO_REG(val, data->pwm_config[ix]); dme1737_write(data, DME1737_REG_PWM_CONFIG(ix), data->pwm_config[ix]); } else { - /* PWM is not in auto mode so we save the temp - * channel assignment for later use */ + /* + * PWM is not in auto mode so we save the temp + * channel assignment for later use + */ data->pwm_acz[ix] = val; } break; @@ -1328,10 +1431,12 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, /* Refresh the cache */ data->pwm_min[ix] = dme1737_read(data, DME1737_REG_PWM_MIN(ix)); - /* There are only 2 values supported for the auto_pwm_min + /* + * There are only 2 values supported for the auto_pwm_min * value: 0 or auto_point1_pwm. So if the temperature drops * below the auto_point1_temp_hyst value, the fan either turns - * off or runs at auto_point1_pwm duty-cycle. */ + * off or runs at auto_point1_pwm duty-cycle. + */ if (val > ((data->pwm_min[ix] + 1) / 2)) { data->pwm_rr[0] = PWM_OFF_TO_REG(1, ix, dme1737_read(data, @@ -1346,7 +1451,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, break; case SYS_PWM_AUTO_POINT1_PWM: /* Only valid for pwm[1-3] */ - data->pwm_min[ix] = SENSORS_LIMIT(val, 0, 255); + data->pwm_min[ix] = clamp_val(val, 0, 255); dme1737_write(data, DME1737_REG_PWM_MIN(ix), data->pwm_min[ix]); break; @@ -1376,7 +1481,12 @@ static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct dme1737_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; data->vrm = val; return count; @@ -1402,7 +1512,7 @@ static ssize_t show_name(struct device *dev, struct device_attribute *attr, * Sysfs device attribute defines and structs * --------------------------------------------------------------------- */ -/* Voltages 0-6 */ +/* Voltages 0-7 */ #define SENSOR_DEVICE_ATTR_IN(ix) \ static SENSOR_DEVICE_ATTR_2(in##ix##_input, S_IRUGO, \ @@ -1421,6 +1531,7 @@ SENSOR_DEVICE_ATTR_IN(3); SENSOR_DEVICE_ATTR_IN(4); SENSOR_DEVICE_ATTR_IN(5); SENSOR_DEVICE_ATTR_IN(6); +SENSOR_DEVICE_ATTR_IN(7); /* Temperatures 1-3 */ @@ -1535,11 +1646,13 @@ static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); /* for ISA devices */ -/* This struct holds all the attributes that are always present and need to be +/* + * This struct holds all the attributes that are always present and need to be * created unconditionally. The attributes that need modification of their * permissions are created read-only and write permissions are added or removed - * on the fly when required */ -static struct attribute *dme1737_attr[] ={ + * on the fly when required + */ +static struct attribute *dme1737_attr[] = { /* Voltages */ &sensor_dev_attr_in0_input.dev_attr.attr, &sensor_dev_attr_in0_min.dev_attr.attr, @@ -1594,10 +1707,6 @@ static struct attribute *dme1737_attr[] ={ &sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr, &sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr, &sensor_dev_attr_zone2_auto_channels_temp.dev_attr.attr, - &sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr, - &sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr, - &sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr, - &sensor_dev_attr_zone3_auto_channels_temp.dev_attr.attr, NULL }; @@ -1605,27 +1714,27 @@ static const struct attribute_group dme1737_group = { .attrs = dme1737_attr, }; -/* The following struct holds misc attributes, which are not available in all - * chips. Their creation depends on the chip type which is determined during - * module load. */ -static struct attribute *dme1737_misc_attr[] = { - /* Temperatures */ +/* + * The following struct holds temp offset attributes, which are not available + * in all chips. The following chips support them: + * DME1737, SCH311x + */ +static struct attribute *dme1737_temp_offset_attr[] = { &sensor_dev_attr_temp1_offset.dev_attr.attr, &sensor_dev_attr_temp2_offset.dev_attr.attr, &sensor_dev_attr_temp3_offset.dev_attr.attr, - /* Zones */ - &sensor_dev_attr_zone1_auto_point1_temp_hyst.dev_attr.attr, - &sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.attr, - &sensor_dev_attr_zone3_auto_point1_temp_hyst.dev_attr.attr, NULL }; -static const struct attribute_group dme1737_misc_group = { - .attrs = dme1737_misc_attr, +static const struct attribute_group dme1737_temp_offset_group = { + .attrs = dme1737_temp_offset_attr, }; -/* The following struct holds VID-related attributes. Their creation - depends on the chip type which is determined during module load. */ +/* + * The following struct holds VID related attributes, which are not available + * in all chips. The following chips support them: + * DME1737 + */ static struct attribute *dme1737_vid_attr[] = { &dev_attr_vrm.attr, &dev_attr_cpu0_vid.attr, @@ -1636,9 +1745,62 @@ static const struct attribute_group dme1737_vid_group = { .attrs = dme1737_vid_attr, }; -/* The following structs hold the PWM attributes, some of which are optional. +/* + * The following struct holds temp zone 3 related attributes, which are not + * available in all chips. The following chips support them: + * DME1737, SCH311x, SCH5027 + */ +static struct attribute *dme1737_zone3_attr[] = { + &sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_zone3_auto_channels_temp.dev_attr.attr, + NULL +}; + +static const struct attribute_group dme1737_zone3_group = { + .attrs = dme1737_zone3_attr, +}; + + +/* + * The following struct holds temp zone hysteresis related attributes, which + * are not available in all chips. The following chips support them: + * DME1737, SCH311x + */ +static struct attribute *dme1737_zone_hyst_attr[] = { + &sensor_dev_attr_zone1_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_zone3_auto_point1_temp_hyst.dev_attr.attr, + NULL +}; + +static const struct attribute_group dme1737_zone_hyst_group = { + .attrs = dme1737_zone_hyst_attr, +}; + +/* + * The following struct holds voltage in7 related attributes, which + * are not available in all chips. The following chips support them: + * SCH5127 + */ +static struct attribute *dme1737_in7_attr[] = { + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in7_min.dev_attr.attr, + &sensor_dev_attr_in7_max.dev_attr.attr, + &sensor_dev_attr_in7_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group dme1737_in7_group = { + .attrs = dme1737_in7_attr, +}; + +/* + * The following structs hold the PWM attributes, some of which are optional. * Their creation depends on the chip configuration which is determined during - * module load. */ + * module load. + */ static struct attribute *dme1737_pwm1_attr[] = { &sensor_dev_attr_pwm1.dev_attr.attr, &sensor_dev_attr_pwm1_freq.dev_attr.attr, @@ -1691,18 +1853,22 @@ static const struct attribute_group dme1737_pwm_group[] = { { .attrs = dme1737_pwm6_attr }, }; -/* The following struct holds misc PWM attributes, which are not available in - * all chips. Their creation depends on the chip type which is determined - * during module load. */ -static struct attribute *dme1737_pwm_misc_attr[] = { +/* + * The following struct holds auto PWM min attributes, which are not available + * in all chips. Their creation depends on the chip type which is determined + * during module load. + */ +static struct attribute *dme1737_auto_pwm_min_attr[] = { &sensor_dev_attr_pwm1_auto_pwm_min.dev_attr.attr, &sensor_dev_attr_pwm2_auto_pwm_min.dev_attr.attr, &sensor_dev_attr_pwm3_auto_pwm_min.dev_attr.attr, }; -/* The following structs hold the fan attributes, some of which are optional. +/* + * The following structs hold the fan attributes, some of which are optional. * Their creation depends on the chip configuration which is determined during - * module load. */ + * module load. + */ static struct attribute *dme1737_fan1_attr[] = { &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan1_min.dev_attr.attr, @@ -1755,8 +1921,10 @@ static const struct attribute_group dme1737_fan_group[] = { { .attrs = dme1737_fan6_attr }, }; -/* The permissions of the following zone attributes are changed to read- - * writeable if the chip is *not* locked. Otherwise they stay read-only. */ +/* + * The permissions of the following zone attributes are changed to read- + * writeable if the chip is *not* locked. Otherwise they stay read-only. + */ static struct attribute *dme1737_zone_chmod_attr[] = { &sensor_dev_attr_zone1_auto_point1_temp.dev_attr.attr, &sensor_dev_attr_zone1_auto_point2_temp.dev_attr.attr, @@ -1764,19 +1932,34 @@ static struct attribute *dme1737_zone_chmod_attr[] = { &sensor_dev_attr_zone2_auto_point1_temp.dev_attr.attr, &sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr, &sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr, + NULL +}; + +static const struct attribute_group dme1737_zone_chmod_group = { + .attrs = dme1737_zone_chmod_attr, +}; + + +/* + * The permissions of the following zone 3 attributes are changed to read- + * writeable if the chip is *not* locked. Otherwise they stay read-only. + */ +static struct attribute *dme1737_zone3_chmod_attr[] = { &sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr, &sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr, &sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr, NULL }; -static const struct attribute_group dme1737_zone_chmod_group = { - .attrs = dme1737_zone_chmod_attr, +static const struct attribute_group dme1737_zone3_chmod_group = { + .attrs = dme1737_zone3_chmod_attr, }; -/* The permissions of the following PWM attributes are changed to read- +/* + * The permissions of the following PWM attributes are changed to read- * writeable if the chip is *not* locked and the respective PWM is available. - * Otherwise they stay read-only. */ + * Otherwise they stay read-only. + */ static struct attribute *dme1737_pwm1_chmod_attr[] = { &sensor_dev_attr_pwm1_freq.dev_attr.attr, &sensor_dev_attr_pwm1_enable.dev_attr.attr, @@ -1821,8 +2004,10 @@ static const struct attribute_group dme1737_pwm_chmod_group[] = { { .attrs = dme1737_pwm6_chmod_attr }, }; -/* Pwm[1-3] are read-writeable if the associated pwm is in manual mode and the - * chip is not locked. Otherwise they are read-only. */ +/* + * Pwm[1-3] are read-writeable if the associated pwm is in manual mode and the + * chip is not locked. Otherwise they are read-only. + */ static struct attribute *dme1737_pwm_chmod_attr[] = { &sensor_dev_attr_pwm1.dev_attr.attr, &sensor_dev_attr_pwm2.dev_attr.attr, @@ -1862,7 +2047,7 @@ static inline void dme1737_sio_outb(int sio_cip, int reg, int val) static int dme1737_i2c_get_features(int, struct dme1737_data*); static void dme1737_chmod_file(struct device *dev, - struct attribute *attr, mode_t mode) + struct attribute *attr, umode_t mode) { if (sysfs_chmod_file(&dev->kobj, attr, mode)) { dev_warn(dev, "Failed to change permissions of %s.\n", @@ -1872,13 +2057,12 @@ static void dme1737_chmod_file(struct device *dev, static void dme1737_chmod_group(struct device *dev, const struct attribute_group *group, - mode_t mode) + umode_t mode) { struct attribute **attr; - for (attr = group->attrs; *attr; attr++) { + for (attr = group->attrs; *attr; attr++) dme1737_chmod_file(dev, *attr, mode); - } } static void dme1737_remove_files(struct device *dev) @@ -1887,35 +2071,37 @@ static void dme1737_remove_files(struct device *dev) int ix; for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) { - if (data->has_fan & (1 << ix)) { + if (data->has_features & HAS_FAN(ix)) { sysfs_remove_group(&dev->kobj, &dme1737_fan_group[ix]); } } for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_group); ix++) { - if (data->has_pwm & (1 << ix)) { + if (data->has_features & HAS_PWM(ix)) { sysfs_remove_group(&dev->kobj, &dme1737_pwm_group[ix]); - if (data->type != sch5027 && ix < 3) { + if ((data->has_features & HAS_PWM_MIN) && ix < 3) { sysfs_remove_file(&dev->kobj, - dme1737_pwm_misc_attr[ix]); + dme1737_auto_pwm_min_attr[ix]); } } } - if (data->type != sch5027) { - sysfs_remove_group(&dev->kobj, &dme1737_misc_group); - } - if (data->type == dme1737) { + if (data->has_features & HAS_TEMP_OFFSET) + sysfs_remove_group(&dev->kobj, &dme1737_temp_offset_group); + if (data->has_features & HAS_VID) sysfs_remove_group(&dev->kobj, &dme1737_vid_group); - } - + if (data->has_features & HAS_ZONE3) + sysfs_remove_group(&dev->kobj, &dme1737_zone3_group); + if (data->has_features & HAS_ZONE_HYST) + sysfs_remove_group(&dev->kobj, &dme1737_zone_hyst_group); + if (data->has_features & HAS_IN7) + sysfs_remove_group(&dev->kobj, &dme1737_in7_group); sysfs_remove_group(&dev->kobj, &dme1737_group); - if (!data->client) { + if (!data->client) sysfs_remove_file(&dev->kobj, &dev_attr_name.attr); - } } static int dme1737_create_files(struct device *dev) @@ -1924,80 +2110,107 @@ static int dme1737_create_files(struct device *dev) int err, ix; /* Create a name attribute for ISA devices */ - if (!data->client && - (err = sysfs_create_file(&dev->kobj, &dev_attr_name.attr))) { - goto exit; + if (!data->client) { + err = sysfs_create_file(&dev->kobj, &dev_attr_name.attr); + if (err) + goto exit; } /* Create standard sysfs attributes */ - if ((err = sysfs_create_group(&dev->kobj, &dme1737_group))) { + err = sysfs_create_group(&dev->kobj, &dme1737_group); + if (err) goto exit_remove; - } - /* Create misc sysfs attributes */ - if ((data->type != sch5027) && - (err = sysfs_create_group(&dev->kobj, - &dme1737_misc_group))) { - goto exit_remove; + /* Create chip-dependent sysfs attributes */ + if (data->has_features & HAS_TEMP_OFFSET) { + err = sysfs_create_group(&dev->kobj, + &dme1737_temp_offset_group); + if (err) + goto exit_remove; } - - /* Create VID-related sysfs attributes */ - if ((data->type == dme1737) && - (err = sysfs_create_group(&dev->kobj, - &dme1737_vid_group))) { - goto exit_remove; + if (data->has_features & HAS_VID) { + err = sysfs_create_group(&dev->kobj, &dme1737_vid_group); + if (err) + goto exit_remove; + } + if (data->has_features & HAS_ZONE3) { + err = sysfs_create_group(&dev->kobj, &dme1737_zone3_group); + if (err) + goto exit_remove; + } + if (data->has_features & HAS_ZONE_HYST) { + err = sysfs_create_group(&dev->kobj, &dme1737_zone_hyst_group); + if (err) + goto exit_remove; + } + if (data->has_features & HAS_IN7) { + err = sysfs_create_group(&dev->kobj, &dme1737_in7_group); + if (err) + goto exit_remove; } /* Create fan sysfs attributes */ for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) { - if (data->has_fan & (1 << ix)) { - if ((err = sysfs_create_group(&dev->kobj, - &dme1737_fan_group[ix]))) { + if (data->has_features & HAS_FAN(ix)) { + err = sysfs_create_group(&dev->kobj, + &dme1737_fan_group[ix]); + if (err) goto exit_remove; - } } } /* Create PWM sysfs attributes */ for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_group); ix++) { - if (data->has_pwm & (1 << ix)) { - if ((err = sysfs_create_group(&dev->kobj, - &dme1737_pwm_group[ix]))) { - goto exit_remove; - } - if (data->type != sch5027 && ix < 3 && - (err = sysfs_create_file(&dev->kobj, - dme1737_pwm_misc_attr[ix]))) { + if (data->has_features & HAS_PWM(ix)) { + err = sysfs_create_group(&dev->kobj, + &dme1737_pwm_group[ix]); + if (err) goto exit_remove; + if ((data->has_features & HAS_PWM_MIN) && (ix < 3)) { + err = sysfs_create_file(&dev->kobj, + dme1737_auto_pwm_min_attr[ix]); + if (err) + goto exit_remove; } } } - /* Inform if the device is locked. Otherwise change the permissions of - * selected attributes from read-only to read-writeable. */ + /* + * Inform if the device is locked. Otherwise change the permissions of + * selected attributes from read-only to read-writeable. + */ if (data->config & 0x02) { - dev_info(dev, "Device is locked. Some attributes " - "will be read-only.\n"); + dev_info(dev, + "Device is locked. Some attributes will be read-only.\n"); } else { /* Change permissions of zone sysfs attributes */ dme1737_chmod_group(dev, &dme1737_zone_chmod_group, S_IRUGO | S_IWUSR); - /* Change permissions of misc sysfs attributes */ - if (data->type != sch5027) { - dme1737_chmod_group(dev, &dme1737_misc_group, + /* Change permissions of chip-dependent sysfs attributes */ + if (data->has_features & HAS_TEMP_OFFSET) { + dme1737_chmod_group(dev, &dme1737_temp_offset_group, + S_IRUGO | S_IWUSR); + } + if (data->has_features & HAS_ZONE3) { + dme1737_chmod_group(dev, &dme1737_zone3_chmod_group, + S_IRUGO | S_IWUSR); + } + if (data->has_features & HAS_ZONE_HYST) { + dme1737_chmod_group(dev, &dme1737_zone_hyst_group, S_IRUGO | S_IWUSR); } /* Change permissions of PWM sysfs attributes */ for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_chmod_group); ix++) { - if (data->has_pwm & (1 << ix)) { + if (data->has_features & HAS_PWM(ix)) { dme1737_chmod_group(dev, &dme1737_pwm_chmod_group[ix], S_IRUGO | S_IWUSR); - if (data->type != sch5027 && ix < 3) { + if ((data->has_features & HAS_PWM_MIN) && + ix < 3) { dme1737_chmod_file(dev, - dme1737_pwm_misc_attr[ix], + dme1737_auto_pwm_min_attr[ix], S_IRUGO | S_IWUSR); } } @@ -2005,7 +2218,7 @@ static int dme1737_create_files(struct device *dev) /* Change permissions of pwm[1-3] if in manual mode */ for (ix = 0; ix < 3; ix++) { - if ((data->has_pwm & (1 << ix)) && + if ((data->has_features & HAS_PWM(ix)) && (PWM_EN_FROM_REG(data->pwm_config[ix]) == 1)) { dme1737_chmod_file(dev, dme1737_pwm_chmod_attr[ix], @@ -2036,9 +2249,8 @@ static int dme1737_init_device(struct device *dev) /* Inform if part is not monitoring/started */ if (!(data->config & 0x01)) { if (!force_start) { - dev_err(dev, "Device is not monitoring. " - "Use the force_start load parameter to " - "override.\n"); + dev_err(dev, + "Device is not monitoring. Use the force_start load parameter to override.\n"); return -EFAULT; } @@ -2052,80 +2264,99 @@ static int dme1737_init_device(struct device *dev) return -EFAULT; } - /* Determine which optional fan and pwm features are enabled/present */ + /* + * Determine which optional fan and pwm features are enabled (only + * valid for I2C devices) + */ if (client) { /* I2C chip */ data->config2 = dme1737_read(data, DME1737_REG_CONFIG2); /* Check if optional fan3 input is enabled */ - if (data->config2 & 0x04) { - data->has_fan |= (1 << 2); - } + if (data->config2 & 0x04) + data->has_features |= HAS_FAN(2); - /* Fan4 and pwm3 are only available if the client's I2C address + /* + * Fan4 and pwm3 are only available if the client's I2C address * is the default 0x2e. Otherwise the I/Os associated with - * these functions are used for addr enable/select. */ - if (client->addr == 0x2e) { - data->has_fan |= (1 << 3); - data->has_pwm |= (1 << 2); - } + * these functions are used for addr enable/select. + */ + if (client->addr == 0x2e) + data->has_features |= HAS_FAN(3) | HAS_PWM(2); - /* Determine which of the optional fan[5-6] and pwm[5-6] + /* + * Determine which of the optional fan[5-6] and pwm[5-6] * features are enabled. For this, we need to query the runtime * registers through the Super-IO LPC interface. Try both - * config ports 0x2e and 0x4e. */ + * config ports 0x2e and 0x4e. + */ if (dme1737_i2c_get_features(0x2e, data) && dme1737_i2c_get_features(0x4e, data)) { - dev_warn(dev, "Failed to query Super-IO for optional " - "features.\n"); + dev_warn(dev, + "Failed to query Super-IO for optional features.\n"); } - } else { /* ISA chip */ - /* Fan3 and pwm3 are always available. Fan[4-5] and pwm[5-6] - * don't exist in the ISA chip. */ - data->has_fan |= (1 << 2); - data->has_pwm |= (1 << 2); } - /* Fan1, fan2, pwm1, and pwm2 are always present */ - data->has_fan |= 0x03; - data->has_pwm |= 0x03; + /* Fan[1-2] and pwm[1-2] are present in all chips */ + data->has_features |= HAS_FAN(0) | HAS_FAN(1) | HAS_PWM(0) | HAS_PWM(1); - dev_info(dev, "Optional features: pwm3=%s, pwm5=%s, pwm6=%s, " - "fan3=%s, fan4=%s, fan5=%s, fan6=%s.\n", - (data->has_pwm & (1 << 2)) ? "yes" : "no", - (data->has_pwm & (1 << 4)) ? "yes" : "no", - (data->has_pwm & (1 << 5)) ? "yes" : "no", - (data->has_fan & (1 << 2)) ? "yes" : "no", - (data->has_fan & (1 << 3)) ? "yes" : "no", - (data->has_fan & (1 << 4)) ? "yes" : "no", - (data->has_fan & (1 << 5)) ? "yes" : "no"); + /* Chip-dependent features */ + switch (data->type) { + case dme1737: + data->has_features |= HAS_TEMP_OFFSET | HAS_VID | HAS_ZONE3 | + HAS_ZONE_HYST | HAS_PWM_MIN; + break; + case sch311x: + data->has_features |= HAS_TEMP_OFFSET | HAS_ZONE3 | + HAS_ZONE_HYST | HAS_PWM_MIN | HAS_FAN(2) | HAS_PWM(2); + break; + case sch5027: + data->has_features |= HAS_ZONE3; + break; + case sch5127: + data->has_features |= HAS_FAN(2) | HAS_PWM(2) | HAS_IN7; + break; + default: + break; + } + + dev_info(dev, + "Optional features: pwm3=%s, pwm5=%s, pwm6=%s, fan3=%s, fan4=%s, fan5=%s, fan6=%s.\n", + (data->has_features & HAS_PWM(2)) ? "yes" : "no", + (data->has_features & HAS_PWM(4)) ? "yes" : "no", + (data->has_features & HAS_PWM(5)) ? "yes" : "no", + (data->has_features & HAS_FAN(2)) ? "yes" : "no", + (data->has_features & HAS_FAN(3)) ? "yes" : "no", + (data->has_features & HAS_FAN(4)) ? "yes" : "no", + (data->has_features & HAS_FAN(5)) ? "yes" : "no"); reg = dme1737_read(data, DME1737_REG_TACH_PWM); /* Inform if fan-to-pwm mapping differs from the default */ if (client && reg != 0xa4) { /* I2C chip */ - dev_warn(dev, "Non-standard fan to pwm mapping: " - "fan1->pwm%d, fan2->pwm%d, fan3->pwm%d, " - "fan4->pwm%d. Please report to the driver " - "maintainer.\n", + dev_warn(dev, + "Non-standard fan to pwm mapping: fan1->pwm%d, fan2->pwm%d, fan3->pwm%d, fan4->pwm%d. %s\n", (reg & 0x03) + 1, ((reg >> 2) & 0x03) + 1, - ((reg >> 4) & 0x03) + 1, ((reg >> 6) & 0x03) + 1); + ((reg >> 4) & 0x03) + 1, ((reg >> 6) & 0x03) + 1, + DO_REPORT); } else if (!client && reg != 0x24) { /* ISA chip */ - dev_warn(dev, "Non-standard fan to pwm mapping: " - "fan1->pwm%d, fan2->pwm%d, fan3->pwm%d. " - "Please report to the driver maintainer.\n", + dev_warn(dev, + "Non-standard fan to pwm mapping: fan1->pwm%d, fan2->pwm%d, fan3->pwm%d. %s\n", (reg & 0x03) + 1, ((reg >> 2) & 0x03) + 1, - ((reg >> 4) & 0x03) + 1); + ((reg >> 4) & 0x03) + 1, DO_REPORT); } - /* Switch pwm[1-3] to manual mode if they are currently disabled and + /* + * Switch pwm[1-3] to manual mode if they are currently disabled and * set the duty-cycles to 0% (which is identical to the PWMs being - * disabled). */ + * disabled). + */ if (!(data->config & 0x02)) { for (ix = 0; ix < 3; ix++) { data->pwm_config[ix] = dme1737_read(data, DME1737_REG_PWM_CONFIG(ix)); - if ((data->has_pwm & (1 << ix)) && + if ((data->has_features & HAS_PWM(ix)) && (PWM_EN_FROM_REG(data->pwm_config[ix]) == -1)) { - dev_info(dev, "Switching pwm%d to " - "manual mode.\n", ix + 1); + dev_info(dev, + "Switching pwm%d to manual mode.\n", + ix + 1); data->pwm_config[ix] = PWM_EN_TO_REG(1, data->pwm_config[ix]); dme1737_write(data, DME1737_REG_PWM(ix), 0); @@ -2142,9 +2373,8 @@ static int dme1737_init_device(struct device *dev) data->pwm_acz[2] = 4; /* pwm3 -> zone3 */ /* Set VRM */ - if (data->type == dme1737) { + if (data->has_features & HAS_VID) data->vrm = vid_which_vrm(); - } return 0; } @@ -2162,11 +2392,13 @@ static int dme1737_i2c_get_features(int sio_cip, struct dme1737_data *data) dme1737_sio_enter(sio_cip); - /* Check device ID - * The DME1737 can return either 0x78 or 0x77 as its device ID. - * The SCH5027 returns 0x89 as its device ID. */ + /* + * Check device ID + * We currently know about two kinds of DME1737 and SCH5027. + */ reg = force_id ? force_id : dme1737_sio_inb(sio_cip, 0x20); - if (!(reg == 0x77 || reg == 0x78 || reg == 0x89)) { + if (!(reg == DME1737_ID_1 || reg == DME1737_ID_2 || + reg == SCH5027_ID)) { err = -ENODEV; goto exit; } @@ -2175,27 +2407,26 @@ static int dme1737_i2c_get_features(int sio_cip, struct dme1737_data *data) dme1737_sio_outb(sio_cip, 0x07, 0x0a); /* Get the base address of the runtime registers */ - if (!(addr = (dme1737_sio_inb(sio_cip, 0x60) << 8) | - dme1737_sio_inb(sio_cip, 0x61))) { + addr = (dme1737_sio_inb(sio_cip, 0x60) << 8) | + dme1737_sio_inb(sio_cip, 0x61); + if (!addr) { err = -ENODEV; goto exit; } - /* Read the runtime registers to determine which optional features + /* + * Read the runtime registers to determine which optional features * are enabled and available. Bits [3:2] of registers 0x43-0x46 are set - * to '10' if the respective feature is enabled. */ - if ((inb(addr + 0x43) & 0x0c) == 0x08) { /* fan6 */ - data->has_fan |= (1 << 5); - } - if ((inb(addr + 0x44) & 0x0c) == 0x08) { /* pwm6 */ - data->has_pwm |= (1 << 5); - } - if ((inb(addr + 0x45) & 0x0c) == 0x08) { /* fan5 */ - data->has_fan |= (1 << 4); - } - if ((inb(addr + 0x46) & 0x0c) == 0x08) { /* pwm5 */ - data->has_pwm |= (1 << 4); - } + * to '10' if the respective feature is enabled. + */ + if ((inb(addr + 0x43) & 0x0c) == 0x08) /* fan6 */ + data->has_features |= HAS_FAN(5); + if ((inb(addr + 0x44) & 0x0c) == 0x08) /* pwm6 */ + data->has_features |= HAS_PWM(5); + if ((inb(addr + 0x45) & 0x0c) == 0x08) /* fan5 */ + data->has_features |= HAS_FAN(4); + if ((inb(addr + 0x46) & 0x0c) == 0x08) /* pwm5 */ + data->has_features |= HAS_PWM(4); exit: dme1737_sio_exit(sio_cip); @@ -2212,9 +2443,8 @@ static int dme1737_i2c_detect(struct i2c_client *client, u8 company, verstep = 0; const char *name; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - } company = i2c_smbus_read_byte_data(client, DME1737_REG_COMPANY); verstep = i2c_smbus_read_byte_data(client, DME1737_REG_VERSTEP); @@ -2222,7 +2452,6 @@ static int dme1737_i2c_detect(struct i2c_client *client, if (company == DME1737_COMPANY_SMSC && verstep == SCH5027_VERSTEP) { name = "sch5027"; - } else if (company == DME1737_COMPANY_SMSC && (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) { name = "dme1737"; @@ -2245,11 +2474,9 @@ static int dme1737_i2c_probe(struct i2c_client *client, struct device *dev = &client->dev; int err; - data = kzalloc(sizeof(struct dme1737_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(dev, sizeof(struct dme1737_data), GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); data->type = id->driver_data; @@ -2258,15 +2485,17 @@ static int dme1737_i2c_probe(struct i2c_client *client, mutex_init(&data->update_lock); /* Initialize the DME1737 chip */ - if ((err = dme1737_init_device(dev))) { + err = dme1737_init_device(dev); + if (err) { dev_err(dev, "Failed to initialize device.\n"); - goto exit_kfree; + return err; } /* Create sysfs files */ - if ((err = dme1737_create_files(dev))) { + err = dme1737_create_files(dev); + if (err) { dev_err(dev, "Failed to create sysfs files.\n"); - goto exit_kfree; + return err; } /* Register device */ @@ -2281,9 +2510,6 @@ static int dme1737_i2c_probe(struct i2c_client *client, exit_remove: dme1737_remove_files(dev); -exit_kfree: - kfree(data); -exit: return err; } @@ -2294,7 +2520,6 @@ static int dme1737_i2c_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); dme1737_remove_files(&client->dev); - kfree(data); return 0; } @@ -2328,11 +2553,13 @@ static int __init dme1737_isa_detect(int sio_cip, unsigned short *addr) dme1737_sio_enter(sio_cip); - /* Check device ID - * We currently know about SCH3112 (0x7c), SCH3114 (0x7d), and - * SCH3116 (0x7f). */ + /* + * Check device ID + * We currently know about SCH3112, SCH3114, SCH3116, and SCH5127 + */ reg = force_id ? force_id : dme1737_sio_inb(sio_cip, 0x20); - if (!(reg == 0x7c || reg == 0x7d || reg == 0x7f)) { + if (!(reg == SCH3112_ID || reg == SCH3114_ID || reg == SCH3116_ID || + reg == SCH5127_ID)) { err = -ENODEV; goto exit; } @@ -2341,15 +2568,18 @@ static int __init dme1737_isa_detect(int sio_cip, unsigned short *addr) dme1737_sio_outb(sio_cip, 0x07, 0x0a); /* Get the base address of the runtime registers */ - if (!(base_addr = (dme1737_sio_inb(sio_cip, 0x60) << 8) | - dme1737_sio_inb(sio_cip, 0x61))) { - printk(KERN_ERR "dme1737: Base address not set.\n"); + base_addr = (dme1737_sio_inb(sio_cip, 0x60) << 8) | + dme1737_sio_inb(sio_cip, 0x61); + if (!base_addr) { + pr_err("Base address not set\n"); err = -ENODEV; goto exit; } - /* Access to the hwmon registers is through an index/data register - * pair located at offset 0x70/0x71. */ + /* + * Access to the hwmon registers is through an index/data register + * pair located at offset 0x70/0x71. + */ *addr = base_addr + 0x70; exit: @@ -2371,21 +2601,22 @@ static int __init dme1737_isa_device_add(unsigned short addr) if (err) goto exit; - if (!(pdev = platform_device_alloc("dme1737", addr))) { - printk(KERN_ERR "dme1737: Failed to allocate device.\n"); + pdev = platform_device_alloc("dme1737", addr); + if (!pdev) { + pr_err("Failed to allocate device\n"); err = -ENOMEM; goto exit; } - if ((err = platform_device_add_resources(pdev, &res, 1))) { - printk(KERN_ERR "dme1737: Failed to add device resource " - "(err = %d).\n", err); + err = platform_device_add_resources(pdev, &res, 1); + if (err) { + pr_err("Failed to add device resource (err = %d)\n", err); goto exit_device_put; } - if ((err = platform_device_add(pdev))) { - printk(KERN_ERR "dme1737: Failed to add device (err = %d).\n", - err); + err = platform_device_add(pdev); + if (err) { + pr_err("Failed to add device (err = %d)\n", err); goto exit_device_put; } @@ -2398,7 +2629,7 @@ exit: return err; } -static int __devinit dme1737_isa_probe(struct platform_device *pdev) +static int dme1737_isa_probe(struct platform_device *pdev) { u8 company, device; struct resource *res; @@ -2407,51 +2638,68 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev) int err; res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start, DME1737_EXTENT, "dme1737")) { + if (!devm_request_region(dev, res->start, DME1737_EXTENT, "dme1737")) { dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n", (unsigned short)res->start, (unsigned short)res->start + DME1737_EXTENT - 1); - err = -EBUSY; - goto exit; - } - - if (!(data = kzalloc(sizeof(struct dme1737_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit_release_region; + return -EBUSY; } + data = devm_kzalloc(dev, sizeof(struct dme1737_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + data->addr = res->start; platform_set_drvdata(pdev, data); /* Skip chip detection if module is loaded with force_id parameter */ - if (!force_id) { + switch (force_id) { + case SCH3112_ID: + case SCH3114_ID: + case SCH3116_ID: + data->type = sch311x; + break; + case SCH5127_ID: + data->type = sch5127; + break; + default: company = dme1737_read(data, DME1737_REG_COMPANY); device = dme1737_read(data, DME1737_REG_DEVICE); - if (!((company == DME1737_COMPANY_SMSC) && - (device == SCH311X_DEVICE))) { - err = -ENODEV; - goto exit_kfree; + if ((company == DME1737_COMPANY_SMSC) && + (device == SCH311X_DEVICE)) { + data->type = sch311x; + } else if ((company == DME1737_COMPANY_SMSC) && + (device == SCH5127_DEVICE)) { + data->type = sch5127; + } else { + return -ENODEV; } } - data->type = sch311x; - /* Fill in the remaining client fields and initialize the mutex */ - data->name = "sch311x"; + if (data->type == sch5127) + data->name = "sch5127"; + else + data->name = "sch311x"; + + /* Initialize the mutex */ mutex_init(&data->update_lock); - dev_info(dev, "Found a SCH311x chip at 0x%04x\n", data->addr); + dev_info(dev, "Found a %s chip at 0x%04x\n", + data->type == sch5127 ? "SCH5127" : "SCH311x", data->addr); /* Initialize the chip */ - if ((err = dme1737_init_device(dev))) { + err = dme1737_init_device(dev); + if (err) { dev_err(dev, "Failed to initialize device.\n"); - goto exit_kfree; + return err; } /* Create sysfs files */ - if ((err = dme1737_create_files(dev))) { + err = dme1737_create_files(dev); + if (err) { dev_err(dev, "Failed to create sysfs files.\n"); - goto exit_kfree; + return err; } /* Register device */ @@ -2466,24 +2714,15 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev) exit_remove_files: dme1737_remove_files(dev); -exit_kfree: - platform_set_drvdata(pdev, NULL); - kfree(data); -exit_release_region: - release_region(res->start, DME1737_EXTENT); -exit: return err; } -static int __devexit dme1737_isa_remove(struct platform_device *pdev) +static int dme1737_isa_remove(struct platform_device *pdev) { struct dme1737_data *data = platform_get_drvdata(pdev); hwmon_device_unregister(data->hwmon_dev); dme1737_remove_files(&pdev->dev); - release_region(data->addr, DME1737_EXTENT); - platform_set_drvdata(pdev, NULL); - kfree(data); return 0; } @@ -2494,7 +2733,7 @@ static struct platform_driver dme1737_isa_driver = { .name = "dme1737", }, .probe = dme1737_isa_probe, - .remove = __devexit_p(dme1737_isa_remove), + .remove = dme1737_isa_remove, }; /* --------------------------------------------------------------------- @@ -2506,9 +2745,9 @@ static int __init dme1737_init(void) int err; unsigned short addr; - if ((err = i2c_add_driver(&dme1737_i2c_driver))) { + err = i2c_add_driver(&dme1737_i2c_driver); + if (err) goto exit; - } if (dme1737_isa_detect(0x2e, &addr) && dme1737_isa_detect(0x4e, &addr) && @@ -2519,14 +2758,14 @@ static int __init dme1737_init(void) return 0; } - if ((err = platform_driver_register(&dme1737_isa_driver))) { + err = platform_driver_register(&dme1737_isa_driver); + if (err) goto exit_del_i2c_driver; - } /* Sets global pdev as a side effect */ - if ((err = dme1737_isa_device_add(addr))) { + err = dme1737_isa_device_add(addr); + if (err) goto exit_del_isa_driver; - } return 0; diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c index e11363467a8..fc6f5d54e7f 100644 --- a/drivers/hwmon/ds1621.c +++ b/drivers/hwmon/ds1621.c @@ -1,25 +1,38 @@ /* - ds1621.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Christian W. Zuckschwerdt <zany@triq.net> 2000-11-23 - based on lm75.c by Frodo Looijaard <frodol@dds.nl> - Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with - the help of 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; 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. -*/ + * ds1621.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Christian W. Zuckschwerdt <zany@triq.net> 2000-11-23 + * based on lm75.c by Frodo Looijaard <frodol@dds.nl> + * Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with + * the help of Jean Delvare <jdelvare@suse.de> + * + * The DS1621 device is a digital temperature/thermometer with 9-bit + * resolution, a thermal alarm output (Tout), and user-defined minimum + * and maximum temperature thresholds (TH and TL). + * + * The DS1625, DS1631, DS1721, and DS1731 are pin compatible with the DS1621 + * and similar in operation, with slight variations as noted in the device + * datasheets (please refer to www.maximintegrated.com for specific + * device information). + * + * Since the DS1621 was the first chipset supported by this driver, + * most comments will refer to this chipset, but are actually general + * and concern all supported chipsets, unless mentioned otherwise. + * + * 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> @@ -31,27 +44,62 @@ #include <linux/err.h> #include <linux/mutex.h> #include <linux/sysfs.h> -#include "lm75.h" +#include <linux/kernel.h> -/* Addresses to scan */ -static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, - 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; +/* Supported devices */ +enum chips { ds1621, ds1625, ds1631, ds1721, ds1731 }; /* Insmod parameters */ static int polarity = -1; module_param(polarity, int, 0); MODULE_PARM_DESC(polarity, "Output's polarity: 0 = active high, 1 = active low"); -/* Many DS1621 constants specified below */ -/* Config register used for detection */ -/* 7 6 5 4 3 2 1 0 */ -/* |Done|THF |TLF |NVB | X | X |POL |1SHOT| */ +/* + * The Configuration/Status register + * + * - DS1621: + * 7 6 5 4 3 2 1 0 + * |Done|THF |TLF |NVB | X | X |POL |1SHOT| + * + * - DS1625: + * 7 6 5 4 3 2 1 0 + * |Done|THF |TLF |NVB | 1 | 0 |POL |1SHOT| + * + * - DS1631, DS1731: + * 7 6 5 4 3 2 1 0 + * |Done|THF |TLF |NVB | R1 | R0 |POL |1SHOT| + * + * - DS1721: + * 7 6 5 4 3 2 1 0 + * |Done| X | X | U | R1 | R0 |POL |1SHOT| + * + * Where: + * - 'X' is Reserved + * - 'U' is Undefined + */ #define DS1621_REG_CONFIG_NVB 0x10 +#define DS1621_REG_CONFIG_RESOL 0x0C #define DS1621_REG_CONFIG_POLARITY 0x02 #define DS1621_REG_CONFIG_1SHOT 0x01 #define DS1621_REG_CONFIG_DONE 0x80 -/* The DS1621 registers */ +#define DS1621_REG_CONFIG_RESOL_SHIFT 2 + +/* ds1721 conversion rates: {C/LSB, time(ms), resolution bit setting} */ +static const unsigned short ds1721_convrates[] = { + 94, /* 9-bits (0.5, 93.75, RES[0..1] = 0 */ + 188, /* 10-bits (0.25, 187.5, RES[0..1] = 1 */ + 375, /* 11-bits (0.125, 375, RES[0..1] = 2 */ + 750, /* 12-bits (0.0625, 750, RES[0..1] = 3 */ +}; + +#define DS1621_CONVERSION_MAX 750 +#define DS1625_CONVERSION_MAX 500 + +#define DS1621_TEMP_MAX 125000 +#define DS1621_TEMP_MIN (-55000) + +/* The DS1621 temperature registers */ static const u8 DS1621_REG_TEMP[3] = { 0xAA, /* input, word, RO */ 0xA2, /* min, word, RW */ @@ -59,6 +107,7 @@ static const u8 DS1621_REG_TEMP[3] = { }; #define DS1621_REG_CONF 0xAC /* byte, RW */ #define DS1621_COM_START 0xEE /* no data */ +#define DS1721_COM_START 0x51 /* no data */ #define DS1621_COM_STOP 0x22 /* no data */ /* The DS1621 configuration register */ @@ -67,40 +116,45 @@ static const u8 DS1621_REG_TEMP[3] = { /* Conversions */ #define ALARMS_FROM_REG(val) ((val) & \ - (DS1621_ALARM_TEMP_HIGH | DS1621_ALARM_TEMP_LOW)) + (DS1621_ALARM_TEMP_HIGH | DS1621_ALARM_TEMP_LOW)) /* Each client has this additional data */ struct ds1621_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ + enum chips kind; /* device type */ u16 temp[3]; /* Register values, word */ u8 conf; /* Register encoding, combined */ + u8 zbits; /* Resolution encoded as number of + * zero bits */ + u16 update_interval; /* Conversion rate in milliseconds */ }; -/* Temperature registers are word-sized. - DS1621 uses a high-byte first convention, which is exactly opposite to - the SMBus standard. */ -static int ds1621_read_temp(struct i2c_client *client, u8 reg) +static inline int DS1621_TEMP_FROM_REG(u16 reg) { - int ret; - - ret = i2c_smbus_read_word_data(client, reg); - if (ret < 0) - return ret; - return swab16(ret); + return DIV_ROUND_CLOSEST(((s16)reg / 16) * 625, 10); } -static int ds1621_write_temp(struct i2c_client *client, u8 reg, u16 value) +/* + * TEMP: 0.001C/bit (-55C to +125C) + * REG: + * - 1621, 1625: 0.5C/bit, 7 zero-bits + * - 1631, 1721, 1731: 0.0625C/bit, 4 zero-bits + */ +static inline u16 DS1621_TEMP_TO_REG(long temp, u8 zbits) { - return i2c_smbus_write_word_data(client, reg, swab16(value)); + temp = clamp_val(temp, DS1621_TEMP_MIN, DS1621_TEMP_MAX); + temp = DIV_ROUND_CLOSEST(temp * (1 << (8 - zbits)), 1000) << zbits; + return temp; } -static void ds1621_init_client(struct i2c_client *client) +static void ds1621_init_client(struct ds1621_data *data, + struct i2c_client *client) { - u8 conf, new_conf; + u8 conf, new_conf, sreg, resol; new_conf = conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF); /* switch to continuous conversion mode */ @@ -111,24 +165,46 @@ static void ds1621_init_client(struct i2c_client *client) new_conf &= ~DS1621_REG_CONFIG_POLARITY; else if (polarity == 1) new_conf |= DS1621_REG_CONFIG_POLARITY; - + if (conf != new_conf) i2c_smbus_write_byte_data(client, DS1621_REG_CONF, new_conf); - + + switch (data->kind) { + case ds1625: + data->update_interval = DS1625_CONVERSION_MAX; + data->zbits = 7; + sreg = DS1621_COM_START; + break; + case ds1631: + case ds1721: + case ds1731: + resol = (new_conf & DS1621_REG_CONFIG_RESOL) >> + DS1621_REG_CONFIG_RESOL_SHIFT; + data->update_interval = ds1721_convrates[resol]; + data->zbits = 7 - resol; + sreg = DS1721_COM_START; + break; + default: + data->update_interval = DS1621_CONVERSION_MAX; + data->zbits = 7; + sreg = DS1621_COM_START; + break; + } + /* start conversion */ - i2c_smbus_write_byte(client, DS1621_COM_START); + i2c_smbus_write_byte(client, sreg); } static struct ds1621_data *ds1621_update_client(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct ds1621_data *data = i2c_get_clientdata(client); + struct ds1621_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; u8 new_conf; mutex_lock(&data->update_lock); - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { + if (time_after(jiffies, data->last_updated + data->update_interval) || + !data->valid) { int i; dev_dbg(&client->dev, "Starting ds1621 update\n"); @@ -136,7 +212,7 @@ static struct ds1621_data *ds1621_update_client(struct device *dev) data->conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF); for (i = 0; i < ARRAY_SIZE(data->temp); i++) - data->temp[i] = ds1621_read_temp(client, + data->temp[i] = i2c_smbus_read_word_swapped(client, DS1621_REG_TEMP[i]); /* reset alarms if necessary */ @@ -164,21 +240,25 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *da, struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct ds1621_data *data = ds1621_update_client(dev); return sprintf(buf, "%d\n", - LM75_TEMP_FROM_REG(data->temp[attr->index])); + DS1621_TEMP_FROM_REG(data->temp[attr->index])); } static ssize_t set_temp(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct i2c_client *client = to_i2c_client(dev); - struct ds1621_data *data = i2c_get_clientdata(client); - u16 val = LM75_TEMP_TO_REG(simple_strtol(buf, NULL, 10)); + struct ds1621_data *data = dev_get_drvdata(dev); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - data->temp[attr->index] = val; - ds1621_write_temp(client, DS1621_REG_TEMP[attr->index], - data->temp[attr->index]); + data->temp[attr->index] = DS1621_TEMP_TO_REG(val, data->zbits); + i2c_smbus_write_word_swapped(data->client, DS1621_REG_TEMP[attr->index], + data->temp[attr->index]); mutex_unlock(&data->update_lock); return count; } @@ -198,7 +278,46 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *da, return sprintf(buf, "%d\n", !!(data->conf & attr->index)); } +static ssize_t show_convrate(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct ds1621_data *data = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%hu\n", data->update_interval); +} + +static ssize_t set_convrate(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct ds1621_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long convrate; + s32 err; + int resol = 0; + + err = kstrtoul(buf, 10, &convrate); + if (err) + return err; + + /* Convert rate into resolution bits */ + while (resol < (ARRAY_SIZE(ds1721_convrates) - 1) && + convrate > ds1721_convrates[resol]) + resol++; + + mutex_lock(&data->update_lock); + data->conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF); + data->conf &= ~DS1621_REG_CONFIG_RESOL; + data->conf |= (resol << DS1621_REG_CONFIG_RESOL_SHIFT); + i2c_smbus_write_byte_data(client, DS1621_REG_CONF, data->conf); + data->update_interval = ds1721_convrates[resol]; + mutex_unlock(&data->update_lock); + + return count; +} + static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); +static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_convrate, + set_convrate); + static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp, set_temp, 1); static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp, set_temp, 2); @@ -214,98 +333,60 @@ static struct attribute *ds1621_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 }; +static umode_t ds1621_attribute_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct ds1621_data *data = dev_get_drvdata(dev); + + if (attr == &dev_attr_update_interval.attr) + if (data->kind == ds1621 || data->kind == ds1625) + /* shhh, we're hiding update_interval */ + return 0; + return attr->mode; +} + static const struct attribute_group ds1621_group = { .attrs = ds1621_attributes, + .is_visible = ds1621_attribute_visible }; - - -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int ds1621_detect(struct i2c_client *client, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = client->adapter; - int conf, temp; - int i; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA - | I2C_FUNC_SMBUS_WORD_DATA - | I2C_FUNC_SMBUS_WRITE_BYTE)) - return -ENODEV; - - /* Now, we do the remaining detection. It is lousy. */ - /* The NVB bit should be low if no EEPROM write has been requested - during the latest 10ms, which is highly improbable in our case. */ - conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF); - if (conf < 0 || conf & DS1621_REG_CONFIG_NVB) - return -ENODEV; - /* The 7 lowest bits of a temperature should always be 0. */ - for (i = 0; i < ARRAY_SIZE(DS1621_REG_TEMP); i++) { - temp = i2c_smbus_read_word_data(client, DS1621_REG_TEMP[i]); - if (temp < 0 || (temp & 0x7f00)) - return -ENODEV; - } - - strlcpy(info->type, "ds1621", I2C_NAME_SIZE); - - return 0; -} +__ATTRIBUTE_GROUPS(ds1621); static int ds1621_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ds1621_data *data; - int err; + struct device *hwmon_dev; - data = kzalloc(sizeof(struct ds1621_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&client->dev, sizeof(struct ds1621_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; - i2c_set_clientdata(client, data); mutex_init(&data->update_lock); - /* Initialize the DS1621 chip */ - ds1621_init_client(client); - - /* Register sysfs hooks */ - if ((err = sysfs_create_group(&client->dev.kobj, &ds1621_group))) - goto exit_free; - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - - exit_remove_files: - sysfs_remove_group(&client->dev.kobj, &ds1621_group); - exit_free: - kfree(data); - exit: - return err; -} + data->kind = id->driver_data; + data->client = client; -static int ds1621_remove(struct i2c_client *client) -{ - struct ds1621_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &ds1621_group); - - kfree(data); + /* Initialize the DS1621 chip */ + ds1621_init_client(data, client); - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, + client->name, data, + ds1621_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct i2c_device_id ds1621_id[] = { - { "ds1621", 0 }, - { "ds1625", 0 }, + { "ds1621", ds1621 }, + { "ds1625", ds1625 }, + { "ds1631", ds1631 }, + { "ds1721", ds1721 }, + { "ds1731", ds1731 }, { } }; MODULE_DEVICE_TABLE(i2c, ds1621_id); @@ -317,26 +398,11 @@ static struct i2c_driver ds1621_driver = { .name = "ds1621", }, .probe = ds1621_probe, - .remove = ds1621_remove, .id_table = ds1621_id, - .detect = ds1621_detect, - .address_list = normal_i2c, }; -static int __init ds1621_init(void) -{ - return i2c_add_driver(&ds1621_driver); -} - -static void __exit ds1621_exit(void) -{ - i2c_del_driver(&ds1621_driver); -} - +module_i2c_driver(ds1621_driver); MODULE_AUTHOR("Christian W. Zuckschwerdt <zany@triq.net>"); MODULE_DESCRIPTION("DS1621 driver"); MODULE_LICENSE("GPL"); - -module_init(ds1621_init); -module_exit(ds1621_exit); diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c new file mode 100644 index 00000000000..0918b913658 --- /dev/null +++ b/drivers/hwmon/ds620.c @@ -0,0 +1,298 @@ +/* + * ds620.c - Support for temperature sensor and thermostat DS620 + * + * Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de> + * + * based on ds1621.c by Christian W. Zuckschwerdt <zany@triq.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/sysfs.h> +#include <linux/i2c/ds620.h> + +/* + * Many DS620 constants specified below + * 15 14 13 12 11 10 09 08 + * |Done|NVB |THF |TLF |R1 |R0 |AUTOC|1SHOT| + * + * 07 06 05 04 03 02 01 00 + * |PO2 |PO1 |A2 |A1 |A0 | | | | + */ +#define DS620_REG_CONFIG_DONE 0x8000 +#define DS620_REG_CONFIG_NVB 0x4000 +#define DS620_REG_CONFIG_THF 0x2000 +#define DS620_REG_CONFIG_TLF 0x1000 +#define DS620_REG_CONFIG_R1 0x0800 +#define DS620_REG_CONFIG_R0 0x0400 +#define DS620_REG_CONFIG_AUTOC 0x0200 +#define DS620_REG_CONFIG_1SHOT 0x0100 +#define DS620_REG_CONFIG_PO2 0x0080 +#define DS620_REG_CONFIG_PO1 0x0040 +#define DS620_REG_CONFIG_A2 0x0020 +#define DS620_REG_CONFIG_A1 0x0010 +#define DS620_REG_CONFIG_A0 0x0008 + +/* The DS620 registers */ +static const u8 DS620_REG_TEMP[3] = { + 0xAA, /* input, word, RO */ + 0xA2, /* min, word, RW */ + 0xA0, /* max, word, RW */ +}; + +#define DS620_REG_CONF 0xAC /* word, RW */ +#define DS620_COM_START 0x51 /* no data */ +#define DS620_COM_STOP 0x22 /* no data */ + +/* Each client has this additional data */ +struct ds620_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + s16 temp[3]; /* Register values, word */ +}; + +static void ds620_init_client(struct i2c_client *client) +{ + struct ds620_platform_data *ds620_info = dev_get_platdata(&client->dev); + u16 conf, new_conf; + + new_conf = conf = + i2c_smbus_read_word_swapped(client, DS620_REG_CONF); + + /* switch to continuous conversion mode */ + new_conf &= ~DS620_REG_CONFIG_1SHOT; + /* already high at power-on, but don't trust the BIOS! */ + new_conf |= DS620_REG_CONFIG_PO2; + /* thermostat mode according to platform data */ + if (ds620_info && ds620_info->pomode == 1) + new_conf &= ~DS620_REG_CONFIG_PO1; /* PO_LOW */ + else if (ds620_info && ds620_info->pomode == 2) + new_conf |= DS620_REG_CONFIG_PO1; /* PO_HIGH */ + else + new_conf &= ~DS620_REG_CONFIG_PO2; /* always low */ + /* with highest precision */ + new_conf |= DS620_REG_CONFIG_R1 | DS620_REG_CONFIG_R0; + + if (conf != new_conf) + i2c_smbus_write_word_swapped(client, DS620_REG_CONF, new_conf); + + /* start conversion */ + i2c_smbus_write_byte(client, DS620_COM_START); +} + +static struct ds620_data *ds620_update_client(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds620_data *data = i2c_get_clientdata(client); + struct ds620_data *ret = data; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int i; + int res; + + dev_dbg(&client->dev, "Starting ds620 update\n"); + + for (i = 0; i < ARRAY_SIZE(data->temp); i++) { + res = i2c_smbus_read_word_swapped(client, + DS620_REG_TEMP[i]); + if (res < 0) { + ret = ERR_PTR(res); + goto abort; + } + + data->temp[i] = res; + } + + data->last_updated = jiffies; + data->valid = 1; + } +abort: + mutex_unlock(&data->update_lock); + + return ret; +} + +static ssize_t show_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ds620_data *data = ds620_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", ((data->temp[attr->index] / 8) * 625) / 10); +} + +static ssize_t set_temp(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int res; + long val; + + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + struct ds620_data *data = i2c_get_clientdata(client); + + res = kstrtol(buf, 10, &val); + + if (res) + return res; + + val = (val * 10 / 625) * 8; + + mutex_lock(&data->update_lock); + data->temp[attr->index] = val; + i2c_smbus_write_word_swapped(client, DS620_REG_TEMP[attr->index], + data->temp[attr->index]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t show_alarm(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ds620_data *data = ds620_update_client(dev); + struct i2c_client *client = to_i2c_client(dev); + u16 conf, new_conf; + int res; + + if (IS_ERR(data)) + return PTR_ERR(data); + + /* reset alarms if necessary */ + res = i2c_smbus_read_word_swapped(client, DS620_REG_CONF); + if (res < 0) + return res; + + new_conf = conf = res; + new_conf &= ~attr->index; + if (conf != new_conf) { + res = i2c_smbus_write_word_swapped(client, DS620_REG_CONF, + new_conf); + if (res < 0) + return res; + } + + return sprintf(buf, "%d\n", !!(conf & attr->index)); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp, set_temp, 1); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp, set_temp, 2); +static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, + DS620_REG_CONFIG_TLF); +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, + DS620_REG_CONFIG_THF); + +static struct attribute *ds620_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group ds620_group = { + .attrs = ds620_attributes, +}; + +static int ds620_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ds620_data *data; + int err; + + data = devm_kzalloc(&client->dev, sizeof(struct ds620_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* Initialize the DS620 chip */ + ds620_init_client(client); + + /* Register sysfs hooks */ + err = sysfs_create_group(&client->dev.kobj, &ds620_group); + if (err) + return err; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove_files; + } + + dev_info(&client->dev, "temperature sensor found\n"); + + return 0; + +exit_remove_files: + sysfs_remove_group(&client->dev.kobj, &ds620_group); + return err; +} + +static int ds620_remove(struct i2c_client *client) +{ + struct ds620_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &ds620_group); + + return 0; +} + +static const struct i2c_device_id ds620_id[] = { + {"ds620", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, ds620_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver ds620_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "ds620", + }, + .probe = ds620_probe, + .remove = ds620_remove, + .id_table = ds620_id, +}; + +module_i2c_driver(ds620_driver); + +MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); +MODULE_DESCRIPTION("DS620 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c new file mode 100644 index 00000000000..a37b2204a41 --- /dev/null +++ b/drivers/hwmon/emc1403.c @@ -0,0 +1,499 @@ +/* + * emc1403.c - SMSC Thermal Driver + * + * Copyright (C) 2008 Intel Corp + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 of the License. + * + * 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/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/sysfs.h> +#include <linux/mutex.h> +#include <linux/regmap.h> + +#define THERMAL_PID_REG 0xfd +#define THERMAL_SMSC_ID_REG 0xfe +#define THERMAL_REVISION_REG 0xff + +enum emc1403_chip { emc1402, emc1403, emc1404 }; + +struct thermal_data { + struct regmap *regmap; + struct mutex mutex; + const struct attribute_group *groups[4]; +}; + +static ssize_t show_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sda = to_sensor_dev_attr(attr); + struct thermal_data *data = dev_get_drvdata(dev); + unsigned int val; + int retval; + + retval = regmap_read(data->regmap, sda->index, &val); + if (retval < 0) + return retval; + return sprintf(buf, "%d000\n", val); +} + +static ssize_t show_bit(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sda = to_sensor_dev_attr_2(attr); + struct thermal_data *data = dev_get_drvdata(dev); + unsigned int val; + int retval; + + retval = regmap_read(data->regmap, sda->nr, &val); + if (retval < 0) + return retval; + return sprintf(buf, "%d\n", !!(val & sda->index)); +} + +static ssize_t store_temp(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sensor_device_attribute *sda = to_sensor_dev_attr(attr); + struct thermal_data *data = dev_get_drvdata(dev); + unsigned long val; + int retval; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + retval = regmap_write(data->regmap, sda->index, + DIV_ROUND_CLOSEST(val, 1000)); + if (retval < 0) + return retval; + return count; +} + +static ssize_t store_bit(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sda = to_sensor_dev_attr_2(attr); + struct thermal_data *data = dev_get_drvdata(dev); + unsigned long val; + int retval; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + retval = regmap_update_bits(data->regmap, sda->nr, sda->index, + val ? sda->index : 0); + if (retval < 0) + return retval; + return count; +} + +static ssize_t show_hyst_common(struct device *dev, + struct device_attribute *attr, char *buf, + bool is_min) +{ + struct sensor_device_attribute *sda = to_sensor_dev_attr(attr); + struct thermal_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + unsigned int limit; + unsigned int hyst; + int retval; + + retval = regmap_read(regmap, sda->index, &limit); + if (retval < 0) + return retval; + + retval = regmap_read(regmap, 0x21, &hyst); + if (retval < 0) + return retval; + + return sprintf(buf, "%d000\n", is_min ? limit + hyst : limit - hyst); +} + +static ssize_t show_hyst(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return show_hyst_common(dev, attr, buf, false); +} + +static ssize_t show_min_hyst(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return show_hyst_common(dev, attr, buf, true); +} + +static ssize_t store_hyst(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sensor_device_attribute *sda = to_sensor_dev_attr(attr); + struct thermal_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + unsigned int limit; + int retval; + int hyst; + unsigned long val; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + mutex_lock(&data->mutex); + retval = regmap_read(regmap, sda->index, &limit); + if (retval < 0) + goto fail; + + hyst = limit * 1000 - val; + hyst = clamp_val(DIV_ROUND_CLOSEST(hyst, 1000), 0, 255); + retval = regmap_write(regmap, 0x21, hyst); + if (retval == 0) + retval = count; +fail: + mutex_unlock(&data->mutex); + return retval; +} + +/* + * Sensors. We pass the actual i2c register to the methods. + */ + +static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x06); +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x05); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x20); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0x00); +static SENSOR_DEVICE_ATTR_2(temp1_min_alarm, S_IRUGO, + show_bit, NULL, 0x36, 0x01); +static SENSOR_DEVICE_ATTR_2(temp1_max_alarm, S_IRUGO, + show_bit, NULL, 0x35, 0x01); +static SENSOR_DEVICE_ATTR_2(temp1_crit_alarm, S_IRUGO, + show_bit, NULL, 0x37, 0x01); +static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO, show_min_hyst, NULL, 0x06); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_hyst, NULL, 0x05); +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO | S_IWUSR, + show_hyst, store_hyst, 0x20); + +static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x08); +static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x07); +static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x19); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 0x01); +static SENSOR_DEVICE_ATTR_2(temp2_fault, S_IRUGO, show_bit, NULL, 0x1b, 0x02); +static SENSOR_DEVICE_ATTR_2(temp2_min_alarm, S_IRUGO, + show_bit, NULL, 0x36, 0x02); +static SENSOR_DEVICE_ATTR_2(temp2_max_alarm, S_IRUGO, + show_bit, NULL, 0x35, 0x02); +static SENSOR_DEVICE_ATTR_2(temp2_crit_alarm, S_IRUGO, + show_bit, NULL, 0x37, 0x02); +static SENSOR_DEVICE_ATTR(temp2_min_hyst, S_IRUGO, show_min_hyst, NULL, 0x08); +static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IRUGO, show_hyst, NULL, 0x07); +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_hyst, NULL, 0x19); + +static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x16); +static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x15); +static SENSOR_DEVICE_ATTR(temp3_crit, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x1A); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 0x23); +static SENSOR_DEVICE_ATTR_2(temp3_fault, S_IRUGO, show_bit, NULL, 0x1b, 0x04); +static SENSOR_DEVICE_ATTR_2(temp3_min_alarm, S_IRUGO, + show_bit, NULL, 0x36, 0x04); +static SENSOR_DEVICE_ATTR_2(temp3_max_alarm, S_IRUGO, + show_bit, NULL, 0x35, 0x04); +static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO, + show_bit, NULL, 0x37, 0x04); +static SENSOR_DEVICE_ATTR(temp3_min_hyst, S_IRUGO, show_min_hyst, NULL, 0x16); +static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IRUGO, show_hyst, NULL, 0x15); +static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_hyst, NULL, 0x1A); + +static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x2D); +static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x2C); +static SENSOR_DEVICE_ATTR(temp4_crit, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x30); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 0x2A); +static SENSOR_DEVICE_ATTR_2(temp4_fault, S_IRUGO, show_bit, NULL, 0x1b, 0x08); +static SENSOR_DEVICE_ATTR_2(temp4_min_alarm, S_IRUGO, + show_bit, NULL, 0x36, 0x08); +static SENSOR_DEVICE_ATTR_2(temp4_max_alarm, S_IRUGO, + show_bit, NULL, 0x35, 0x08); +static SENSOR_DEVICE_ATTR_2(temp4_crit_alarm, S_IRUGO, + show_bit, NULL, 0x37, 0x08); +static SENSOR_DEVICE_ATTR(temp4_min_hyst, S_IRUGO, show_min_hyst, NULL, 0x2D); +static SENSOR_DEVICE_ATTR(temp4_max_hyst, S_IRUGO, show_hyst, NULL, 0x2C); +static SENSOR_DEVICE_ATTR(temp4_crit_hyst, S_IRUGO, show_hyst, NULL, 0x30); + +static SENSOR_DEVICE_ATTR_2(power_state, S_IRUGO | S_IWUSR, + show_bit, store_bit, 0x03, 0x40); + +static struct attribute *emc1402_attrs[] = { + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_min_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, + + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_min_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, + + &sensor_dev_attr_power_state.dev_attr.attr, + NULL +}; + +static const struct attribute_group emc1402_group = { + .attrs = emc1402_attrs, +}; + +static struct attribute *emc1403_attrs[] = { + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.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_input.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_min_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr, + NULL +}; + +static const struct attribute_group emc1403_group = { + .attrs = emc1403_attrs, +}; + +static struct attribute *emc1404_attrs[] = { + &sensor_dev_attr_temp4_min.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp4_crit.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp4_fault.dev_attr.attr, + &sensor_dev_attr_temp4_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_min_hyst.dev_attr.attr, + &sensor_dev_attr_temp4_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp4_crit_hyst.dev_attr.attr, + NULL +}; + +static const struct attribute_group emc1404_group = { + .attrs = emc1404_attrs, +}; + +/* + * EMC14x2 uses a different register and different bits to report alarm and + * fault status. For simplicity, provide a separate attribute group for this + * chip series. + * Since we can not re-use the same attribute names, create a separate attribute + * array. + */ +static struct sensor_device_attribute_2 emc1402_alarms[] = { + SENSOR_ATTR_2(temp1_min_alarm, S_IRUGO, show_bit, NULL, 0x02, 0x20), + SENSOR_ATTR_2(temp1_max_alarm, S_IRUGO, show_bit, NULL, 0x02, 0x40), + SENSOR_ATTR_2(temp1_crit_alarm, S_IRUGO, show_bit, NULL, 0x02, 0x01), + + SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_bit, NULL, 0x02, 0x04), + SENSOR_ATTR_2(temp2_min_alarm, S_IRUGO, show_bit, NULL, 0x02, 0x08), + SENSOR_ATTR_2(temp2_max_alarm, S_IRUGO, show_bit, NULL, 0x02, 0x10), + SENSOR_ATTR_2(temp2_crit_alarm, S_IRUGO, show_bit, NULL, 0x02, 0x02), +}; + +static struct attribute *emc1402_alarm_attrs[] = { + &emc1402_alarms[0].dev_attr.attr, + &emc1402_alarms[1].dev_attr.attr, + &emc1402_alarms[2].dev_attr.attr, + &emc1402_alarms[3].dev_attr.attr, + &emc1402_alarms[4].dev_attr.attr, + &emc1402_alarms[5].dev_attr.attr, + &emc1402_alarms[6].dev_attr.attr, + NULL, +}; + +static const struct attribute_group emc1402_alarm_group = { + .attrs = emc1402_alarm_attrs, +}; + +static int emc1403_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + int id; + /* Check if thermal chip is SMSC and EMC1403 or EMC1423 */ + + id = i2c_smbus_read_byte_data(client, THERMAL_SMSC_ID_REG); + if (id != 0x5d) + return -ENODEV; + + id = i2c_smbus_read_byte_data(client, THERMAL_PID_REG); + switch (id) { + case 0x20: + strlcpy(info->type, "emc1402", I2C_NAME_SIZE); + break; + case 0x21: + strlcpy(info->type, "emc1403", I2C_NAME_SIZE); + break; + case 0x22: + strlcpy(info->type, "emc1422", I2C_NAME_SIZE); + break; + case 0x23: + strlcpy(info->type, "emc1423", I2C_NAME_SIZE); + break; + case 0x25: + strlcpy(info->type, "emc1404", I2C_NAME_SIZE); + break; + case 0x27: + strlcpy(info->type, "emc1424", I2C_NAME_SIZE); + break; + default: + return -ENODEV; + } + + id = i2c_smbus_read_byte_data(client, THERMAL_REVISION_REG); + if (id < 0x01 || id > 0x04) + return -ENODEV; + + return 0; +} + +static bool emc1403_regmap_is_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00: /* internal diode high byte */ + case 0x01: /* external diode 1 high byte */ + case 0x02: /* status */ + case 0x10: /* external diode 1 low byte */ + case 0x1b: /* external diode fault */ + case 0x23: /* external diode 2 high byte */ + case 0x24: /* external diode 2 low byte */ + case 0x29: /* internal diode low byte */ + case 0x2a: /* externl diode 3 high byte */ + case 0x2b: /* external diode 3 low byte */ + case 0x35: /* high limit status */ + case 0x36: /* low limit status */ + case 0x37: /* therm limit status */ + return true; + default: + return false; + } +} + +static struct regmap_config emc1403_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = emc1403_regmap_is_volatile, +}; + +static int emc1403_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct thermal_data *data; + struct device *hwmon_dev; + + data = devm_kzalloc(&client->dev, sizeof(struct thermal_data), + GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + data->regmap = devm_regmap_init_i2c(client, &emc1403_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + + mutex_init(&data->mutex); + + switch (id->driver_data) { + case emc1404: + data->groups[2] = &emc1404_group; + case emc1403: + data->groups[1] = &emc1403_group; + case emc1402: + data->groups[0] = &emc1402_group; + } + + if (id->driver_data == emc1402) + data->groups[1] = &emc1402_alarm_group; + + hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, + client->name, data, + data->groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + dev_info(&client->dev, "%s Thermal chip found\n", id->name); + return 0; +} + +static const unsigned short emc1403_address_list[] = { + 0x18, 0x1c, 0x29, 0x4c, 0x4d, 0x5c, I2C_CLIENT_END +}; + +/* Last digit of chip name indicates number of channels */ +static const struct i2c_device_id emc1403_idtable[] = { + { "emc1402", emc1402 }, + { "emc1403", emc1403 }, + { "emc1404", emc1404 }, + { "emc1412", emc1402 }, + { "emc1413", emc1403 }, + { "emc1414", emc1404 }, + { "emc1422", emc1402 }, + { "emc1423", emc1403 }, + { "emc1424", emc1404 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, emc1403_idtable); + +static struct i2c_driver sensor_emc1403 = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "emc1403", + }, + .detect = emc1403_detect, + .probe = emc1403_probe, + .id_table = emc1403_idtable, + .address_list = emc1403_address_list, +}; + +module_i2c_driver(sensor_emc1403); + +MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com"); +MODULE_DESCRIPTION("emc1403 Thermal Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/emc2103.c b/drivers/hwmon/emc2103.c new file mode 100644 index 00000000000..78002de46cb --- /dev/null +++ b/drivers/hwmon/emc2103.c @@ -0,0 +1,730 @@ +/* + * emc2103.c - Support for SMSC EMC2103 + * Copyright (c) 2010 SMSC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> + +/* Addresses scanned */ +static const unsigned short normal_i2c[] = { 0x2E, I2C_CLIENT_END }; + +static const u8 REG_TEMP[4] = { 0x00, 0x02, 0x04, 0x06 }; +static const u8 REG_TEMP_MIN[4] = { 0x3c, 0x38, 0x39, 0x3a }; +static const u8 REG_TEMP_MAX[4] = { 0x34, 0x30, 0x31, 0x32 }; + +#define REG_CONF1 0x20 +#define REG_TEMP_MAX_ALARM 0x24 +#define REG_TEMP_MIN_ALARM 0x25 +#define REG_FAN_CONF1 0x42 +#define REG_FAN_TARGET_LO 0x4c +#define REG_FAN_TARGET_HI 0x4d +#define REG_FAN_TACH_HI 0x4e +#define REG_FAN_TACH_LO 0x4f +#define REG_PRODUCT_ID 0xfd +#define REG_MFG_ID 0xfe + +/* equation 4 from datasheet: rpm = (3932160 * multipler) / count */ +#define FAN_RPM_FACTOR 3932160 + +/* + * 2103-2 and 2103-4's 3rd temperature sensor can be connected to two diodes + * in anti-parallel mode, and in this configuration both can be read + * independently (so we have 4 temperature inputs). The device can't + * detect if it's connected in this mode, so we have to manually enable + * it. Default is to leave the device in the state it's already in (-1). + * This parameter allows APD mode to be optionally forced on or off + */ +static int apd = -1; +module_param(apd, bint, 0); +MODULE_PARM_DESC(init, "Set to zero to disable anti-parallel diode mode"); + +struct temperature { + s8 degrees; + u8 fraction; /* 0-7 multiples of 0.125 */ +}; + +struct emc2103_data { + struct device *hwmon_dev; + struct mutex update_lock; + bool valid; /* registers are valid */ + bool fan_rpm_control; + int temp_count; /* num of temp sensors */ + unsigned long last_updated; /* in jiffies */ + struct temperature temp[4]; /* internal + 3 external */ + s8 temp_min[4]; /* no fractional part */ + s8 temp_max[4]; /* no fractional part */ + u8 temp_min_alarm; + u8 temp_max_alarm; + u8 fan_multiplier; + u16 fan_tach; + u16 fan_target; +}; + +static int read_u8_from_i2c(struct i2c_client *client, u8 i2c_reg, u8 *output) +{ + int status = i2c_smbus_read_byte_data(client, i2c_reg); + if (status < 0) { + dev_warn(&client->dev, "reg 0x%02x, err %d\n", + i2c_reg, status); + } else { + *output = status; + } + return status; +} + +static void read_temp_from_i2c(struct i2c_client *client, u8 i2c_reg, + struct temperature *temp) +{ + u8 degrees, fractional; + + if (read_u8_from_i2c(client, i2c_reg, °rees) < 0) + return; + + if (read_u8_from_i2c(client, i2c_reg + 1, &fractional) < 0) + return; + + temp->degrees = degrees; + temp->fraction = (fractional & 0xe0) >> 5; +} + +static void read_fan_from_i2c(struct i2c_client *client, u16 *output, + u8 hi_addr, u8 lo_addr) +{ + u8 high_byte, lo_byte; + + if (read_u8_from_i2c(client, hi_addr, &high_byte) < 0) + return; + + if (read_u8_from_i2c(client, lo_addr, &lo_byte) < 0) + return; + + *output = ((u16)high_byte << 5) | (lo_byte >> 3); +} + +static void write_fan_target_to_i2c(struct i2c_client *client, u16 new_target) +{ + u8 high_byte = (new_target & 0x1fe0) >> 5; + u8 low_byte = (new_target & 0x001f) << 3; + i2c_smbus_write_byte_data(client, REG_FAN_TARGET_LO, low_byte); + i2c_smbus_write_byte_data(client, REG_FAN_TARGET_HI, high_byte); +} + +static void read_fan_config_from_i2c(struct i2c_client *client) + +{ + struct emc2103_data *data = i2c_get_clientdata(client); + u8 conf1; + + if (read_u8_from_i2c(client, REG_FAN_CONF1, &conf1) < 0) + return; + + data->fan_multiplier = 1 << ((conf1 & 0x60) >> 5); + data->fan_rpm_control = (conf1 & 0x80) != 0; +} + +static struct emc2103_data *emc2103_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct emc2103_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + int i; + + for (i = 0; i < data->temp_count; i++) { + read_temp_from_i2c(client, REG_TEMP[i], &data->temp[i]); + read_u8_from_i2c(client, REG_TEMP_MIN[i], + &data->temp_min[i]); + read_u8_from_i2c(client, REG_TEMP_MAX[i], + &data->temp_max[i]); + } + + read_u8_from_i2c(client, REG_TEMP_MIN_ALARM, + &data->temp_min_alarm); + read_u8_from_i2c(client, REG_TEMP_MAX_ALARM, + &data->temp_max_alarm); + + read_fan_from_i2c(client, &data->fan_tach, + REG_FAN_TACH_HI, REG_FAN_TACH_LO); + read_fan_from_i2c(client, &data->fan_target, + REG_FAN_TARGET_HI, REG_FAN_TARGET_LO); + read_fan_config_from_i2c(client); + + data->last_updated = jiffies; + data->valid = true; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static ssize_t +show_temp(struct device *dev, struct device_attribute *da, char *buf) +{ + int nr = to_sensor_dev_attr(da)->index; + struct emc2103_data *data = emc2103_update_device(dev); + int millidegrees = data->temp[nr].degrees * 1000 + + data->temp[nr].fraction * 125; + return sprintf(buf, "%d\n", millidegrees); +} + +static ssize_t +show_temp_min(struct device *dev, struct device_attribute *da, char *buf) +{ + int nr = to_sensor_dev_attr(da)->index; + struct emc2103_data *data = emc2103_update_device(dev); + int millidegrees = data->temp_min[nr] * 1000; + return sprintf(buf, "%d\n", millidegrees); +} + +static ssize_t +show_temp_max(struct device *dev, struct device_attribute *da, char *buf) +{ + int nr = to_sensor_dev_attr(da)->index; + struct emc2103_data *data = emc2103_update_device(dev); + int millidegrees = data->temp_max[nr] * 1000; + return sprintf(buf, "%d\n", millidegrees); +} + +static ssize_t +show_temp_fault(struct device *dev, struct device_attribute *da, char *buf) +{ + int nr = to_sensor_dev_attr(da)->index; + struct emc2103_data *data = emc2103_update_device(dev); + bool fault = (data->temp[nr].degrees == -128); + return sprintf(buf, "%d\n", fault ? 1 : 0); +} + +static ssize_t +show_temp_min_alarm(struct device *dev, struct device_attribute *da, char *buf) +{ + int nr = to_sensor_dev_attr(da)->index; + struct emc2103_data *data = emc2103_update_device(dev); + bool alarm = data->temp_min_alarm & (1 << nr); + return sprintf(buf, "%d\n", alarm ? 1 : 0); +} + +static ssize_t +show_temp_max_alarm(struct device *dev, struct device_attribute *da, char *buf) +{ + int nr = to_sensor_dev_attr(da)->index; + struct emc2103_data *data = emc2103_update_device(dev); + bool alarm = data->temp_max_alarm & (1 << nr); + return sprintf(buf, "%d\n", alarm ? 1 : 0); +} + +static ssize_t set_temp_min(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int nr = to_sensor_dev_attr(da)->index; + struct i2c_client *client = to_i2c_client(dev); + struct emc2103_data *data = i2c_get_clientdata(client); + long val; + + int result = kstrtol(buf, 10, &val); + if (result < 0) + return result; + + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -63, 127); + + mutex_lock(&data->update_lock); + data->temp_min[nr] = val; + i2c_smbus_write_byte_data(client, REG_TEMP_MIN[nr], val); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t set_temp_max(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int nr = to_sensor_dev_attr(da)->index; + struct i2c_client *client = to_i2c_client(dev); + struct emc2103_data *data = i2c_get_clientdata(client); + long val; + + int result = kstrtol(buf, 10, &val); + if (result < 0) + return result; + + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -63, 127); + + mutex_lock(&data->update_lock); + data->temp_max[nr] = val; + i2c_smbus_write_byte_data(client, REG_TEMP_MAX[nr], val); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t +show_fan(struct device *dev, struct device_attribute *da, char *buf) +{ + struct emc2103_data *data = emc2103_update_device(dev); + int rpm = 0; + if (data->fan_tach != 0) + rpm = (FAN_RPM_FACTOR * data->fan_multiplier) / data->fan_tach; + return sprintf(buf, "%d\n", rpm); +} + +static ssize_t +show_fan_div(struct device *dev, struct device_attribute *da, char *buf) +{ + struct emc2103_data *data = emc2103_update_device(dev); + int fan_div = 8 / data->fan_multiplier; + return sprintf(buf, "%d\n", fan_div); +} + +/* + * Note: we also update the fan target here, because its value is + * determined in part by the fan clock divider. This follows the principle + * of least surprise; the user doesn't expect the fan target to change just + * because the divider changed. + */ +static ssize_t set_fan_div(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct emc2103_data *data = emc2103_update_device(dev); + struct i2c_client *client = to_i2c_client(dev); + int new_range_bits, old_div = 8 / data->fan_multiplier; + long new_div; + + int status = kstrtol(buf, 10, &new_div); + if (status < 0) + return status; + + if (new_div == old_div) /* No change */ + return count; + + switch (new_div) { + case 1: + new_range_bits = 3; + break; + case 2: + new_range_bits = 2; + break; + case 4: + new_range_bits = 1; + break; + case 8: + new_range_bits = 0; + break; + default: + return -EINVAL; + } + + mutex_lock(&data->update_lock); + + status = i2c_smbus_read_byte_data(client, REG_FAN_CONF1); + if (status < 0) { + dev_dbg(&client->dev, "reg 0x%02x, err %d\n", + REG_FAN_CONF1, status); + mutex_unlock(&data->update_lock); + return status; + } + status &= 0x9F; + status |= (new_range_bits << 5); + i2c_smbus_write_byte_data(client, REG_FAN_CONF1, status); + + data->fan_multiplier = 8 / new_div; + + /* update fan target if high byte is not disabled */ + if ((data->fan_target & 0x1fe0) != 0x1fe0) { + u16 new_target = (data->fan_target * old_div) / new_div; + data->fan_target = min(new_target, (u16)0x1fff); + write_fan_target_to_i2c(client, data->fan_target); + } + + /* invalidate data to force re-read from hardware */ + data->valid = false; + + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_fan_target(struct device *dev, struct device_attribute *da, char *buf) +{ + struct emc2103_data *data = emc2103_update_device(dev); + int rpm = 0; + + /* high byte of 0xff indicates disabled so return 0 */ + if ((data->fan_target != 0) && ((data->fan_target & 0x1fe0) != 0x1fe0)) + rpm = (FAN_RPM_FACTOR * data->fan_multiplier) + / data->fan_target; + + return sprintf(buf, "%d\n", rpm); +} + +static ssize_t set_fan_target(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct emc2103_data *data = emc2103_update_device(dev); + struct i2c_client *client = to_i2c_client(dev); + unsigned long rpm_target; + + int result = kstrtoul(buf, 10, &rpm_target); + if (result < 0) + return result; + + /* Datasheet states 16384 as maximum RPM target (table 3.2) */ + rpm_target = clamp_val(rpm_target, 0, 16384); + + mutex_lock(&data->update_lock); + + if (rpm_target == 0) + data->fan_target = 0x1fff; + else + data->fan_target = clamp_val( + (FAN_RPM_FACTOR * data->fan_multiplier) / rpm_target, + 0, 0x1fff); + + write_fan_target_to_i2c(client, data->fan_target); + + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_fan_fault(struct device *dev, struct device_attribute *da, char *buf) +{ + struct emc2103_data *data = emc2103_update_device(dev); + bool fault = ((data->fan_tach & 0x1fe0) == 0x1fe0); + return sprintf(buf, "%d\n", fault ? 1 : 0); +} + +static ssize_t +show_pwm_enable(struct device *dev, struct device_attribute *da, char *buf) +{ + struct emc2103_data *data = emc2103_update_device(dev); + return sprintf(buf, "%d\n", data->fan_rpm_control ? 3 : 0); +} + +static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct emc2103_data *data = i2c_get_clientdata(client); + long new_value; + u8 conf_reg; + + int result = kstrtol(buf, 10, &new_value); + if (result < 0) + return result; + + mutex_lock(&data->update_lock); + switch (new_value) { + case 0: + data->fan_rpm_control = false; + break; + case 3: + data->fan_rpm_control = true; + break; + default: + count = -EINVAL; + goto err; + } + + result = read_u8_from_i2c(client, REG_FAN_CONF1, &conf_reg); + if (result) { + count = result; + goto err; + } + + if (data->fan_rpm_control) + conf_reg |= 0x80; + else + conf_reg &= ~0x80; + + i2c_smbus_write_byte_data(client, REG_FAN_CONF1, conf_reg); +err: + mutex_unlock(&data->update_lock); + return count; +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR, show_temp_min, + set_temp_min, 0); +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, show_temp_max, + set_temp_max, 0); +static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_temp_min_alarm, + NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_temp_max_alarm, + NULL, 0); + +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO | S_IWUSR, show_temp_min, + set_temp_min, 1); +static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR, show_temp_max, + set_temp_max, 1); +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_temp_min_alarm, + NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_temp_max_alarm, + NULL, 1); + +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO | S_IWUSR, show_temp_min, + set_temp_min, 2); +static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO | S_IWUSR, show_temp_max, + set_temp_max, 2); +static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2); +static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_temp_min_alarm, + NULL, 2); +static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_temp_max_alarm, + NULL, 2); + +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO | S_IWUSR, show_temp_min, + set_temp_min, 3); +static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO | S_IWUSR, show_temp_max, + set_temp_max, 3); +static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_temp_fault, NULL, 3); +static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_temp_min_alarm, + NULL, 3); +static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_temp_max_alarm, + NULL, 3); + +static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL); +static DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, show_fan_div, set_fan_div); +static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, show_fan_target, + set_fan_target); +static DEVICE_ATTR(fan1_fault, S_IRUGO, show_fan_fault, NULL); + +static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable, + set_pwm_enable); + +/* sensors present on all models */ +static struct attribute *emc2103_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_fault.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &dev_attr_fan1_input.attr, + &dev_attr_fan1_div.attr, + &dev_attr_fan1_target.attr, + &dev_attr_fan1_fault.attr, + &dev_attr_pwm1_enable.attr, + NULL +}; + +/* extra temperature sensors only present on 2103-2 and 2103-4 */ +static struct attribute *emc2103_attributes_temp3[] = { + &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_fault.dev_attr.attr, + &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, + NULL +}; + +/* extra temperature sensors only present on 2103-2 and 2103-4 in APD mode */ +static struct attribute *emc2103_attributes_temp4[] = { + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp4_min.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp4_fault.dev_attr.attr, + &sensor_dev_attr_temp4_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group emc2103_group = { + .attrs = emc2103_attributes, +}; + +static const struct attribute_group emc2103_temp3_group = { + .attrs = emc2103_attributes_temp3, +}; + +static const struct attribute_group emc2103_temp4_group = { + .attrs = emc2103_attributes_temp4, +}; + +static int +emc2103_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct emc2103_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + data = devm_kzalloc(&client->dev, sizeof(struct emc2103_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* 2103-2 and 2103-4 have 3 external diodes, 2103-1 has 1 */ + status = i2c_smbus_read_byte_data(client, REG_PRODUCT_ID); + if (status == 0x24) { + /* 2103-1 only has 1 external diode */ + data->temp_count = 2; + } else { + /* 2103-2 and 2103-4 have 3 or 4 external diodes */ + status = i2c_smbus_read_byte_data(client, REG_CONF1); + if (status < 0) { + dev_dbg(&client->dev, "reg 0x%02x, err %d\n", REG_CONF1, + status); + return status; + } + + /* detect current state of hardware */ + data->temp_count = (status & 0x01) ? 4 : 3; + + /* force APD state if module parameter is set */ + if (apd == 0) { + /* force APD mode off */ + data->temp_count = 3; + status &= ~(0x01); + i2c_smbus_write_byte_data(client, REG_CONF1, status); + } else if (apd == 1) { + /* force APD mode on */ + data->temp_count = 4; + status |= 0x01; + i2c_smbus_write_byte_data(client, REG_CONF1, status); + } + } + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &emc2103_group); + if (status) + return status; + + if (data->temp_count >= 3) { + status = sysfs_create_group(&client->dev.kobj, + &emc2103_temp3_group); + if (status) + goto exit_remove; + } + + if (data->temp_count == 4) { + status = sysfs_create_group(&client->dev.kobj, + &emc2103_temp4_group); + if (status) + goto exit_remove_temp3; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_remove_temp4; + } + + dev_info(&client->dev, "%s: sensor '%s'\n", + dev_name(data->hwmon_dev), client->name); + + return 0; + +exit_remove_temp4: + if (data->temp_count == 4) + sysfs_remove_group(&client->dev.kobj, &emc2103_temp4_group); +exit_remove_temp3: + if (data->temp_count >= 3) + sysfs_remove_group(&client->dev.kobj, &emc2103_temp3_group); +exit_remove: + sysfs_remove_group(&client->dev.kobj, &emc2103_group); + return status; +} + +static int emc2103_remove(struct i2c_client *client) +{ + struct emc2103_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + + if (data->temp_count == 4) + sysfs_remove_group(&client->dev.kobj, &emc2103_temp4_group); + + if (data->temp_count >= 3) + sysfs_remove_group(&client->dev.kobj, &emc2103_temp3_group); + + sysfs_remove_group(&client->dev.kobj, &emc2103_group); + + return 0; +} + +static const struct i2c_device_id emc2103_ids[] = { + { "emc2103", 0, }, + { /* LIST END */ } +}; +MODULE_DEVICE_TABLE(i2c, emc2103_ids); + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int +emc2103_detect(struct i2c_client *new_client, struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = new_client->adapter; + int manufacturer, product; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + manufacturer = i2c_smbus_read_byte_data(new_client, REG_MFG_ID); + if (manufacturer != 0x5D) + return -ENODEV; + + product = i2c_smbus_read_byte_data(new_client, REG_PRODUCT_ID); + if ((product != 0x24) && (product != 0x26)) + return -ENODEV; + + strlcpy(info->type, "emc2103", I2C_NAME_SIZE); + + return 0; +} + +static struct i2c_driver emc2103_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "emc2103", + }, + .probe = emc2103_probe, + .remove = emc2103_remove, + .id_table = emc2103_ids, + .detect = emc2103_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(emc2103_driver); + +MODULE_AUTHOR("Steve Glendinning <steve.glendinning@shawell.net>"); +MODULE_DESCRIPTION("SMSC EMC2103 hwmon driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/emc6w201.c b/drivers/hwmon/emc6w201.c new file mode 100644 index 00000000000..f76a74cb6dc --- /dev/null +++ b/drivers/hwmon/emc6w201.c @@ -0,0 +1,553 @@ +/* + * emc6w201.c - Hardware monitoring driver for the SMSC EMC6W201 + * Copyright (C) 2011 Jean Delvare <jdelvare@suse.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> + +/* + * Addresses to scan + */ + +static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; + +/* + * The EMC6W201 registers + */ + +#define EMC6W201_REG_IN(nr) (0x20 + (nr)) +#define EMC6W201_REG_TEMP(nr) (0x26 + (nr)) +#define EMC6W201_REG_FAN(nr) (0x2C + (nr) * 2) +#define EMC6W201_REG_COMPANY 0x3E +#define EMC6W201_REG_VERSTEP 0x3F +#define EMC6W201_REG_CONFIG 0x40 +#define EMC6W201_REG_IN_LOW(nr) (0x4A + (nr) * 2) +#define EMC6W201_REG_IN_HIGH(nr) (0x4B + (nr) * 2) +#define EMC6W201_REG_TEMP_LOW(nr) (0x56 + (nr) * 2) +#define EMC6W201_REG_TEMP_HIGH(nr) (0x57 + (nr) * 2) +#define EMC6W201_REG_FAN_MIN(nr) (0x62 + (nr) * 2) + +enum subfeature { input, min, max }; + +/* + * Per-device data + */ + +struct emc6w201_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* registers values */ + u8 in[3][6]; + s8 temp[3][6]; + u16 fan[2][5]; +}; + +/* + * Combine LSB and MSB registers in a single value + * Locking: must be called with data->update_lock held + */ +static u16 emc6w201_read16(struct i2c_client *client, u8 reg) +{ + int lsb, msb; + + lsb = i2c_smbus_read_byte_data(client, reg); + msb = i2c_smbus_read_byte_data(client, reg + 1); + if (unlikely(lsb < 0 || msb < 0)) { + dev_err(&client->dev, "%d-bit %s failed at 0x%02x\n", + 16, "read", reg); + return 0xFFFF; /* Arbitrary value */ + } + + return (msb << 8) | lsb; +} + +/* + * Write 16-bit value to LSB and MSB registers + * Locking: must be called with data->update_lock held + */ +static int emc6w201_write16(struct i2c_client *client, u8 reg, u16 val) +{ + int err; + + err = i2c_smbus_write_byte_data(client, reg, val & 0xff); + if (likely(!err)) + err = i2c_smbus_write_byte_data(client, reg + 1, val >> 8); + if (unlikely(err < 0)) + dev_err(&client->dev, "%d-bit %s failed at 0x%02x\n", + 16, "write", reg); + + return err; +} + +/* Read 8-bit value from register */ +static u8 emc6w201_read8(struct i2c_client *client, u8 reg) +{ + int val; + + val = i2c_smbus_read_byte_data(client, reg); + if (unlikely(val < 0)) { + dev_err(&client->dev, "%d-bit %s failed at 0x%02x\n", + 8, "read", reg); + return 0x00; /* Arbitrary value */ + } + + return val; +} + +/* Write 8-bit value to register */ +static int emc6w201_write8(struct i2c_client *client, u8 reg, u8 val) +{ + int err; + + err = i2c_smbus_write_byte_data(client, reg, val); + if (unlikely(err < 0)) + dev_err(&client->dev, "%d-bit %s failed at 0x%02x\n", + 8, "write", reg); + + return err; +} + +static struct emc6w201_data *emc6w201_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct emc6w201_data *data = i2c_get_clientdata(client); + int nr; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + for (nr = 0; nr < 6; nr++) { + data->in[input][nr] = + emc6w201_read8(client, + EMC6W201_REG_IN(nr)); + data->in[min][nr] = + emc6w201_read8(client, + EMC6W201_REG_IN_LOW(nr)); + data->in[max][nr] = + emc6w201_read8(client, + EMC6W201_REG_IN_HIGH(nr)); + } + + for (nr = 0; nr < 6; nr++) { + data->temp[input][nr] = + emc6w201_read8(client, + EMC6W201_REG_TEMP(nr)); + data->temp[min][nr] = + emc6w201_read8(client, + EMC6W201_REG_TEMP_LOW(nr)); + data->temp[max][nr] = + emc6w201_read8(client, + EMC6W201_REG_TEMP_HIGH(nr)); + } + + for (nr = 0; nr < 5; nr++) { + data->fan[input][nr] = + emc6w201_read16(client, + EMC6W201_REG_FAN(nr)); + data->fan[min][nr] = + emc6w201_read16(client, + EMC6W201_REG_FAN_MIN(nr)); + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +/* + * Sysfs callback functions + */ + +static const s16 nominal_mv[6] = { 2500, 1500, 3300, 5000, 1500, 1500 }; + +static ssize_t show_in(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct emc6w201_data *data = emc6w201_update_device(dev); + int sf = to_sensor_dev_attr_2(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->nr; + + return sprintf(buf, "%u\n", + (unsigned)data->in[sf][nr] * nominal_mv[nr] / 0xC0); +} + +static ssize_t set_in(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct emc6w201_data *data = i2c_get_clientdata(client); + int sf = to_sensor_dev_attr_2(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->nr; + int err; + long val; + u8 reg; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + val = DIV_ROUND_CLOSEST(val * 0xC0, nominal_mv[nr]); + reg = (sf == min) ? EMC6W201_REG_IN_LOW(nr) + : EMC6W201_REG_IN_HIGH(nr); + + mutex_lock(&data->update_lock); + data->in[sf][nr] = clamp_val(val, 0, 255); + err = emc6w201_write8(client, reg, data->in[sf][nr]); + mutex_unlock(&data->update_lock); + + return err < 0 ? err : count; +} + +static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct emc6w201_data *data = emc6w201_update_device(dev); + int sf = to_sensor_dev_attr_2(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->nr; + + return sprintf(buf, "%d\n", (int)data->temp[sf][nr] * 1000); +} + +static ssize_t set_temp(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct emc6w201_data *data = i2c_get_clientdata(client); + int sf = to_sensor_dev_attr_2(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->nr; + int err; + long val; + u8 reg; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + val /= 1000; + reg = (sf == min) ? EMC6W201_REG_TEMP_LOW(nr) + : EMC6W201_REG_TEMP_HIGH(nr); + + mutex_lock(&data->update_lock); + data->temp[sf][nr] = clamp_val(val, -127, 128); + err = emc6w201_write8(client, reg, data->temp[sf][nr]); + mutex_unlock(&data->update_lock); + + return err < 0 ? err : count; +} + +static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct emc6w201_data *data = emc6w201_update_device(dev); + int sf = to_sensor_dev_attr_2(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->nr; + unsigned rpm; + + if (data->fan[sf][nr] == 0 || data->fan[sf][nr] == 0xFFFF) + rpm = 0; + else + rpm = 5400000U / data->fan[sf][nr]; + + return sprintf(buf, "%u\n", rpm); +} + +static ssize_t set_fan(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct emc6w201_data *data = i2c_get_clientdata(client); + int sf = to_sensor_dev_attr_2(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->nr; + int err; + unsigned long val; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + if (val == 0) { + val = 0xFFFF; + } else { + val = DIV_ROUND_CLOSEST(5400000U, val); + val = clamp_val(val, 0, 0xFFFE); + } + + mutex_lock(&data->update_lock); + data->fan[sf][nr] = val; + err = emc6w201_write16(client, EMC6W201_REG_FAN_MIN(nr), + data->fan[sf][nr]); + mutex_unlock(&data->update_lock); + + return err < 0 ? err : count; +} + +static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, input); +static SENSOR_DEVICE_ATTR_2(in0_min, S_IRUGO | S_IWUSR, show_in, set_in, + 0, min); +static SENSOR_DEVICE_ATTR_2(in0_max, S_IRUGO | S_IWUSR, show_in, set_in, + 0, max); +static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 1, input); +static SENSOR_DEVICE_ATTR_2(in1_min, S_IRUGO | S_IWUSR, show_in, set_in, + 1, min); +static SENSOR_DEVICE_ATTR_2(in1_max, S_IRUGO | S_IWUSR, show_in, set_in, + 1, max); +static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 2, input); +static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_in, set_in, + 2, min); +static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_in, set_in, + 2, max); +static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 3, input); +static SENSOR_DEVICE_ATTR_2(in3_min, S_IRUGO | S_IWUSR, show_in, set_in, + 3, min); +static SENSOR_DEVICE_ATTR_2(in3_max, S_IRUGO | S_IWUSR, show_in, set_in, + 3, max); +static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 4, input); +static SENSOR_DEVICE_ATTR_2(in4_min, S_IRUGO | S_IWUSR, show_in, set_in, + 4, min); +static SENSOR_DEVICE_ATTR_2(in4_max, S_IRUGO | S_IWUSR, show_in, set_in, + 4, max); +static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, show_in, NULL, 5, input); +static SENSOR_DEVICE_ATTR_2(in5_min, S_IRUGO | S_IWUSR, show_in, set_in, + 5, min); +static SENSOR_DEVICE_ATTR_2(in5_max, S_IRUGO | S_IWUSR, show_in, set_in, + 5, max); + +static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, input); +static SENSOR_DEVICE_ATTR_2(temp1_min, S_IRUGO | S_IWUSR, show_temp, set_temp, + 0, min); +static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 0, max); +static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 1, input); +static SENSOR_DEVICE_ATTR_2(temp2_min, S_IRUGO | S_IWUSR, show_temp, set_temp, + 1, min); +static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 1, max); +static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 2, input); +static SENSOR_DEVICE_ATTR_2(temp3_min, S_IRUGO | S_IWUSR, show_temp, set_temp, + 2, min); +static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 2, max); +static SENSOR_DEVICE_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 3, input); +static SENSOR_DEVICE_ATTR_2(temp4_min, S_IRUGO | S_IWUSR, show_temp, set_temp, + 3, min); +static SENSOR_DEVICE_ATTR_2(temp4_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 3, max); +static SENSOR_DEVICE_ATTR_2(temp5_input, S_IRUGO, show_temp, NULL, 4, input); +static SENSOR_DEVICE_ATTR_2(temp5_min, S_IRUGO | S_IWUSR, show_temp, set_temp, + 4, min); +static SENSOR_DEVICE_ATTR_2(temp5_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 4, max); +static SENSOR_DEVICE_ATTR_2(temp6_input, S_IRUGO, show_temp, NULL, 5, input); +static SENSOR_DEVICE_ATTR_2(temp6_min, S_IRUGO | S_IWUSR, show_temp, set_temp, + 5, min); +static SENSOR_DEVICE_ATTR_2(temp6_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 5, max); + +static SENSOR_DEVICE_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, input); +static SENSOR_DEVICE_ATTR_2(fan1_min, S_IRUGO | S_IWUSR, show_fan, set_fan, + 0, min); +static SENSOR_DEVICE_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 1, input); +static SENSOR_DEVICE_ATTR_2(fan2_min, S_IRUGO | S_IWUSR, show_fan, set_fan, + 1, min); +static SENSOR_DEVICE_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 2, input); +static SENSOR_DEVICE_ATTR_2(fan3_min, S_IRUGO | S_IWUSR, show_fan, set_fan, + 2, min); +static SENSOR_DEVICE_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 3, input); +static SENSOR_DEVICE_ATTR_2(fan4_min, S_IRUGO | S_IWUSR, show_fan, set_fan, + 3, min); +static SENSOR_DEVICE_ATTR_2(fan5_input, S_IRUGO, show_fan, NULL, 4, input); +static SENSOR_DEVICE_ATTR_2(fan5_min, S_IRUGO | S_IWUSR, show_fan, set_fan, + 4, min); + +static struct attribute *emc6w201_attributes[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, + + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp4_min.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp5_min.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + &sensor_dev_attr_temp6_input.dev_attr.attr, + &sensor_dev_attr_temp6_min.dev_attr.attr, + &sensor_dev_attr_temp6_max.dev_attr.attr, + + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan3_min.dev_attr.attr, + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan4_min.dev_attr.attr, + &sensor_dev_attr_fan5_input.dev_attr.attr, + &sensor_dev_attr_fan5_min.dev_attr.attr, + NULL +}; + +static const struct attribute_group emc6w201_group = { + .attrs = emc6w201_attributes, +}; + +/* + * Driver interface + */ + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int emc6w201_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + int company, verstep, config; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + /* Identification */ + company = i2c_smbus_read_byte_data(client, EMC6W201_REG_COMPANY); + if (company != 0x5C) + return -ENODEV; + verstep = i2c_smbus_read_byte_data(client, EMC6W201_REG_VERSTEP); + if (verstep < 0 || (verstep & 0xF0) != 0xB0) + return -ENODEV; + if ((verstep & 0x0F) > 2) { + dev_dbg(&client->dev, "Unknwown EMC6W201 stepping %d\n", + verstep & 0x0F); + return -ENODEV; + } + + /* Check configuration */ + config = i2c_smbus_read_byte_data(client, EMC6W201_REG_CONFIG); + if (config < 0 || (config & 0xF4) != 0x04) + return -ENODEV; + if (!(config & 0x01)) { + dev_err(&client->dev, "Monitoring not enabled\n"); + return -ENODEV; + } + + strlcpy(info->type, "emc6w201", I2C_NAME_SIZE); + + return 0; +} + +static int emc6w201_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct emc6w201_data *data; + int err; + + data = devm_kzalloc(&client->dev, sizeof(struct emc6w201_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* Create sysfs attribute */ + err = sysfs_create_group(&client->dev.kobj, &emc6w201_group); + if (err) + return err; + + /* Expose as a hwmon device */ + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + return 0; + + exit_remove: + sysfs_remove_group(&client->dev.kobj, &emc6w201_group); + return err; +} + +static int emc6w201_remove(struct i2c_client *client) +{ + struct emc6w201_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &emc6w201_group); + + return 0; +} + +static const struct i2c_device_id emc6w201_id[] = { + { "emc6w201", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, emc6w201_id); + +static struct i2c_driver emc6w201_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "emc6w201", + }, + .probe = emc6w201_probe, + .remove = emc6w201_remove, + .id_table = emc6w201_id, + .detect = emc6w201_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(emc6w201_driver); + +MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); +MODULE_DESCRIPTION("SMSC EMC6W201 hardware monitoring driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c index 525a00bd70b..9e57b77ecd3 100644 --- a/drivers/hwmon/f71805f.c +++ b/drivers/hwmon/f71805f.c @@ -1,7 +1,7 @@ /* * f71805f.c - driver for the Fintek F71805F/FG and F71872F/FG Super-I/O * chips integrated hardware monitoring features - * Copyright (C) 2005-2006 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2005-2006 Jean Delvare <jdelvare@suse.de> * * The F71805F/FG is a LPC Super-I/O chip made by Fintek. It integrates * complete hardware monitoring features: voltage, fan and temperature @@ -28,6 +28,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> @@ -200,7 +202,7 @@ struct f71805f_sio_data { static inline long in_from_reg(u8 reg) { - return (reg * 8); + return reg * 8; } /* The 2 least significant bits are not used */ @@ -210,13 +212,13 @@ static inline u8 in_to_reg(long val) return 0; if (val >= 2016) return 0xfc; - return (((val + 16) / 32) << 2); + return ((val + 16) / 32) << 2; } /* in0 is downscaled by a factor 2 internally */ static inline long in0_from_reg(u8 reg) { - return (reg * 16); + return reg * 16; } static inline u8 in0_to_reg(long val) @@ -225,7 +227,7 @@ static inline u8 in0_to_reg(long val) return 0; if (val >= 4032) return 0xfc; - return (((val + 32) / 64) << 2); + return ((val + 32) / 64) << 2; } /* The 4 most significant bits are not used */ @@ -234,17 +236,19 @@ static inline long fan_from_reg(u16 reg) reg &= 0xfff; if (!reg || reg == 0xfff) return 0; - return (1500000 / reg); + return 1500000 / reg; } static inline u16 fan_to_reg(long rpm) { - /* If the low limit is set below what the chip can measure, - store the largest possible 12-bit value in the registers, - so that no alarm will ever trigger. */ + /* + * If the low limit is set below what the chip can measure, + * store the largest possible 12-bit value in the registers, + * so that no alarm will ever trigger. + */ if (rpm < 367) return 0xfff; - return (1500000 / rpm); + return 1500000 / rpm; } static inline unsigned long pwm_freq_from_reg(u8 reg) @@ -276,16 +280,16 @@ static inline int pwm_mode_from_reg(u8 reg) static inline long temp_from_reg(u8 reg) { - return (reg * 1000); + return reg * 1000; } static inline u8 temp_to_reg(long val) { - if (val < 0) - val = 0; - else if (val > 1000 * 0xff) - val = 0xff; - return ((val + 500) / 1000); + if (val <= 0) + return 0; + if (val >= 1000 * 0xff) + return 0xff; + return (val + 500) / 1000; } /* @@ -306,9 +310,11 @@ static void f71805f_write8(struct f71805f_data *data, u8 reg, u8 val) outb(val, data->addr + DATA_REG_OFFSET); } -/* It is important to read the MSB first, because doing so latches the - value of the LSB, so we are sure both bytes belong to the same value. - Must be called with data->update_lock held, except during initialization */ +/* + * It is important to read the MSB first, because doing so latches the + * value of the LSB, so we are sure both bytes belong to the same value. + * Must be called with data->update_lock held, except during initialization + */ static u16 f71805f_read16(struct f71805f_data *data, u8 reg) { u16 val; @@ -453,7 +459,12 @@ static ssize_t set_in0_max(struct device *dev, struct device_attribute struct f71805f_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); int nr = attr->index; - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_high[nr] = in0_to_reg(val); @@ -469,7 +480,12 @@ static ssize_t set_in0_min(struct device *dev, struct device_attribute struct f71805f_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); int nr = attr->index; - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_low[nr] = in0_to_reg(val); @@ -515,7 +531,12 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute struct f71805f_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); int nr = attr->index; - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_high[nr] = in_to_reg(val); @@ -531,7 +552,12 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute struct f71805f_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); int nr = attr->index; - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_low[nr] = in_to_reg(val); @@ -577,7 +603,12 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute struct f71805f_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); int nr = attr->index; - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->fan_low[nr] = fan_to_reg(val); @@ -593,7 +624,12 @@ static ssize_t set_fan_target(struct device *dev, struct device_attribute struct f71805f_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); int nr = attr->index; - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->fan_target[nr] = fan_to_reg(val); @@ -662,7 +698,12 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, struct f71805f_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); int nr = attr->index; - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; if (val > 255) return -EINVAL; @@ -683,8 +724,13 @@ static ssize_t set_pwm_enable(struct device *dev, struct device_attribute struct f71805f_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); int nr = attr->index; - unsigned long val = simple_strtoul(buf, NULL, 10); u8 reg; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; if (val < 1 || val > 3) return -EINVAL; @@ -728,7 +774,12 @@ static ssize_t set_pwm_freq(struct device *dev, struct device_attribute struct f71805f_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); int nr = attr->index; - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->pwm_freq[nr] = pwm_freq_to_reg(val); @@ -740,7 +791,7 @@ static ssize_t set_pwm_freq(struct device *dev, struct device_attribute static ssize_t show_pwm_auto_point_temp(struct device *dev, struct device_attribute *devattr, - char* buf) + char *buf) { struct f71805f_data *data = dev_get_drvdata(dev); struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); @@ -753,13 +804,18 @@ static ssize_t show_pwm_auto_point_temp(struct device *dev, static ssize_t set_pwm_auto_point_temp(struct device *dev, struct device_attribute *devattr, - const char* buf, size_t count) + const char *buf, size_t count) { struct f71805f_data *data = dev_get_drvdata(dev); struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); int pwmnr = attr->nr; int apnr = attr->index; - unsigned long val = simple_strtol(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->auto_points[pwmnr].temp[apnr] = temp_to_reg(val); @@ -772,7 +828,7 @@ static ssize_t set_pwm_auto_point_temp(struct device *dev, static ssize_t show_pwm_auto_point_fan(struct device *dev, struct device_attribute *devattr, - char* buf) + char *buf) { struct f71805f_data *data = dev_get_drvdata(dev); struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); @@ -785,18 +841,23 @@ static ssize_t show_pwm_auto_point_fan(struct device *dev, static ssize_t set_pwm_auto_point_fan(struct device *dev, struct device_attribute *devattr, - const char* buf, size_t count) + const char *buf, size_t count) { struct f71805f_data *data = dev_get_drvdata(dev); struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); int pwmnr = attr->nr; int apnr = attr->index; - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->auto_points[pwmnr].fan[apnr] = fan_to_reg(val); f71805f_write16(data, F71805F_REG_PWM_AUTO_POINT_FAN(pwmnr, apnr), - data->auto_points[pwmnr].fan[apnr]); + data->auto_points[pwmnr].fan[apnr]); mutex_unlock(&data->update_lock); return count; @@ -849,7 +910,12 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute struct f71805f_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); int nr = attr->index; - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_high[nr] = temp_to_reg(val); @@ -865,7 +931,12 @@ static ssize_t set_temp_hyst(struct device *dev, struct device_attribute struct f71805f_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); int nr = attr->index; - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_hyst[nr] = temp_to_reg(val); @@ -918,9 +989,9 @@ static ssize_t show_name(struct device *dev, struct device_attribute } static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in0, NULL, 0); -static SENSOR_DEVICE_ATTR(in0_max, S_IRUGO| S_IWUSR, +static SENSOR_DEVICE_ATTR(in0_max, S_IRUGO | S_IWUSR, show_in0_max, set_in0_max, 0); -static SENSOR_DEVICE_ATTR(in0_min, S_IRUGO| S_IWUSR, +static SENSOR_DEVICE_ATTR(in0_min, S_IRUGO | S_IWUSR, show_in0_min, set_in0_min, 0); static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1); static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO | S_IWUSR, @@ -1008,8 +1079,10 @@ static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp_hyst, set_temp_hyst, 2); static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2); -/* pwm (value) files are created read-only, write permission is - then added or removed dynamically as needed */ +/* + * pwm (value) files are created read-only, write permission is + * then added or removed dynamically as needed + */ static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO, show_pwm, set_pwm, 0); static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable, 0); @@ -1244,8 +1317,10 @@ static const struct attribute_group f71805f_group_optin[4] = { { .attrs = f71805f_attributes_optin[3] }, }; -/* We don't include pwm_freq files in the arrays above, because they must be - created conditionally (only if pwm_mode is 1 == PWM) */ +/* + * We don't include pwm_freq files in the arrays above, because they must be + * created conditionally (only if pwm_mode is 1 == PWM) + */ static struct attribute *f71805f_attributes_pwm_freq[] = { &sensor_dev_attr_pwm1_freq.dev_attr.attr, &sensor_dev_attr_pwm2_freq.dev_attr.attr, @@ -1268,25 +1343,28 @@ static struct attribute *f71805f_attr_pwm[] = { * Device registration and initialization */ -static void __devinit f71805f_init_device(struct f71805f_data *data) +static void f71805f_init_device(struct f71805f_data *data) { u8 reg; int i; reg = f71805f_read8(data, F71805F_REG_START); if ((reg & 0x41) != 0x01) { - printk(KERN_DEBUG DRVNAME ": Starting monitoring " - "operations\n"); + pr_debug("Starting monitoring operations\n"); f71805f_write8(data, F71805F_REG_START, (reg | 0x01) & ~0x40); } - /* Fan monitoring can be disabled. If it is, we won't be polling - the register values, and won't create the related sysfs files. */ + /* + * Fan monitoring can be disabled. If it is, we won't be polling + * the register values, and won't create the related sysfs files. + */ for (i = 0; i < 3; i++) { data->fan_ctrl[i] = f71805f_read8(data, F71805F_REG_FAN_CTRL(i)); - /* Clear latch full bit, else "speed mode" fan speed control - doesn't work */ + /* + * Clear latch full bit, else "speed mode" fan speed control + * doesn't work + */ if (data->fan_ctrl[i] & FAN_CTRL_LATCH_FULL) { data->fan_ctrl[i] &= ~FAN_CTRL_LATCH_FULL; f71805f_write8(data, F71805F_REG_FAN_CTRL(i), @@ -1295,31 +1373,30 @@ static void __devinit f71805f_init_device(struct f71805f_data *data) } } -static int __devinit f71805f_probe(struct platform_device *pdev) +static int f71805f_probe(struct platform_device *pdev) { - struct f71805f_sio_data *sio_data = pdev->dev.platform_data; + struct f71805f_sio_data *sio_data = dev_get_platdata(&pdev->dev); struct f71805f_data *data; struct resource *res; int i, err; - static const char *names[] = { + static const char * const names[] = { "f71805f", "f71872f", }; - if (!(data = kzalloc(sizeof(struct f71805f_data), GFP_KERNEL))) { - err = -ENOMEM; - printk(KERN_ERR DRVNAME ": Out of memory\n"); - goto exit; - } + data = devm_kzalloc(&pdev->dev, sizeof(struct f71805f_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start + ADDR_REG_OFFSET, 2, DRVNAME)) { - err = -EBUSY; + if (!devm_request_region(&pdev->dev, res->start + ADDR_REG_OFFSET, 2, + DRVNAME)) { dev_err(&pdev->dev, "Failed to request region 0x%lx-0x%lx\n", (unsigned long)(res->start + ADDR_REG_OFFSET), (unsigned long)(res->start + ADDR_REG_OFFSET + 1)); - goto exit_free; + return -EBUSY; } data->addr = res->start; data->name = names[sio_data->kind]; @@ -1345,40 +1422,47 @@ static int __devinit f71805f_probe(struct platform_device *pdev) f71805f_init_device(data); /* Register sysfs interface files */ - if ((err = sysfs_create_group(&pdev->dev.kobj, &f71805f_group))) - goto exit_release_region; + err = sysfs_create_group(&pdev->dev.kobj, &f71805f_group); + if (err) + return err; if (data->has_in & (1 << 4)) { /* in4 */ - if ((err = sysfs_create_group(&pdev->dev.kobj, - &f71805f_group_optin[0]))) + err = sysfs_create_group(&pdev->dev.kobj, + &f71805f_group_optin[0]); + if (err) goto exit_remove_files; } if (data->has_in & (1 << 8)) { /* in8 */ - if ((err = sysfs_create_group(&pdev->dev.kobj, - &f71805f_group_optin[1]))) + err = sysfs_create_group(&pdev->dev.kobj, + &f71805f_group_optin[1]); + if (err) goto exit_remove_files; } if (data->has_in & (1 << 9)) { /* in9 (F71872F/FG only) */ - if ((err = sysfs_create_group(&pdev->dev.kobj, - &f71805f_group_optin[2]))) + err = sysfs_create_group(&pdev->dev.kobj, + &f71805f_group_optin[2]); + if (err) goto exit_remove_files; } if (data->has_in & (1 << 10)) { /* in9 (F71872F/FG only) */ - if ((err = sysfs_create_group(&pdev->dev.kobj, - &f71805f_group_optin[3]))) + err = sysfs_create_group(&pdev->dev.kobj, + &f71805f_group_optin[3]); + if (err) goto exit_remove_files; } for (i = 0; i < 3; i++) { /* If control mode is PWM, create pwm_freq file */ if (!(data->fan_ctrl[i] & FAN_CTRL_DC_MODE)) { - if ((err = sysfs_create_file(&pdev->dev.kobj, - f71805f_attributes_pwm_freq[i]))) + err = sysfs_create_file(&pdev->dev.kobj, + f71805f_attributes_pwm_freq[i]); + if (err) goto exit_remove_files; } /* If PWM is in manual mode, add write permission */ if (data->fan_ctrl[i] & FAN_CTRL_MODE_MANUAL) { - if ((err = sysfs_chmod_file(&pdev->dev.kobj, - f71805f_attr_pwm[i], - S_IRUGO | S_IWUSR))) { + err = sysfs_chmod_file(&pdev->dev.kobj, + f71805f_attr_pwm[i], + S_IRUGO | S_IWUSR); + if (err) { dev_err(&pdev->dev, "chmod +w pwm%d failed\n", i + 1); goto exit_remove_files; @@ -1400,19 +1484,12 @@ exit_remove_files: for (i = 0; i < 4; i++) sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_optin[i]); sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq); -exit_release_region: - release_region(res->start + ADDR_REG_OFFSET, 2); -exit_free: - platform_set_drvdata(pdev, NULL); - kfree(data); -exit: return err; } -static int __devexit f71805f_remove(struct platform_device *pdev) +static int f71805f_remove(struct platform_device *pdev) { struct f71805f_data *data = platform_get_drvdata(pdev); - struct resource *res; int i; hwmon_device_unregister(data->hwmon_dev); @@ -1420,11 +1497,6 @@ static int __devexit f71805f_remove(struct platform_device *pdev) for (i = 0; i < 4; i++) sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_optin[i]); sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq); - platform_set_drvdata(pdev, NULL); - kfree(data); - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - release_region(res->start + ADDR_REG_OFFSET, 2); return 0; } @@ -1435,7 +1507,7 @@ static struct platform_driver f71805f_driver = { .name = DRVNAME, }, .probe = f71805f_probe, - .remove = __devexit_p(f71805f_remove), + .remove = f71805f_remove, }; static int __init f71805f_device_add(unsigned short address, @@ -1451,7 +1523,7 @@ static int __init f71805f_device_add(unsigned short address, pdev = platform_device_alloc(DRVNAME, address); if (!pdev) { err = -ENOMEM; - printk(KERN_ERR DRVNAME ": Device allocation failed\n"); + pr_err("Device allocation failed\n"); goto exit; } @@ -1462,22 +1534,20 @@ static int __init f71805f_device_add(unsigned short address, err = platform_device_add_resources(pdev, &res, 1); if (err) { - printk(KERN_ERR DRVNAME ": Device resource addition failed " - "(%d)\n", err); + pr_err("Device resource addition failed (%d)\n", err); goto exit_device_put; } err = platform_device_add_data(pdev, sio_data, sizeof(struct f71805f_sio_data)); if (err) { - printk(KERN_ERR DRVNAME ": Platform data allocation failed\n"); + pr_err("Platform data allocation failed\n"); goto exit_device_put; } err = platform_device_add(pdev); if (err) { - printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", - err); + pr_err("Device addition failed (%d)\n", err); goto exit_device_put; } @@ -1495,7 +1565,7 @@ static int __init f71805f_find(int sioaddr, unsigned short *address, int err = -ENODEV; u16 devid; - static const char *names[] = { + static const char * const names[] = { "F71805F/FG", "F71872F/FG or F71806F/FG", }; @@ -1516,30 +1586,27 @@ static int __init f71805f_find(int sioaddr, unsigned short *address, sio_data->fnsel1 = superio_inb(sioaddr, SIO_REG_FNSEL1); break; default: - printk(KERN_INFO DRVNAME ": Unsupported Fintek device, " - "skipping\n"); + pr_info("Unsupported Fintek device, skipping\n"); goto exit; } superio_select(sioaddr, F71805F_LD_HWM); if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) { - printk(KERN_WARNING DRVNAME ": Device not activated, " - "skipping\n"); + pr_warn("Device not activated, skipping\n"); goto exit; } *address = superio_inw(sioaddr, SIO_REG_ADDR); if (*address == 0) { - printk(KERN_WARNING DRVNAME ": Base address not set, " - "skipping\n"); + pr_warn("Base address not set, skipping\n"); goto exit; } *address &= ~(REGION_LENGTH - 1); /* Ignore 3 LSB */ err = 0; - printk(KERN_INFO DRVNAME ": Found %s chip at %#x, revision %u\n", - names[sio_data->kind], *address, - superio_inb(sioaddr, SIO_REG_DEVREV)); + pr_info("Found %s chip at %#x, revision %u\n", + names[sio_data->kind], *address, + superio_inb(sioaddr, SIO_REG_DEVREV)); exit: superio_exit(sioaddr); @@ -1579,7 +1646,7 @@ static void __exit f71805f_exit(void) platform_driver_unregister(&f71805f_driver); } -MODULE_AUTHOR("Jean Delvare <khali@linux-fr>"); +MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("F71805F/F71872F hardware monitoring driver"); diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index a95fa4256ca..03d8592810b 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -1,6 +1,6 @@ /*************************************************************************** * Copyright (C) 2006 by Hans Edgington <hans@edgington.nl> * - * Copyright (C) 2007-2009 Hans de Goede <hdegoede@redhat.com> * + * Copyright (C) 2007-2011 Hans de Goede <hdegoede@redhat.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 * @@ -18,6 +18,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> @@ -35,7 +37,7 @@ #define SIO_F71858FG_LD_HWM 0x02 /* Hardware monitor logical device */ #define SIO_F71882FG_LD_HWM 0x04 /* Hardware monitor logical device */ #define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */ -#define SIO_LOCK_KEY 0xAA /* Key to diasble Super-I/O */ +#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ #define SIO_REG_LDSEL 0x07 /* Logical device select */ #define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ @@ -45,22 +47,27 @@ #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ #define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */ +#define SIO_F71808E_ID 0x0901 /* Chipset ID */ +#define SIO_F71808A_ID 0x1001 /* Chipset ID */ #define SIO_F71858_ID 0x0507 /* Chipset ID */ #define SIO_F71862_ID 0x0601 /* Chipset ID */ +#define SIO_F71869_ID 0x0814 /* Chipset ID */ +#define SIO_F71869A_ID 0x1007 /* Chipset ID */ #define SIO_F71882_ID 0x0541 /* Chipset ID */ #define SIO_F71889_ID 0x0723 /* Chipset ID */ +#define SIO_F71889E_ID 0x0909 /* Chipset ID */ +#define SIO_F71889A_ID 0x1005 /* Chipset ID */ #define SIO_F8000_ID 0x0581 /* Chipset ID */ +#define SIO_F81865_ID 0x0704 /* Chipset ID */ #define REGION_LENGTH 8 #define ADDR_REG_OFFSET 5 #define DATA_REG_OFFSET 6 -#define F71882FG_REG_PECI 0x0A - -#define F71882FG_REG_IN_STATUS 0x12 /* f71882fg only */ -#define F71882FG_REG_IN_BEEP 0x13 /* f71882fg only */ +#define F71882FG_REG_IN_STATUS 0x12 /* f7188x only */ +#define F71882FG_REG_IN_BEEP 0x13 /* f7188x only */ #define F71882FG_REG_IN(nr) (0x20 + (nr)) -#define F71882FG_REG_IN1_HIGH 0x32 /* f71882fg only */ +#define F71882FG_REG_IN1_HIGH 0x32 /* f7188x only */ #define F71882FG_REG_FAN(nr) (0xA0 + (16 * (nr))) #define F71882FG_REG_FAN_TARGET(nr) (0xA2 + (16 * (nr))) @@ -84,26 +91,130 @@ #define F71882FG_REG_FAN_HYST(nr) (0x98 + (nr)) +#define F71882FG_REG_FAN_FAULT_T 0x9F +#define F71882FG_FAN_NEG_TEMP_EN 0x20 +#define F71882FG_FAN_PROG_SEL 0x80 + #define F71882FG_REG_POINT_PWM(pwm, point) (0xAA + (point) + (16 * (pwm))) #define F71882FG_REG_POINT_TEMP(pwm, point) (0xA6 + (point) + (16 * (pwm))) #define F71882FG_REG_POINT_MAPPING(nr) (0xAF + 16 * (nr)) #define F71882FG_REG_START 0x01 +#define F71882FG_MAX_INS 9 + #define FAN_MIN_DETECT 366 /* Lowest detectable fanspeed */ static unsigned short force_id; module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); -enum chips { f71858fg, f71862fg, f71882fg, f71889fg, f8000 }; +enum chips { f71808e, f71808a, f71858fg, f71862fg, f71869, f71869a, f71882fg, + f71889fg, f71889ed, f71889a, f8000, f81865f }; -static const char *f71882fg_names[] = { +static const char *const f71882fg_names[] = { + "f71808e", + "f71808a", "f71858fg", "f71862fg", + "f71869", /* Both f71869f and f71869e, reg. compatible and same id */ + "f71869a", "f71882fg", - "f71889fg", + "f71889fg", /* f81801u too, same id */ + "f71889ed", + "f71889a", "f8000", + "f81865f", +}; + +static const char f71882fg_has_in[][F71882FG_MAX_INS] = { + [f71808e] = { 1, 1, 1, 1, 1, 1, 0, 1, 1 }, + [f71808a] = { 1, 1, 1, 1, 0, 0, 0, 1, 1 }, + [f71858fg] = { 1, 1, 1, 0, 0, 0, 0, 0, 0 }, + [f71862fg] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [f71869] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [f71869a] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [f71882fg] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [f71889fg] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [f71889ed] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [f71889a] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [f8000] = { 1, 1, 1, 0, 0, 0, 0, 0, 0 }, + [f81865f] = { 1, 1, 1, 1, 1, 1, 1, 0, 0 }, +}; + +static const char f71882fg_has_in1_alarm[] = { + [f71808e] = 0, + [f71808a] = 0, + [f71858fg] = 0, + [f71862fg] = 0, + [f71869] = 0, + [f71869a] = 0, + [f71882fg] = 1, + [f71889fg] = 1, + [f71889ed] = 1, + [f71889a] = 1, + [f8000] = 0, + [f81865f] = 1, +}; + +static const char f71882fg_fan_has_beep[] = { + [f71808e] = 0, + [f71808a] = 0, + [f71858fg] = 0, + [f71862fg] = 1, + [f71869] = 1, + [f71869a] = 1, + [f71882fg] = 1, + [f71889fg] = 1, + [f71889ed] = 1, + [f71889a] = 1, + [f8000] = 0, + [f81865f] = 1, +}; + +static const char f71882fg_nr_fans[] = { + [f71808e] = 3, + [f71808a] = 2, /* +1 fan which is monitor + simple pwm only */ + [f71858fg] = 3, + [f71862fg] = 3, + [f71869] = 3, + [f71869a] = 3, + [f71882fg] = 4, + [f71889fg] = 3, + [f71889ed] = 3, + [f71889a] = 3, + [f8000] = 3, /* +1 fan which is monitor only */ + [f81865f] = 2, +}; + +static const char f71882fg_temp_has_beep[] = { + [f71808e] = 0, + [f71808a] = 1, + [f71858fg] = 0, + [f71862fg] = 1, + [f71869] = 1, + [f71869a] = 1, + [f71882fg] = 1, + [f71889fg] = 1, + [f71889ed] = 1, + [f71889a] = 1, + [f8000] = 0, + [f81865f] = 1, +}; + +static const char f71882fg_nr_temps[] = { + [f71808e] = 2, + [f71808a] = 2, + [f71858fg] = 3, + [f71862fg] = 3, + [f71869] = 3, + [f71869a] = 3, + [f71882fg] = 3, + [f71889fg] = 3, + [f71889ed] = 3, + [f71889a] = 3, + [f8000] = 3, + [f81865f] = 2, }; static struct platform_device *f71882fg_pdev; @@ -111,7 +222,7 @@ static struct platform_device *f71882fg_pdev; /* Super-I/O Function prototypes */ static inline int superio_inb(int base, int reg); static inline int superio_inw(int base, int reg); -static inline void superio_enter(int base); +static inline int superio_enter(int base); static inline void superio_select(int base, int ld); static inline void superio_exit(int base); @@ -127,11 +238,12 @@ struct f71882fg_data { struct mutex update_lock; int temp_start; /* temp numbering start (0 or 1) */ char valid; /* !=0 if following fields are valid */ + char auto_point_temp_signed; unsigned long last_updated; /* In jiffies */ unsigned long last_limits; /* In jiffies */ /* Register Values */ - u8 in[9]; + u8 in[F71882FG_MAX_INS]; u8 in1_max; u8 in_status; u8 in_beep; @@ -140,9 +252,11 @@ struct f71882fg_data { u16 fan_full_speed[4]; u8 fan_status; u8 fan_beep; - /* Note: all models have only 3 temperature channels, but on some - they are addressed as 0-2 and on others as 1-3, so for coding - convenience we reserve space for 4 channels */ + /* + * Note: all models have max 3 temperature channels, but on some + * they are addressed as 0-2 and on others as 1-3, so for coding + * convenience we reserve space for 4 channels + */ u16 temp[4]; u8 temp_ovt[4]; u8 temp_high[4]; @@ -218,6 +332,10 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, char *buf); static ssize_t store_pwm(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count); +static ssize_t show_simple_pwm(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t store_simple_pwm(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count); static ssize_t show_pwm_enable(struct device *dev, struct device_attribute *devattr, char *buf); static ssize_t store_pwm_enable(struct device *dev, @@ -246,7 +364,7 @@ static ssize_t store_pwm_auto_point_temp(struct device *dev, static ssize_t show_name(struct device *dev, struct device_attribute *devattr, char *buf); -static int __devinit f71882fg_probe(struct platform_device * pdev); +static int f71882fg_probe(struct platform_device *pdev); static int f71882fg_remove(struct platform_device *pdev); static struct platform_driver f71882fg_driver = { @@ -260,13 +378,11 @@ static struct platform_driver f71882fg_driver = { static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); -/* Temp and in attr for the f71858fg, the f71858fg is special as it - has its temperature indexes start at 0 (the others start at 1) and - it only has 3 voltage inputs */ -static struct sensor_device_attribute_2 f71858fg_in_temp_attr[] = { - SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), - SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1), - SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2), +/* + * Temp attr for the f71858fg, the f71858fg is special as it has its + * temperature indexes start at 0 (the others start at 1) + */ +static struct sensor_device_attribute_2 f71858fg_temp_attr[] = { SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0), SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max, store_temp_max, 0, 0), @@ -290,7 +406,6 @@ static struct sensor_device_attribute_2 f71858fg_in_temp_attr[] = { SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 0, 1), SENSOR_ATTR_2(temp2_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5), - SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 1), SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1), SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2), SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max, @@ -306,37 +421,27 @@ static struct sensor_device_attribute_2 f71858fg_in_temp_attr[] = { SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), }; -/* Temp and in attr common to the f71862fg, f71882fg and f71889fg */ -static struct sensor_device_attribute_2 fxxxx_in_temp_attr[] = { - SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), - SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1), - SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2), - SENSOR_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 0, 3), - SENSOR_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 0, 4), - SENSOR_ATTR_2(in5_input, S_IRUGO, show_in, NULL, 0, 5), - SENSOR_ATTR_2(in6_input, S_IRUGO, show_in, NULL, 0, 6), - SENSOR_ATTR_2(in7_input, S_IRUGO, show_in, NULL, 0, 7), - SENSOR_ATTR_2(in8_input, S_IRUGO, show_in, NULL, 0, 8), +/* Temp attr for the standard models */ +static struct sensor_device_attribute_2 fxxxx_temp_attr[3][9] = { { SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 1), SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max, store_temp_max, 0, 1), SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, store_temp_max_hyst, 0, 1), - /* Should really be temp1_max_alarm, but older versions did not handle - the max and crit alarms separately and lm_sensors v2 depends on the - presence of temp#_alarm files. The same goes for temp2/3 _alarm. */ + /* + * Should really be temp1_max_alarm, but older versions did not handle + * the max and crit alarms separately and lm_sensors v2 depends on the + * presence of temp#_alarm files. The same goes for temp2/3 _alarm. + */ SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 1), - SENSOR_ATTR_2(temp1_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 1), SENSOR_ATTR_2(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit, store_temp_crit, 0, 1), SENSOR_ATTR_2(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 0, 1), SENSOR_ATTR_2(temp1_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5), - SENSOR_ATTR_2(temp1_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 5), SENSOR_ATTR_2(temp1_type, S_IRUGO, show_temp_type, NULL, 0, 1), SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 1), +}, { SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 2), SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_max, store_temp_max, 0, 2), @@ -344,17 +449,14 @@ static struct sensor_device_attribute_2 fxxxx_in_temp_attr[] = { store_temp_max_hyst, 0, 2), /* Should be temp2_max_alarm, see temp1_alarm note */ SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 2), - SENSOR_ATTR_2(temp2_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 2), SENSOR_ATTR_2(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit, store_temp_crit, 0, 2), SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 0, 2), SENSOR_ATTR_2(temp2_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6), - SENSOR_ATTR_2(temp2_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 6), SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 2), SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), +}, { SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 3), SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max, store_temp_max, 0, 3), @@ -362,37 +464,40 @@ static struct sensor_device_attribute_2 fxxxx_in_temp_attr[] = { store_temp_max_hyst, 0, 3), /* Should be temp3_max_alarm, see temp1_alarm note */ SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 3), - SENSOR_ATTR_2(temp3_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 3), SENSOR_ATTR_2(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit, store_temp_crit, 0, 3), SENSOR_ATTR_2(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 0, 3), SENSOR_ATTR_2(temp3_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 7), - SENSOR_ATTR_2(temp3_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 7), SENSOR_ATTR_2(temp3_type, S_IRUGO, show_temp_type, NULL, 0, 3), SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 3), -}; +} }; -/* For models with in1 alarm capability */ -static struct sensor_device_attribute_2 fxxxx_in1_alarm_attr[] = { - SENSOR_ATTR_2(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max, - 0, 1), - SENSOR_ATTR_2(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep, - 0, 1), - SENSOR_ATTR_2(in1_alarm, S_IRUGO, show_in_alarm, NULL, 0, 1), -}; +/* Temp attr for models which can beep on temp alarm */ +static struct sensor_device_attribute_2 fxxxx_temp_beep_attr[3][2] = { { + SENSOR_ATTR_2(temp1_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 1), + SENSOR_ATTR_2(temp1_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 5), +}, { + SENSOR_ATTR_2(temp2_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 2), + SENSOR_ATTR_2(temp2_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 6), +}, { + SENSOR_ATTR_2(temp3_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 3), + SENSOR_ATTR_2(temp3_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 7), +} }; -/* Temp and in attr for the f8000 - Note on the f8000 temp_ovt (crit) is used as max, and temp_high (max) - is used as hysteresis value to clear alarms - Also like the f71858fg its temperature indexes start at 0 +/* + * Temp attr for the f8000 + * Note on the f8000 temp_ovt (crit) is used as max, and temp_high (max) + * is used as hysteresis value to clear alarms + * Also like the f71858fg its temperature indexes start at 0 */ -static struct sensor_device_attribute_2 f8000_in_temp_attr[] = { - SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), - SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1), - SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2), +static struct sensor_device_attribute_2 f8000_temp_attr[] = { SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0), SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_crit, store_temp_crit, 0, 0), @@ -406,7 +511,6 @@ static struct sensor_device_attribute_2 f8000_in_temp_attr[] = { SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max, store_temp_max, 0, 1), SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5), - SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 1), SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1), SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2), SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_crit, @@ -417,6 +521,28 @@ static struct sensor_device_attribute_2 f8000_in_temp_attr[] = { SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), }; +/* in attr for all models */ +static struct sensor_device_attribute_2 fxxxx_in_attr[] = { + SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), + SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1), + SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2), + SENSOR_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 0, 3), + SENSOR_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 0, 4), + SENSOR_ATTR_2(in5_input, S_IRUGO, show_in, NULL, 0, 5), + SENSOR_ATTR_2(in6_input, S_IRUGO, show_in, NULL, 0, 6), + SENSOR_ATTR_2(in7_input, S_IRUGO, show_in, NULL, 0, 7), + SENSOR_ATTR_2(in8_input, S_IRUGO, show_in, NULL, 0, 8), +}; + +/* For models with in1 alarm capability */ +static struct sensor_device_attribute_2 fxxxx_in1_alarm_attr[] = { + SENSOR_ATTR_2(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max, + 0, 1), + SENSOR_ATTR_2(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep, + 0, 1), + SENSOR_ATTR_2(in1_alarm, S_IRUGO, show_in_alarm, NULL, 0, 1), +}; + /* Fan / PWM attr common to all models */ static struct sensor_device_attribute_2 fxxxx_fan_attr[4][6] = { { SENSOR_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0), @@ -464,6 +590,14 @@ static struct sensor_device_attribute_2 fxxxx_fan_attr[4][6] = { { show_pwm_interpolate, store_pwm_interpolate, 0, 3), } }; +/* Attr for the third fan of the f71808a, which only has manual pwm */ +static struct sensor_device_attribute_2 f71808a_fan3_attr[] = { + SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2), + SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2), + SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, + show_simple_pwm, store_simple_pwm, 0, 2), +}; + /* Attr for models which can beep on Fan alarm */ static struct sensor_device_attribute_2 fxxxx_fan_beep_attr[] = { SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep, @@ -476,9 +610,11 @@ static struct sensor_device_attribute_2 fxxxx_fan_beep_attr[] = { store_fan_beep, 0, 3), }; -/* PWM attr for the f71862fg, fewer pwms and fewer zones per pwm than the - f71858fg / f71882fg / f71889fg */ -static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = { +/* + * PWM attr for the f71862fg, fewer pwms and fewer zones per pwm than the + * standard models + */ +static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[3][7] = { { SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 0), @@ -500,7 +636,7 @@ static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = { 0, 0), SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 0), - +}, { SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 1), @@ -522,7 +658,7 @@ static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = { 0, 1), SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 1), - +}, { SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 2), @@ -544,9 +680,91 @@ static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = { 0, 2), SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 2), -}; +} }; -/* PWM attr common to the f71858fg, f71882fg and f71889fg */ +/* + * PWM attr for the f71808e/f71869, almost identical to the f71862fg, but the + * pwm setting when the temperature is above the pwmX_auto_point1_temp can be + * programmed instead of being hardcoded to 0xff + */ +static struct sensor_device_attribute_2 f71869_auto_pwm_attr[3][8] = { { + SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 0), + SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 0), + SENSOR_ATTR_2(pwm1_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 0), + SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 0), + SENSOR_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 0), +}, { + SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 1), + SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 1), + SENSOR_ATTR_2(pwm2_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 1), + SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 1), + SENSOR_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 1), +}, { + SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 2), + SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 2), + SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 2), + SENSOR_ATTR_2(pwm3_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 2), + SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 2), + SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 2), + SENSOR_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 2), + SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 2), +} }; + +/* PWM attr for the standard models */ static struct sensor_device_attribute_2 fxxxx_auto_pwm_attr[4][14] = { { SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, @@ -718,10 +936,12 @@ static struct sensor_device_attribute_2 f8000_fan_attr[] = { SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), }; -/* PWM attr for the f8000, zones mapped to temp instead of to pwm! - Also the register block at offset A0 maps to TEMP1 (so our temp2, as the - F8000 starts counting temps at 0), B0 maps the TEMP2 and C0 maps to TEMP0 */ -static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = { +/* + * PWM attr for the f8000, zones mapped to temp instead of to pwm! + * Also the register block at offset A0 maps to TEMP1 (so our temp2, as the + * F8000 starts counting temps at 0), B0 maps the TEMP2 and C0 maps to TEMP0 + */ +static struct sensor_device_attribute_2 f8000_auto_pwm_attr[3][14] = { { SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 0), @@ -762,7 +982,7 @@ static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 2, 2), SENSOR_ATTR_2(temp1_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 2), - +}, { SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 1), @@ -803,7 +1023,7 @@ static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 2, 0), SENSOR_ATTR_2(temp2_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 0), - +}, { SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 2), @@ -844,7 +1064,7 @@ static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 2, 1), SENSOR_ATTR_2(temp3_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 1), -}; +} }; /* Super I/O functions */ static inline int superio_inb(int base, int reg) @@ -856,21 +1076,27 @@ static inline int superio_inb(int base, int reg) static int superio_inw(int base, int reg) { int val; - outb(reg++, base); - val = inb(base + 1) << 8; - outb(reg, base); - val |= inb(base + 1); + val = superio_inb(base, reg) << 8; + val |= superio_inb(base, reg + 1); return val; } -static inline void superio_enter(int base) +static inline int superio_enter(int base) { + /* Don't step on other drivers' I/O space by accident */ + if (!request_muxed_region(base, 2, DRVNAME)) { + pr_err("I/O address 0x%04x already in use\n", base); + return -EBUSY; + } + /* according to the datasheet the key must be send twice! */ - outb( SIO_UNLOCK_KEY, base); - outb( SIO_UNLOCK_KEY, base); + outb(SIO_UNLOCK_KEY, base); + outb(SIO_UNLOCK_KEY, base); + + return 0; } -static inline void superio_select( int base, int ld) +static inline void superio_select(int base, int ld) { outb(SIO_REG_LDSEL, base); outb(ld, base + 1); @@ -879,6 +1105,7 @@ static inline void superio_select( int base, int ld) static inline void superio_exit(int base) { outb(SIO_LOCK_KEY, base); + release_region(base, 2); } static inline int fan_from_reg(u16 reg) @@ -905,10 +1132,8 @@ static u16 f71882fg_read16(struct f71882fg_data *data, u8 reg) { u16 val; - outb(reg++, data->addr + ADDR_REG_OFFSET); - val = inb(data->addr + DATA_REG_OFFSET) << 8; - outb(reg, data->addr + ADDR_REG_OFFSET); - val |= inb(data->addr + DATA_REG_OFFSET); + val = f71882fg_read8(data, reg) << 8; + val |= f71882fg_read8(data, reg + 1); return val; } @@ -921,10 +1146,8 @@ static void f71882fg_write8(struct f71882fg_data *data, u8 reg, u8 val) static void f71882fg_write16(struct f71882fg_data *data, u8 reg, u16 val) { - outb(reg++, data->addr + ADDR_REG_OFFSET); - outb(val >> 8, data->addr + DATA_REG_OFFSET); - outb(reg, data->addr + ADDR_REG_OFFSET); - outb(val & 255, data->addr + DATA_REG_OFFSET); + f71882fg_write8(data, reg, val >> 8); + f71882fg_write8(data, reg + 1, val & 0xff); } static u16 f71882fg_read_temp(struct f71882fg_data *data, int nr) @@ -938,16 +1161,16 @@ static u16 f71882fg_read_temp(struct f71882fg_data *data, int nr) static struct f71882fg_data *f71882fg_update_device(struct device *dev) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr, reg = 0, reg2; - int nr_fans = (data->type == f71882fg) ? 4 : 3; - int nr_ins = (data->type == f71858fg || data->type == f8000) ? 3 : 9; + int nr_fans = f71882fg_nr_fans[data->type]; + int nr_temps = f71882fg_nr_temps[data->type]; + int nr, reg, point; mutex_lock(&data->update_lock); /* Update once every 60 seconds */ - if ( time_after(jiffies, data->last_limits + 60 * HZ ) || + if (time_after(jiffies, data->last_limits + 60 * HZ) || !data->valid) { - if (data->type == f71882fg || data->type == f71889fg) { + if (f71882fg_has_in1_alarm[data->type]) { data->in1_max = f71882fg_read8(data, F71882FG_REG_IN1_HIGH); data->in_beep = @@ -955,7 +1178,8 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) } /* Get High & boundary temps*/ - for (nr = data->temp_start; nr < 3 + data->temp_start; nr++) { + for (nr = data->temp_start; nr < nr_temps + data->temp_start; + nr++) { data->temp_ovt[nr] = f71882fg_read8(data, F71882FG_REG_TEMP_OVT(nr)); data->temp_high[nr] = f71882fg_read8(data, @@ -968,45 +1192,21 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) data->temp_hyst[1] = f71882fg_read8(data, F71882FG_REG_TEMP_HYST(1)); } + /* All but the f71858fg / f8000 have this register */ + if ((data->type != f71858fg) && (data->type != f8000)) { + reg = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE); + data->temp_type[1] = (reg & 0x02) ? 2 : 4; + data->temp_type[2] = (reg & 0x04) ? 2 : 4; + data->temp_type[3] = (reg & 0x08) ? 2 : 4; + } - if (data->type == f71862fg || data->type == f71882fg || - data->type == f71889fg) { + if (f71882fg_fan_has_beep[data->type]) data->fan_beep = f71882fg_read8(data, F71882FG_REG_FAN_BEEP); + + if (f71882fg_temp_has_beep[data->type]) data->temp_beep = f71882fg_read8(data, F71882FG_REG_TEMP_BEEP); - /* Have to hardcode type, because temp1 is special */ - reg = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE); - data->temp_type[2] = (reg & 0x04) ? 2 : 4; - data->temp_type[3] = (reg & 0x08) ? 2 : 4; - } - /* Determine temp index 1 sensor type */ - if (data->type == f71889fg) { - reg2 = f71882fg_read8(data, F71882FG_REG_START); - switch ((reg2 & 0x60) >> 5) { - case 0x00: /* BJT / Thermistor */ - data->temp_type[1] = (reg & 0x02) ? 2 : 4; - break; - case 0x01: /* AMDSI */ - data->temp_type[1] = 5; - break; - case 0x02: /* PECI */ - case 0x03: /* Ibex Peak ?? Report as PECI for now */ - data->temp_type[1] = 6; - break; - } - } else { - reg2 = f71882fg_read8(data, F71882FG_REG_PECI); - if ((reg2 & 0x03) == 0x01) - data->temp_type[1] = 6; /* PECI */ - else if ((reg2 & 0x03) == 0x02) - data->temp_type[1] = 5; /* AMDSI */ - else if (data->type == f71862fg || - data->type == f71882fg) - data->temp_type[1] = (reg & 0x02) ? 2 : 4; - else /* f71858fg and f8000 only support BJT */ - data->temp_type[1] = 2; - } data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); @@ -1020,8 +1220,8 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(nr)); - if (data->type != f71862fg) { - int point; + switch (data->type) { + default: for (point = 0; point < 5; point++) { data->pwm_auto_point_pwm[nr][point] = f71882fg_read8(data, @@ -1034,7 +1234,14 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) F71882FG_REG_POINT_TEMP (nr, point)); } - } else { + break; + case f71808e: + case f71869: + data->pwm_auto_point_pwm[nr][0] = + f71882fg_read8(data, + F71882FG_REG_POINT_PWM(nr, 0)); + /* Fall through */ + case f71862fg: data->pwm_auto_point_pwm[nr][1] = f71882fg_read8(data, F71882FG_REG_POINT_PWM @@ -1051,6 +1258,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) f71882fg_read8(data, F71882FG_REG_POINT_TEMP (nr, 3)); + break; } } data->last_limits = jiffies; @@ -1062,7 +1270,8 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) F71882FG_REG_TEMP_STATUS); data->temp_diode_open = f71882fg_read8(data, F71882FG_REG_TEMP_DIODE_OPEN); - for (nr = data->temp_start; nr < 3 + data->temp_start; nr++) + for (nr = data->temp_start; nr < nr_temps + data->temp_start; + nr++) data->temp[nr] = f71882fg_read_temp(data, nr); data->fan_status = f71882fg_read8(data, @@ -1078,17 +1287,24 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) data->pwm[nr] = f71882fg_read8(data, F71882FG_REG_PWM(nr)); } - - /* The f8000 can monitor 1 more fan, but has no pwm for it */ + /* Some models have 1 more fan with limited capabilities */ + if (data->type == f71808a) { + data->fan[2] = f71882fg_read16(data, + F71882FG_REG_FAN(2)); + data->pwm[2] = f71882fg_read8(data, + F71882FG_REG_PWM(2)); + } if (data->type == f8000) data->fan[3] = f71882fg_read16(data, F71882FG_REG_FAN(3)); - if (data->type == f71882fg || data->type == f71889fg) + + if (f71882fg_has_in1_alarm[data->type]) data->in_status = f71882fg_read8(data, F71882FG_REG_IN_STATUS); - for (nr = 0; nr < nr_ins; nr++) - data->in[nr] = f71882fg_read8(data, - F71882FG_REG_IN(nr)); + for (nr = 0; nr < F71882FG_MAX_INS; nr++) + if (f71882fg_has_in[data->type][nr]) + data->in[nr] = f71882fg_read8(data, + F71882FG_REG_IN(nr)); data->last_updated = jiffies; data->valid = 1; @@ -1127,10 +1343,14 @@ static ssize_t store_fan_full_speed(struct device *dev, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - long val = simple_strtol(buf, NULL, 10); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; - val = SENSORS_LIMIT(val, 23, 1500000); + val = clamp_val(val, 23, 1500000); val = fan_to_reg(val); mutex_lock(&data->update_lock); @@ -1157,8 +1377,12 @@ static ssize_t store_fan_beep(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - unsigned long val = simple_strtoul(buf, NULL, 10); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + unsigned long val; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->fan_beep = f71882fg_read8(data, F71882FG_REG_FAN_BEEP); @@ -1206,8 +1430,15 @@ static ssize_t store_in_max(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10) / 8; - val = SENSORS_LIMIT(val, 0, 255); + int err; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val /= 8; + val = clamp_val(val, 0, 255); mutex_lock(&data->update_lock); f71882fg_write8(data, F71882FG_REG_IN1_HIGH, val); @@ -1233,8 +1464,12 @@ static ssize_t store_in_beep(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - unsigned long val = simple_strtoul(buf, NULL, 10); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + unsigned long val; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_beep = f71882fg_read8(data, F71882FG_REG_IN_BEEP); @@ -1299,9 +1534,15 @@ static ssize_t store_temp_max(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - long val = simple_strtol(buf, NULL, 10) / 1000; - val = SENSORS_LIMIT(val, 0, 255); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val /= 1000; + val = clamp_val(val, 0, 255); mutex_lock(&data->update_lock); f71882fg_write8(data, F71882FG_REG_TEMP_HIGH(nr), val); @@ -1333,17 +1574,22 @@ static ssize_t store_temp_max_hyst(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - long val = simple_strtol(buf, NULL, 10) / 1000; + int err, nr = to_sensor_dev_attr_2(devattr)->index; ssize_t ret = count; u8 reg; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val /= 1000; mutex_lock(&data->update_lock); /* convert abs to relative and check */ data->temp_high[nr] = f71882fg_read8(data, F71882FG_REG_TEMP_HIGH(nr)); - val = SENSORS_LIMIT(val, data->temp_high[nr] - 15, - data->temp_high[nr]); + val = clamp_val(val, data->temp_high[nr] - 15, data->temp_high[nr]); val = data->temp_high[nr] - val; /* convert value to register contents */ @@ -1372,9 +1618,15 @@ static ssize_t store_temp_crit(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - long val = simple_strtol(buf, NULL, 10) / 1000; - val = SENSORS_LIMIT(val, 0, 255); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val /= 1000; + val = clamp_val(val, 0, 255); mutex_lock(&data->update_lock); f71882fg_write8(data, F71882FG_REG_TEMP_OVT(nr), val); @@ -1427,8 +1679,12 @@ static ssize_t store_temp_beep(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - unsigned long val = simple_strtoul(buf, NULL, 10); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + unsigned long val; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_beep = f71882fg_read8(data, F71882FG_REG_TEMP_BEEP); @@ -1490,9 +1746,14 @@ static ssize_t store_pwm(struct device *dev, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - long val = simple_strtol(buf, NULL, 10); - val = SENSORS_LIMIT(val, 0, 255); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val = clamp_val(val, 0, 255); mutex_lock(&data->update_lock); data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); @@ -1521,6 +1782,38 @@ leave: return count; } +static ssize_t show_simple_pwm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int val, nr = to_sensor_dev_attr_2(devattr)->index; + + val = data->pwm[nr]; + return sprintf(buf, "%d\n", val); +} + +static ssize_t store_simple_pwm(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val = clamp_val(val, 0, 255); + + mutex_lock(&data->update_lock); + f71882fg_write8(data, F71882FG_REG_PWM(nr), val); + data->pwm[nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + static ssize_t show_pwm_enable(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -1551,8 +1844,12 @@ static ssize_t store_pwm_enable(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - long val = simple_strtol(buf, NULL, 10); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; /* Special case for F8000 pwm channel 3 which only does auto mode */ if (data->type == f8000 && nr == 2 && val != 2) @@ -1626,10 +1923,15 @@ static ssize_t store_pwm_auto_point_pwm(struct device *dev, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int pwm = to_sensor_dev_attr_2(devattr)->index; + int err, pwm = to_sensor_dev_attr_2(devattr)->index; int point = to_sensor_dev_attr_2(devattr)->nr; - long val = simple_strtol(buf, NULL, 10); - val = SENSORS_LIMIT(val, 0, 255); + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val = clamp_val(val, 0, 255); mutex_lock(&data->update_lock); data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); @@ -1674,16 +1976,22 @@ static ssize_t store_pwm_auto_point_temp_hyst(struct device *dev, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; + int err, nr = to_sensor_dev_attr_2(devattr)->index; int point = to_sensor_dev_attr_2(devattr)->nr; - long val = simple_strtol(buf, NULL, 10) / 1000; u8 reg; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val /= 1000; mutex_lock(&data->update_lock); data->pwm_auto_point_temp[nr][point] = f71882fg_read8(data, F71882FG_REG_POINT_TEMP(nr, point)); - val = SENSORS_LIMIT(val, data->pwm_auto_point_temp[nr][point] - 15, - data->pwm_auto_point_temp[nr][point]); + val = clamp_val(val, data->pwm_auto_point_temp[nr][point] - 15, + data->pwm_auto_point_temp[nr][point]); val = data->pwm_auto_point_temp[nr][point] - val; reg = f71882fg_read8(data, F71882FG_REG_FAN_HYST(nr / 2)); @@ -1716,8 +2024,12 @@ static ssize_t store_pwm_interpolate(struct device *dev, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - unsigned long val = simple_strtoul(buf, NULL, 10); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + unsigned long val; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->pwm_auto_point_mapping[nr] = @@ -1752,8 +2064,12 @@ static ssize_t store_pwm_auto_point_channel(struct device *dev, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - long val = simple_strtol(buf, NULL, 10); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; switch (val) { case 1: @@ -1798,14 +2114,20 @@ static ssize_t store_pwm_auto_point_temp(struct device *dev, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int pwm = to_sensor_dev_attr_2(devattr)->index; + int err, pwm = to_sensor_dev_attr_2(devattr)->index; int point = to_sensor_dev_attr_2(devattr)->nr; - long val = simple_strtol(buf, NULL, 10) / 1000; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; - if (data->type == f71889fg) - val = SENSORS_LIMIT(val, -128, 127); + val /= 1000; + + if (data->auto_point_temp_signed) + val = clamp_val(val, -128, 127); else - val = SENSORS_LIMIT(val, 0, 127); + val = clamp_val(val, 0, 127); mutex_lock(&data->update_lock); f71882fg_write8(data, F71882FG_REG_POINT_TEMP(pwm, point), val); @@ -1822,7 +2144,7 @@ static ssize_t show_name(struct device *dev, struct device_attribute *devattr, return sprintf(buf, "%s\n", f71882fg_names[data->type]); } -static int __devinit f71882fg_create_sysfs_files(struct platform_device *pdev, +static int f71882fg_create_sysfs_files(struct platform_device *pdev, struct sensor_device_attribute_2 *attr, int count) { int err, i; @@ -1844,14 +2166,115 @@ static void f71882fg_remove_sysfs_files(struct platform_device *pdev, device_remove_file(&pdev->dev, &attr[i].dev_attr); } -static int __devinit f71882fg_probe(struct platform_device *pdev) +static int f71882fg_create_fan_sysfs_files( + struct platform_device *pdev, int idx) +{ + struct f71882fg_data *data = platform_get_drvdata(pdev); + int err; + + /* Sanity check the pwm setting */ + err = 0; + switch (data->type) { + case f71858fg: + if (((data->pwm_enable >> (idx * 2)) & 3) == 3) + err = 1; + break; + case f71862fg: + if (((data->pwm_enable >> (idx * 2)) & 1) != 1) + err = 1; + break; + case f8000: + if (idx == 2) + err = data->pwm_enable & 0x20; + break; + default: + break; + } + if (err) { + dev_err(&pdev->dev, + "Invalid (reserved) pwm settings: 0x%02x, " + "skipping fan %d\n", + (data->pwm_enable >> (idx * 2)) & 3, idx + 1); + return 0; /* This is a non fatal condition */ + } + + err = f71882fg_create_sysfs_files(pdev, &fxxxx_fan_attr[idx][0], + ARRAY_SIZE(fxxxx_fan_attr[0])); + if (err) + return err; + + if (f71882fg_fan_has_beep[data->type]) { + err = f71882fg_create_sysfs_files(pdev, + &fxxxx_fan_beep_attr[idx], + 1); + if (err) + return err; + } + + dev_info(&pdev->dev, "Fan: %d is in %s mode\n", idx + 1, + (data->pwm_enable & (1 << (2 * idx))) ? "duty-cycle" : "RPM"); + + /* Check for unsupported auto pwm settings */ + switch (data->type) { + case f71808e: + case f71808a: + case f71869: + case f71869a: + case f71889fg: + case f71889ed: + case f71889a: + data->pwm_auto_point_mapping[idx] = + f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(idx)); + if ((data->pwm_auto_point_mapping[idx] & 0x80) || + (data->pwm_auto_point_mapping[idx] & 3) == 0) { + dev_warn(&pdev->dev, + "Auto pwm controlled by raw digital " + "data, disabling pwm auto_point " + "sysfs attributes for fan %d\n", idx + 1); + return 0; /* This is a non fatal condition */ + } + break; + default: + break; + } + + switch (data->type) { + case f71862fg: + err = f71882fg_create_sysfs_files(pdev, + &f71862fg_auto_pwm_attr[idx][0], + ARRAY_SIZE(f71862fg_auto_pwm_attr[0])); + break; + case f71808e: + case f71869: + err = f71882fg_create_sysfs_files(pdev, + &f71869_auto_pwm_attr[idx][0], + ARRAY_SIZE(f71869_auto_pwm_attr[0])); + break; + case f8000: + err = f71882fg_create_sysfs_files(pdev, + &f8000_auto_pwm_attr[idx][0], + ARRAY_SIZE(f8000_auto_pwm_attr[0])); + break; + default: + err = f71882fg_create_sysfs_files(pdev, + &fxxxx_auto_pwm_attr[idx][0], + ARRAY_SIZE(fxxxx_auto_pwm_attr[0])); + } + + return err; +} + +static int f71882fg_probe(struct platform_device *pdev) { struct f71882fg_data *data; - struct f71882fg_sio_data *sio_data = pdev->dev.platform_data; - int err, i, nr_fans = (sio_data->type == f71882fg) ? 4 : 3; - u8 start_reg; + struct f71882fg_sio_data *sio_data = dev_get_platdata(&pdev->dev); + int nr_fans = f71882fg_nr_fans[sio_data->type]; + int nr_temps = f71882fg_nr_temps[sio_data->type]; + int err, i; + u8 start_reg, reg; - data = kzalloc(sizeof(struct f71882fg_data), GFP_KERNEL); + data = devm_kzalloc(&pdev->dev, sizeof(struct f71882fg_data), + GFP_KERNEL); if (!data) return -ENOMEM; @@ -1865,13 +2288,11 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) start_reg = f71882fg_read8(data, F71882FG_REG_START); if (start_reg & 0x04) { dev_warn(&pdev->dev, "Hardware monitor is powered down\n"); - err = -ENODEV; - goto exit_free; + return -ENODEV; } if (!(start_reg & 0x03)) { dev_warn(&pdev->dev, "Hardware monitoring not activated\n"); - err = -ENODEV; - goto exit_free; + return -ENODEV; } /* Register sysfs interface files */ @@ -1885,127 +2306,106 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) data->temp_config = f71882fg_read8(data, F71882FG_REG_TEMP_CONFIG); if (data->temp_config & 0x10) - /* The f71858fg temperature alarms behave as - the f8000 alarms in this mode */ + /* + * The f71858fg temperature alarms behave as + * the f8000 alarms in this mode + */ err = f71882fg_create_sysfs_files(pdev, - f8000_in_temp_attr, - ARRAY_SIZE(f8000_in_temp_attr)); + f8000_temp_attr, + ARRAY_SIZE(f8000_temp_attr)); else err = f71882fg_create_sysfs_files(pdev, - f71858fg_in_temp_attr, - ARRAY_SIZE(f71858fg_in_temp_attr)); - break; - case f71882fg: - case f71889fg: - err = f71882fg_create_sysfs_files(pdev, - fxxxx_in1_alarm_attr, - ARRAY_SIZE(fxxxx_in1_alarm_attr)); - if (err) - goto exit_unregister_sysfs; - /* fall through! */ - case f71862fg: - err = f71882fg_create_sysfs_files(pdev, - fxxxx_in_temp_attr, - ARRAY_SIZE(fxxxx_in_temp_attr)); + f71858fg_temp_attr, + ARRAY_SIZE(f71858fg_temp_attr)); break; case f8000: err = f71882fg_create_sysfs_files(pdev, - f8000_in_temp_attr, - ARRAY_SIZE(f8000_in_temp_attr)); + f8000_temp_attr, + ARRAY_SIZE(f8000_temp_attr)); break; + default: + err = f71882fg_create_sysfs_files(pdev, + &fxxxx_temp_attr[0][0], + ARRAY_SIZE(fxxxx_temp_attr[0]) * nr_temps); } if (err) goto exit_unregister_sysfs; + + if (f71882fg_temp_has_beep[data->type]) { + err = f71882fg_create_sysfs_files(pdev, + &fxxxx_temp_beep_attr[0][0], + ARRAY_SIZE(fxxxx_temp_beep_attr[0]) + * nr_temps); + if (err) + goto exit_unregister_sysfs; + } + + for (i = 0; i < F71882FG_MAX_INS; i++) { + if (f71882fg_has_in[data->type][i]) { + err = device_create_file(&pdev->dev, + &fxxxx_in_attr[i].dev_attr); + if (err) + goto exit_unregister_sysfs; + } + } + if (f71882fg_has_in1_alarm[data->type]) { + err = f71882fg_create_sysfs_files(pdev, + fxxxx_in1_alarm_attr, + ARRAY_SIZE(fxxxx_in1_alarm_attr)); + if (err) + goto exit_unregister_sysfs; + } } if (start_reg & 0x02) { - data->pwm_enable = - f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); - - /* Sanity check the pwm settings */ switch (data->type) { - case f71858fg: - err = 0; - for (i = 0; i < nr_fans; i++) - if (((data->pwm_enable >> (i * 2)) & 3) == 3) - err = 1; - break; - case f71862fg: - err = (data->pwm_enable & 0x15) != 0x15; - break; - case f71882fg: + case f71808e: + case f71808a: + case f71869: + case f71869a: + /* These always have signed auto point temps */ + data->auto_point_temp_signed = 1; + /* Fall through to select correct fan/pwm reg bank! */ case f71889fg: - err = 0; + case f71889ed: + case f71889a: + reg = f71882fg_read8(data, F71882FG_REG_FAN_FAULT_T); + if (reg & F71882FG_FAN_NEG_TEMP_EN) + data->auto_point_temp_signed = 1; + /* Ensure banked pwm registers point to right bank */ + reg &= ~F71882FG_FAN_PROG_SEL; + f71882fg_write8(data, F71882FG_REG_FAN_FAULT_T, reg); break; - case f8000: - err = data->pwm_enable & 0x20; + default: break; } - if (err) { - dev_err(&pdev->dev, - "Invalid (reserved) pwm settings: 0x%02x\n", - (unsigned int)data->pwm_enable); - err = -ENODEV; - goto exit_unregister_sysfs; - } - err = f71882fg_create_sysfs_files(pdev, &fxxxx_fan_attr[0][0], - ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans); - if (err) - goto exit_unregister_sysfs; + data->pwm_enable = + f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); - if (data->type == f71862fg || data->type == f71882fg || - data->type == f71889fg) { - err = f71882fg_create_sysfs_files(pdev, - fxxxx_fan_beep_attr, nr_fans); + for (i = 0; i < nr_fans; i++) { + err = f71882fg_create_fan_sysfs_files(pdev, i); if (err) goto exit_unregister_sysfs; } + /* Some types have 1 extra fan with limited functionality */ switch (data->type) { - case f71862fg: + case f71808a: err = f71882fg_create_sysfs_files(pdev, - f71862fg_auto_pwm_attr, - ARRAY_SIZE(f71862fg_auto_pwm_attr)); + f71808a_fan3_attr, + ARRAY_SIZE(f71808a_fan3_attr)); break; case f8000: err = f71882fg_create_sysfs_files(pdev, f8000_fan_attr, ARRAY_SIZE(f8000_fan_attr)); - if (err) - goto exit_unregister_sysfs; - err = f71882fg_create_sysfs_files(pdev, - f8000_auto_pwm_attr, - ARRAY_SIZE(f8000_auto_pwm_attr)); break; - case f71889fg: - for (i = 0; i < nr_fans; i++) { - data->pwm_auto_point_mapping[i] = - f71882fg_read8(data, - F71882FG_REG_POINT_MAPPING(i)); - if (data->pwm_auto_point_mapping[i] & 0x80) - break; - } - if (i != nr_fans) { - dev_warn(&pdev->dev, - "Auto pwm controlled by raw digital " - "data, disabling pwm auto_point " - "sysfs attributes\n"); - break; - } - /* fall through */ - default: /* f71858fg / f71882fg */ - err = f71882fg_create_sysfs_files(pdev, - &fxxxx_auto_pwm_attr[0][0], - ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans); + default: + break; } if (err) goto exit_unregister_sysfs; - - for (i = 0; i < nr_fans; i++) - dev_info(&pdev->dev, "Fan: %d is in %s mode\n", i + 1, - (data->pwm_enable & (1 << 2 * i)) ? - "duty-cycle" : "RPM"); } data->hwmon_dev = hwmon_device_register(&pdev->dev); @@ -2020,18 +2420,16 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) exit_unregister_sysfs: f71882fg_remove(pdev); /* Will unregister the sysfs files for us */ return err; /* f71882fg_remove() also frees our data */ -exit_free: - kfree(data); - return err; } static int f71882fg_remove(struct platform_device *pdev) { struct f71882fg_data *data = platform_get_drvdata(pdev); - int nr_fans = (data->type == f71882fg) ? 4 : 3; + int nr_fans = f71882fg_nr_fans[data->type]; + int nr_temps = f71882fg_nr_temps[data->type]; + int i; u8 start_reg = f71882fg_read8(data, F71882FG_REG_START); - platform_set_drvdata(pdev, NULL); if (data->hwmon_dev) hwmon_device_unregister(data->hwmon_dev); @@ -2042,29 +2440,39 @@ static int f71882fg_remove(struct platform_device *pdev) case f71858fg: if (data->temp_config & 0x10) f71882fg_remove_sysfs_files(pdev, - f8000_in_temp_attr, - ARRAY_SIZE(f8000_in_temp_attr)); + f8000_temp_attr, + ARRAY_SIZE(f8000_temp_attr)); else f71882fg_remove_sysfs_files(pdev, - f71858fg_in_temp_attr, - ARRAY_SIZE(f71858fg_in_temp_attr)); - break; - case f71882fg: - case f71889fg: - f71882fg_remove_sysfs_files(pdev, - fxxxx_in1_alarm_attr, - ARRAY_SIZE(fxxxx_in1_alarm_attr)); - /* fall through! */ - case f71862fg: - f71882fg_remove_sysfs_files(pdev, - fxxxx_in_temp_attr, - ARRAY_SIZE(fxxxx_in_temp_attr)); + f71858fg_temp_attr, + ARRAY_SIZE(f71858fg_temp_attr)); break; case f8000: f71882fg_remove_sysfs_files(pdev, - f8000_in_temp_attr, - ARRAY_SIZE(f8000_in_temp_attr)); + f8000_temp_attr, + ARRAY_SIZE(f8000_temp_attr)); break; + default: + f71882fg_remove_sysfs_files(pdev, + &fxxxx_temp_attr[0][0], + ARRAY_SIZE(fxxxx_temp_attr[0]) * nr_temps); + } + if (f71882fg_temp_has_beep[data->type]) { + f71882fg_remove_sysfs_files(pdev, + &fxxxx_temp_beep_attr[0][0], + ARRAY_SIZE(fxxxx_temp_beep_attr[0]) * nr_temps); + } + + for (i = 0; i < F71882FG_MAX_INS; i++) { + if (f71882fg_has_in[data->type][i]) { + device_remove_file(&pdev->dev, + &fxxxx_in_attr[i].dev_attr); + } + } + if (f71882fg_has_in1_alarm[data->type]) { + f71882fg_remove_sysfs_files(pdev, + fxxxx_in1_alarm_attr, + ARRAY_SIZE(fxxxx_in1_alarm_attr)); } } @@ -2072,71 +2480,106 @@ static int f71882fg_remove(struct platform_device *pdev) f71882fg_remove_sysfs_files(pdev, &fxxxx_fan_attr[0][0], ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans); - if (data->type == f71862fg || data->type == f71882fg || - data->type == f71889fg) + if (f71882fg_fan_has_beep[data->type]) { f71882fg_remove_sysfs_files(pdev, fxxxx_fan_beep_attr, nr_fans); + } switch (data->type) { + case f71808a: + f71882fg_remove_sysfs_files(pdev, + &fxxxx_auto_pwm_attr[0][0], + ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans); + f71882fg_remove_sysfs_files(pdev, + f71808a_fan3_attr, + ARRAY_SIZE(f71808a_fan3_attr)); + break; case f71862fg: f71882fg_remove_sysfs_files(pdev, - f71862fg_auto_pwm_attr, - ARRAY_SIZE(f71862fg_auto_pwm_attr)); + &f71862fg_auto_pwm_attr[0][0], + ARRAY_SIZE(f71862fg_auto_pwm_attr[0]) * + nr_fans); + break; + case f71808e: + case f71869: + f71882fg_remove_sysfs_files(pdev, + &f71869_auto_pwm_attr[0][0], + ARRAY_SIZE(f71869_auto_pwm_attr[0]) * nr_fans); break; case f8000: f71882fg_remove_sysfs_files(pdev, f8000_fan_attr, ARRAY_SIZE(f8000_fan_attr)); f71882fg_remove_sysfs_files(pdev, - f8000_auto_pwm_attr, - ARRAY_SIZE(f8000_auto_pwm_attr)); + &f8000_auto_pwm_attr[0][0], + ARRAY_SIZE(f8000_auto_pwm_attr[0]) * nr_fans); break; - default: /* f71858fg / f71882fg / f71889fg */ + default: f71882fg_remove_sysfs_files(pdev, &fxxxx_auto_pwm_attr[0][0], ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans); } } - - kfree(data); - return 0; } -static int __init f71882fg_find(int sioaddr, unsigned short *address, - struct f71882fg_sio_data *sio_data) +static int __init f71882fg_find(int sioaddr, struct f71882fg_sio_data *sio_data) { - int err = -ENODEV; u16 devid; - - superio_enter(sioaddr); + unsigned short address; + int err = superio_enter(sioaddr); + if (err) + return err; devid = superio_inw(sioaddr, SIO_REG_MANID); if (devid != SIO_FINTEK_ID) { - pr_debug(DRVNAME ": Not a Fintek device\n"); + pr_debug("Not a Fintek device\n"); + err = -ENODEV; goto exit; } devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID); switch (devid) { + case SIO_F71808E_ID: + sio_data->type = f71808e; + break; + case SIO_F71808A_ID: + sio_data->type = f71808a; + break; case SIO_F71858_ID: sio_data->type = f71858fg; break; case SIO_F71862_ID: sio_data->type = f71862fg; break; + case SIO_F71869_ID: + sio_data->type = f71869; + break; + case SIO_F71869A_ID: + sio_data->type = f71869a; + break; case SIO_F71882_ID: sio_data->type = f71882fg; break; case SIO_F71889_ID: sio_data->type = f71889fg; break; + case SIO_F71889E_ID: + sio_data->type = f71889ed; + break; + case SIO_F71889A_ID: + sio_data->type = f71889a; + break; case SIO_F8000_ID: sio_data->type = f8000; break; + case SIO_F81865_ID: + sio_data->type = f81865f; + break; default: - printk(KERN_INFO DRVNAME ": Unsupported Fintek device: %04x\n", - (unsigned int)devid); + pr_info("Unsupported Fintek device: %04x\n", + (unsigned int)devid); + err = -ENODEV; goto exit; } @@ -2146,29 +2589,30 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address, superio_select(sioaddr, SIO_F71882FG_LD_HWM); if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) { - printk(KERN_WARNING DRVNAME ": Device not activated\n"); + pr_warn("Device not activated\n"); + err = -ENODEV; goto exit; } - *address = superio_inw(sioaddr, SIO_REG_ADDR); - if (*address == 0) - { - printk(KERN_WARNING DRVNAME ": Base address not set\n"); + address = superio_inw(sioaddr, SIO_REG_ADDR); + if (address == 0) { + pr_warn("Base address not set\n"); + err = -ENODEV; goto exit; } - *address &= ~(REGION_LENGTH - 1); /* Ignore 3 LSB */ + address &= ~(REGION_LENGTH - 1); /* Ignore 3 LSB */ - err = 0; - printk(KERN_INFO DRVNAME ": Found %s chip at %#x, revision %d\n", - f71882fg_names[sio_data->type], (unsigned int)*address, + err = address; + pr_info("Found %s chip at %#x, revision %d\n", + f71882fg_names[sio_data->type], (unsigned int)address, (int)superio_inb(sioaddr, SIO_REG_DEVREV)); exit: superio_exit(sioaddr); return err; } -static int __init f71882fg_device_add(unsigned short address, - const struct f71882fg_sio_data *sio_data) +static int __init f71882fg_device_add(int address, + const struct f71882fg_sio_data *sio_data) { struct resource res = { .start = address, @@ -2188,20 +2632,20 @@ static int __init f71882fg_device_add(unsigned short address, err = platform_device_add_resources(f71882fg_pdev, &res, 1); if (err) { - printk(KERN_ERR DRVNAME ": Device resource addition failed\n"); + pr_err("Device resource addition failed\n"); goto exit_device_put; } err = platform_device_add_data(f71882fg_pdev, sio_data, sizeof(struct f71882fg_sio_data)); if (err) { - printk(KERN_ERR DRVNAME ": Platform data allocation failed\n"); + pr_err("Platform data allocation failed\n"); goto exit_device_put; } err = platform_device_add(f71882fg_pdev); if (err) { - printk(KERN_ERR DRVNAME ": Device addition failed\n"); + pr_err("Device addition failed\n"); goto exit_device_put; } @@ -2215,19 +2659,21 @@ exit_device_put: static int __init f71882fg_init(void) { - int err = -ENODEV; - unsigned short address; + int err; + int address; struct f71882fg_sio_data sio_data; memset(&sio_data, 0, sizeof(sio_data)); - if (f71882fg_find(0x2e, &address, &sio_data) && - f71882fg_find(0x4e, &address, &sio_data)) - goto exit; + address = f71882fg_find(0x2e, &sio_data); + if (address < 0) + address = f71882fg_find(0x4e, &sio_data); + if (address < 0) + return address; err = platform_driver_register(&f71882fg_driver); if (err) - goto exit; + return err; err = f71882fg_device_add(address, &sio_data); if (err) @@ -2237,7 +2683,6 @@ static int __init f71882fg_init(void) exit_driver: platform_driver_unregister(&f71882fg_driver); -exit: return err; } @@ -2248,7 +2693,7 @@ static void __exit f71882fg_exit(void) } MODULE_DESCRIPTION("F71882FG Hardware Monitoring Driver"); -MODULE_AUTHOR("Hans Edgington, Hans de Goede (hdegoede@redhat.com)"); +MODULE_AUTHOR("Hans Edgington, Hans de Goede <hdegoede@redhat.com>"); MODULE_LICENSE("GPL"); module_init(f71882fg_init); diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index 277398f9c93..80c42bea90e 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -1,15 +1,18 @@ /* - * f75375s.c - driver for the Fintek F75375/SP and F75373 - * hardware monitoring features + * f75375s.c - driver for the Fintek F75375/SP, F75373 and + * F75387SG/RG hardware monitoring features * Copyright (C) 2006-2007 Riku Voipio * * 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 + * + * f75387: + * http://www.fintek.com.tw/files/productfiles/F75387_V027P.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 @@ -35,11 +38,12 @@ #include <linux/err.h> #include <linux/mutex.h> #include <linux/f75375s.h> +#include <linux/slab.h> /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x2d, 0x2e, I2C_CLIENT_END }; -enum chips { f75373, f75375 }; +enum chips { f75373, f75375, f75387 }; /* Fintek F75375 registers */ #define F75375_REG_CONFIG0 0x0 @@ -58,6 +62,7 @@ enum chips { f75373, f75375 }; #define F75375_REG_VOLT_LOW(nr) (0x21 + (nr) * 2) #define F75375_REG_TEMP(nr) (0x14 + (nr)) +#define F75387_REG_TEMP11_LSB(nr) (0x1a + (nr)) #define F75375_REG_TEMP_HIGH(nr) (0x28 + (nr) * 2) #define F75375_REG_TEMP_HYST(nr) (0x29 + (nr) * 2) @@ -77,8 +82,11 @@ enum chips { f75373, f75375 }; #define F75375_REG_PWM1_DROP_DUTY 0x6B #define F75375_REG_PWM2_DROP_DUTY 0x6C -#define FAN_CTRL_LINEAR(nr) (4 + nr) -#define FAN_CTRL_MODE(nr) (5 + ((nr) * 2)) +#define F75375_FAN_CTRL_LINEAR(nr) (4 + nr) +#define F75387_FAN_CTRL_LINEAR(nr) (1 + ((nr) * 4)) +#define FAN_CTRL_MODE(nr) (4 + ((nr) * 2)) +#define F75387_FAN_DUTY_MODE(nr) (2 + ((nr) * 4)) +#define F75387_FAN_MANU_MODE(nr) ((nr) * 4) /* * Data structures and manipulation thereof @@ -101,13 +109,18 @@ struct f75375_data { u8 in_min[4]; u16 fan[2]; u16 fan_min[2]; - u16 fan_full[2]; - u16 fan_exp[2]; + u16 fan_max[2]; + u16 fan_target[2]; u8 fan_timer; u8 pwm[2]; u8 pwm_mode[2]; u8 pwm_enable[2]; - s8 temp[2]; + /* + * f75387: For remote temperature reading, it uses signed 11-bit + * values with LSB = 0.125 degree Celsius, left-justified in 16-bit + * registers. For original 8-bit temp readings, the LSB just is 0. + */ + s16 temp11[2]; s8 temp_high[2]; s8 temp_max_hyst[2]; }; @@ -121,6 +134,7 @@ static int f75375_remove(struct i2c_client *client); static const struct i2c_device_id f75375_id[] = { { "f75373", f75373 }, { "f75375", f75375 }, + { "f75387", f75387 }, { } }; MODULE_DEVICE_TABLE(i2c, f75375_id); @@ -145,8 +159,8 @@ static inline int f75375_read8(struct i2c_client *client, u8 reg) /* in most cases, should be called while holding update_lock */ static inline u16 f75375_read16(struct i2c_client *client, u8 reg) { - return ((i2c_smbus_read_byte_data(client, reg) << 8) - | i2c_smbus_read_byte_data(client, reg + 1)); + return (i2c_smbus_read_byte_data(client, reg) << 8) + | i2c_smbus_read_byte_data(client, reg + 1); } static inline void f75375_write8(struct i2c_client *client, u8 reg, @@ -158,12 +172,22 @@ static inline void f75375_write8(struct i2c_client *client, u8 reg, static inline void f75375_write16(struct i2c_client *client, u8 reg, u16 value) { - int err = i2c_smbus_write_byte_data(client, reg, (value << 8)); + int err = i2c_smbus_write_byte_data(client, reg, (value >> 8)); if (err) return; i2c_smbus_write_byte_data(client, reg + 1, (value & 0xFF)); } +static void f75375_write_pwm(struct i2c_client *client, int nr) +{ + struct f75375_data *data = i2c_get_clientdata(client); + if (data->kind == f75387) + f75375_write16(client, F75375_REG_FAN_EXP(nr), data->pwm[nr]); + else + f75375_write8(client, F75375_REG_FAN_PWM_DUTY(nr), + data->pwm[nr]); +} + static struct f75375_data *f75375_update_device(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -180,15 +204,12 @@ static struct f75375_data *f75375_update_device(struct device *dev) f75375_read8(client, F75375_REG_TEMP_HIGH(nr)); data->temp_max_hyst[nr] = f75375_read8(client, F75375_REG_TEMP_HYST(nr)); - data->fan_full[nr] = + data->fan_max[nr] = f75375_read16(client, F75375_REG_FAN_FULL(nr)); data->fan_min[nr] = f75375_read16(client, F75375_REG_FAN_MIN(nr)); - data->fan_exp[nr] = + data->fan_target[nr] = f75375_read16(client, F75375_REG_FAN_EXP(nr)); - data->pwm[nr] = f75375_read8(client, - F75375_REG_FAN_PWM_DUTY(nr)); - } for (nr = 0; nr < 4; nr++) { data->in_max[nr] = @@ -204,8 +225,16 @@ static struct f75375_data *f75375_update_device(struct device *dev) if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) { for (nr = 0; nr < 2; nr++) { - data->temp[nr] = - f75375_read8(client, F75375_REG_TEMP(nr)); + data->pwm[nr] = f75375_read8(client, + F75375_REG_FAN_PWM_DUTY(nr)); + /* assign MSB, therefore shift it by 8 bits */ + data->temp11[nr] = + f75375_read8(client, F75375_REG_TEMP(nr)) << 8; + if (data->kind == f75387) + /* merge F75387's temperature LSB (11-bit) */ + data->temp11[nr] |= + f75375_read8(client, + F75387_REG_TEMP11_LSB(nr)); data->fan[nr] = f75375_read16(client, F75375_REG_FAN(nr)); } @@ -225,14 +254,46 @@ static inline u16 rpm_from_reg(u16 reg) { if (reg == 0 || reg == 0xffff) return 0; - return (1500000 / reg); + return 1500000 / reg; } static inline u16 rpm_to_reg(int rpm) { if (rpm < 367 || rpm > 0xffff) return 0xffff; - return (1500000 / rpm); + return 1500000 / rpm; +} + +static bool duty_mode_enabled(u8 pwm_enable) +{ + switch (pwm_enable) { + case 0: /* Manual, duty mode (full speed) */ + case 1: /* Manual, duty mode */ + case 4: /* Auto, duty mode */ + return true; + case 2: /* Auto, speed mode */ + case 3: /* Manual, speed mode */ + return false; + default: + WARN(1, "Unexpected pwm_enable value %d\n", pwm_enable); + return true; + } +} + +static bool auto_mode_enabled(u8 pwm_enable) +{ + switch (pwm_enable) { + case 0: /* Manual, duty mode (full speed) */ + case 1: /* Manual, duty mode */ + case 3: /* Manual, speed mode */ + return false; + case 2: /* Auto, speed mode */ + case 4: /* Auto, duty mode */ + return true; + default: + WARN(1, "Unexpected pwm_enable value %d\n", pwm_enable); + return false; + } } static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, @@ -241,7 +302,12 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct f75375_data *data = i2c_get_clientdata(client); - int val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; mutex_lock(&data->update_lock); data->fan_min[nr] = rpm_to_reg(val); @@ -250,17 +316,27 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, return count; } -static ssize_t set_fan_exp(struct device *dev, struct device_attribute *attr, +static ssize_t set_fan_target(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct f75375_data *data = i2c_get_clientdata(client); - int val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + if (auto_mode_enabled(data->pwm_enable[nr])) + return -EINVAL; + if (data->kind == f75387 && duty_mode_enabled(data->pwm_enable[nr])) + return -EINVAL; mutex_lock(&data->update_lock); - data->fan_exp[nr] = rpm_to_reg(val); - f75375_write16(client, F75375_REG_FAN_EXP(nr), data->fan_exp[nr]); + data->fan_target[nr] = rpm_to_reg(val); + f75375_write16(client, F75375_REG_FAN_EXP(nr), data->fan_target[nr]); mutex_unlock(&data->update_lock); return count; } @@ -271,11 +347,20 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct f75375_data *data = i2c_get_clientdata(client); - int val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + if (auto_mode_enabled(data->pwm_enable[nr]) || + !duty_mode_enabled(data->pwm_enable[nr])) + return -EINVAL; mutex_lock(&data->update_lock); - data->pwm[nr] = SENSORS_LIMIT(val, 0, 255); - f75375_write8(client, F75375_REG_FAN_PWM_DUTY(nr), data->pwm[nr]); + data->pwm[nr] = clamp_val(val, 0, 255); + f75375_write_pwm(client, nr); mutex_unlock(&data->update_lock); return count; } @@ -297,26 +382,58 @@ static int set_pwm_enable_direct(struct i2c_client *client, int nr, int val) return -EINVAL; fanmode = f75375_read8(client, F75375_REG_FAN_TIMER); - fanmode = ~(3 << FAN_CTRL_MODE(nr)); - - switch (val) { - case 0: /* Full speed */ - fanmode |= (3 << FAN_CTRL_MODE(nr)); - data->pwm[nr] = 255; - f75375_write8(client, F75375_REG_FAN_PWM_DUTY(nr), - data->pwm[nr]); - break; - case 1: /* PWM */ - fanmode |= (3 << FAN_CTRL_MODE(nr)); - break; - case 2: /* AUTOMATIC*/ - fanmode |= (2 << FAN_CTRL_MODE(nr)); - break; - case 3: /* fan speed */ - break; + if (data->kind == f75387) { + /* For now, deny dangerous toggling of duty mode */ + if (duty_mode_enabled(data->pwm_enable[nr]) != + duty_mode_enabled(val)) + return -EOPNOTSUPP; + /* clear each fanX_mode bit before setting them properly */ + fanmode &= ~(1 << F75387_FAN_DUTY_MODE(nr)); + fanmode &= ~(1 << F75387_FAN_MANU_MODE(nr)); + switch (val) { + case 0: /* full speed */ + fanmode |= (1 << F75387_FAN_MANU_MODE(nr)); + fanmode |= (1 << F75387_FAN_DUTY_MODE(nr)); + data->pwm[nr] = 255; + break; + case 1: /* PWM */ + fanmode |= (1 << F75387_FAN_MANU_MODE(nr)); + fanmode |= (1 << F75387_FAN_DUTY_MODE(nr)); + break; + case 2: /* Automatic, speed mode */ + break; + case 3: /* fan speed */ + fanmode |= (1 << F75387_FAN_MANU_MODE(nr)); + break; + case 4: /* Automatic, pwm */ + fanmode |= (1 << F75387_FAN_DUTY_MODE(nr)); + break; + } + } else { + /* clear each fanX_mode bit before setting them properly */ + fanmode &= ~(3 << FAN_CTRL_MODE(nr)); + switch (val) { + case 0: /* full speed */ + fanmode |= (3 << FAN_CTRL_MODE(nr)); + data->pwm[nr] = 255; + break; + case 1: /* PWM */ + fanmode |= (3 << FAN_CTRL_MODE(nr)); + break; + case 2: /* AUTOMATIC*/ + fanmode |= (1 << FAN_CTRL_MODE(nr)); + break; + case 3: /* fan speed */ + break; + case 4: /* Automatic pwm */ + return -EINVAL; + } } + f75375_write8(client, F75375_REG_FAN_TIMER, fanmode); data->pwm_enable[nr] = val; + if (val == 0) + f75375_write_pwm(client, nr); return 0; } @@ -326,8 +443,12 @@ static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct f75375_data *data = i2c_get_clientdata(client); - int val = simple_strtoul(buf, NULL, 10); - int err = 0; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; mutex_lock(&data->update_lock); err = set_pwm_enable_direct(client, nr, val); @@ -341,20 +462,39 @@ static ssize_t set_pwm_mode(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct f75375_data *data = i2c_get_clientdata(client); - int val = simple_strtoul(buf, NULL, 10); - u8 conf = 0; + unsigned long val; + int err; + u8 conf; + char reg, ctrl; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; if (!(val == 0 || val == 1)) return -EINVAL; + /* F75373 does not support DC (linear voltage) fan control mode */ + if (data->kind == f75373 && val == 0) + return -EINVAL; + + /* take care for different registers */ + if (data->kind == f75387) { + reg = F75375_REG_FAN_TIMER; + ctrl = F75387_FAN_CTRL_LINEAR(nr); + } else { + reg = F75375_REG_CONFIG1; + ctrl = F75375_FAN_CTRL_LINEAR(nr); + } + mutex_lock(&data->update_lock); - conf = f75375_read8(client, F75375_REG_CONFIG1); - conf = ~(1 << FAN_CTRL_LINEAR(nr)); + conf = f75375_read8(client, reg); + conf &= ~(1 << ctrl); if (val == 0) - conf |= (1 << FAN_CTRL_LINEAR(nr)) ; + conf |= (1 << ctrl); - f75375_write8(client, F75375_REG_CONFIG1, conf); + f75375_write8(client, reg, conf); data->pwm_mode[nr] = val; mutex_unlock(&data->update_lock); return count; @@ -409,8 +549,14 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct f75375_data *data = i2c_get_clientdata(client); - int val = simple_strtoul(buf, NULL, 10); - val = SENSORS_LIMIT(VOLT_TO_REG(val), 0, 0xff); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + val = clamp_val(VOLT_TO_REG(val), 0, 0xff); mutex_lock(&data->update_lock); data->in_max[nr] = val; f75375_write8(client, F75375_REG_VOLT_HIGH(nr), data->in_max[nr]); @@ -424,8 +570,14 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct f75375_data *data = i2c_get_clientdata(client); - int val = simple_strtoul(buf, NULL, 10); - val = SENSORS_LIMIT(VOLT_TO_REG(val), 0, 0xff); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + val = clamp_val(VOLT_TO_REG(val), 0, 0xff); mutex_lock(&data->update_lock); data->in_min[nr] = val; f75375_write8(client, F75375_REG_VOLT_LOW(nr), data->in_min[nr]); @@ -434,13 +586,14 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, } #define TEMP_FROM_REG(val) ((val) * 1000) #define TEMP_TO_REG(val) ((val) / 1000) +#define TEMP11_FROM_REG(reg) ((reg) / 32 * 125) -static ssize_t show_temp(struct device *dev, struct device_attribute *attr, +static ssize_t show_temp11(struct device *dev, struct device_attribute *attr, char *buf) { int nr = to_sensor_dev_attr(attr)->index; struct f75375_data *data = f75375_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr])); + return sprintf(buf, "%d\n", TEMP11_FROM_REG(data->temp11[nr])); } static ssize_t show_temp_max(struct device *dev, struct device_attribute *attr, @@ -465,8 +618,14 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct f75375_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - val = SENSORS_LIMIT(TEMP_TO_REG(val), 0, 127); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + val = clamp_val(TEMP_TO_REG(val), 0, 127); mutex_lock(&data->update_lock); data->temp_high[nr] = val; f75375_write8(client, F75375_REG_TEMP_HIGH(nr), data->temp_high[nr]); @@ -480,8 +639,14 @@ static ssize_t set_temp_max_hyst(struct device *dev, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct f75375_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - val = SENSORS_LIMIT(TEMP_TO_REG(val), 0, 127); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + val = clamp_val(TEMP_TO_REG(val), 0, 127); mutex_lock(&data->update_lock); data->temp_max_hyst[nr] = val; f75375_write8(client, F75375_REG_TEMP_HYST(nr), @@ -501,8 +666,8 @@ static ssize_t show_##thing(struct device *dev, struct device_attribute *attr, \ show_fan(fan); show_fan(fan_min); -show_fan(fan_full); -show_fan(fan_exp); +show_fan(fan_max); +show_fan(fan_target); static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0); static SENSOR_DEVICE_ATTR(in0_max, S_IRUGO|S_IWUSR, @@ -524,28 +689,28 @@ static SENSOR_DEVICE_ATTR(in3_max, S_IRUGO|S_IWUSR, show_in_max, set_in_max, 3); static SENSOR_DEVICE_ATTR(in3_min, S_IRUGO|S_IWUSR, show_in_min, set_in_min, 3); -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp11, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, set_temp_max_hyst, 0); static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO|S_IWUSR, show_temp_max, set_temp_max, 0); -static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 1); static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, set_temp_max_hyst, 1); static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO|S_IWUSR, show_temp_max, set_temp_max, 1); static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); -static SENSOR_DEVICE_ATTR(fan1_full, S_IRUGO, show_fan_full, NULL, 0); +static SENSOR_DEVICE_ATTR(fan1_max, S_IRUGO, show_fan_max, NULL, 0); static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO|S_IWUSR, show_fan_min, set_fan_min, 0); -static SENSOR_DEVICE_ATTR(fan1_exp, S_IRUGO|S_IWUSR, - show_fan_exp, set_fan_exp, 0); +static SENSOR_DEVICE_ATTR(fan1_target, S_IRUGO|S_IWUSR, + show_fan_target, set_fan_target, 0); static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); -static SENSOR_DEVICE_ATTR(fan2_full, S_IRUGO, show_fan_full, NULL, 1); +static SENSOR_DEVICE_ATTR(fan2_max, S_IRUGO, show_fan_max, NULL, 1); static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO|S_IWUSR, show_fan_min, set_fan_min, 1); -static SENSOR_DEVICE_ATTR(fan2_exp, S_IRUGO|S_IWUSR, - show_fan_exp, set_fan_exp, 1); +static SENSOR_DEVICE_ATTR(fan2_target, S_IRUGO|S_IWUSR, + show_fan_target, set_fan_target, 1); static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO|S_IWUSR, show_pwm, set_pwm, 0); static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO|S_IWUSR, @@ -567,13 +732,13 @@ static struct attribute *f75375_attributes[] = { &sensor_dev_attr_temp2_max.dev_attr.attr, &sensor_dev_attr_temp2_max_hyst.dev_attr.attr, &sensor_dev_attr_fan1_input.dev_attr.attr, - &sensor_dev_attr_fan1_full.dev_attr.attr, + &sensor_dev_attr_fan1_max.dev_attr.attr, &sensor_dev_attr_fan1_min.dev_attr.attr, - &sensor_dev_attr_fan1_exp.dev_attr.attr, + &sensor_dev_attr_fan1_target.dev_attr.attr, &sensor_dev_attr_fan2_input.dev_attr.attr, - &sensor_dev_attr_fan2_full.dev_attr.attr, + &sensor_dev_attr_fan2_max.dev_attr.attr, &sensor_dev_attr_fan2_min.dev_attr.attr, - &sensor_dev_attr_fan2_exp.dev_attr.attr, + &sensor_dev_attr_fan2_target.dev_attr.attr, &sensor_dev_attr_pwm1.dev_attr.attr, &sensor_dev_attr_pwm1_enable.dev_attr.attr, &sensor_dev_attr_pwm1_mode.dev_attr.attr, @@ -603,12 +768,62 @@ static void f75375_init(struct i2c_client *client, struct f75375_data *data, struct f75375s_platform_data *f75375s_pdata) { int nr; + + if (!f75375s_pdata) { + u8 conf, mode; + int nr; + + conf = f75375_read8(client, F75375_REG_CONFIG1); + mode = f75375_read8(client, F75375_REG_FAN_TIMER); + for (nr = 0; nr < 2; nr++) { + if (data->kind == f75387) { + bool manu, duty; + + if (!(mode & (1 << F75387_FAN_CTRL_LINEAR(nr)))) + data->pwm_mode[nr] = 1; + + manu = ((mode >> F75387_FAN_MANU_MODE(nr)) & 1); + duty = ((mode >> F75387_FAN_DUTY_MODE(nr)) & 1); + if (!manu && duty) + /* auto, pwm */ + data->pwm_enable[nr] = 4; + else if (manu && !duty) + /* manual, speed */ + data->pwm_enable[nr] = 3; + else if (!manu && !duty) + /* automatic, speed */ + data->pwm_enable[nr] = 2; + else + /* manual, pwm */ + data->pwm_enable[nr] = 1; + } else { + if (!(conf & (1 << F75375_FAN_CTRL_LINEAR(nr)))) + data->pwm_mode[nr] = 1; + + switch ((mode >> FAN_CTRL_MODE(nr)) & 3) { + case 0: /* speed */ + data->pwm_enable[nr] = 3; + break; + case 1: /* automatic */ + data->pwm_enable[nr] = 2; + break; + default: /* manual */ + data->pwm_enable[nr] = 1; + break; + } + } + } + return; + } + set_pwm_enable_direct(client, 0, f75375s_pdata->pwm_enable[0]); set_pwm_enable_direct(client, 1, f75375s_pdata->pwm_enable[1]); for (nr = 0; nr < 2; nr++) { - data->pwm[nr] = SENSORS_LIMIT(f75375s_pdata->pwm[nr], 0, 255); - f75375_write8(client, F75375_REG_FAN_PWM_DUTY(nr), - data->pwm[nr]); + if (auto_mode_enabled(f75375s_pdata->pwm_enable[nr]) || + !duty_mode_enabled(f75375s_pdata->pwm_enable[nr])) + continue; + data->pwm[nr] = clamp_val(f75375s_pdata->pwm[nr], 0, 255); + f75375_write_pwm(client, nr); } } @@ -617,23 +832,27 @@ static int f75375_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct f75375_data *data; - struct f75375s_platform_data *f75375s_pdata = client->dev.platform_data; + struct f75375s_platform_data *f75375s_pdata = + dev_get_platdata(&client->dev); int err; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - if (!(data = kzalloc(sizeof(struct f75375_data), GFP_KERNEL))) + data = devm_kzalloc(&client->dev, sizeof(struct f75375_data), + GFP_KERNEL); + if (!data) return -ENOMEM; i2c_set_clientdata(client, data); mutex_init(&data->update_lock); data->kind = id->driver_data; - if ((err = sysfs_create_group(&client->dev.kobj, &f75375_group))) - goto exit_free; + err = sysfs_create_group(&client->dev.kobj, &f75375_group); + if (err) + return err; - if (data->kind == f75375) { + if (data->kind != f75373) { err = sysfs_chmod_file(&client->dev.kobj, &sensor_dev_attr_pwm1_mode.dev_attr.attr, S_IRUGO | S_IWUSR); @@ -652,16 +871,12 @@ static int f75375_probe(struct i2c_client *client, goto exit_remove; } - if (f75375s_pdata != NULL) - f75375_init(client, data, f75375s_pdata); + f75375_init(client, data, f75375s_pdata); return 0; exit_remove: sysfs_remove_group(&client->dev.kobj, &f75375_group); -exit_free: - kfree(data); - i2c_set_clientdata(client, NULL); return err; } @@ -670,8 +885,6 @@ static int f75375_remove(struct i2c_client *client) struct f75375_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &f75375_group); - kfree(data); - i2c_set_clientdata(client, NULL); return 0; } @@ -686,10 +899,15 @@ static int f75375_detect(struct i2c_client *client, vendid = f75375_read16(client, F75375_REG_VENDOR); chipid = f75375_read16(client, F75375_CHIP_ID); - if (chipid == 0x0306 && vendid == 0x1934) + if (vendid != 0x1934) + return -ENODEV; + + if (chipid == 0x0306) name = "f75375"; - else if (chipid == 0x0204 && vendid == 0x1934) + else if (chipid == 0x0204) name = "f75373"; + else if (chipid == 0x0410) + name = "f75387"; else return -ENODEV; @@ -700,19 +918,8 @@ static int f75375_detect(struct i2c_client *client, return 0; } -static int __init sensors_f75375_init(void) -{ - return i2c_add_driver(&f75375_driver); -} - -static void __exit sensors_f75375_exit(void) -{ - i2c_del_driver(&f75375_driver); -} +module_i2c_driver(f75375_driver); MODULE_AUTHOR("Riku Voipio"); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("F75373/F75375 hardware monitoring driver"); - -module_init(sensors_f75375_init); -module_exit(sensors_f75375_exit); +MODULE_DESCRIPTION("F75373/F75375/F75387 hardware monitoring driver"); diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c new file mode 100644 index 00000000000..6040121a405 --- /dev/null +++ b/drivers/hwmon/fam15h_power.c @@ -0,0 +1,267 @@ +/* + * fam15h_power.c - AMD Family 15h processor power monitoring + * + * Copyright (c) 2011 Advanced Micro Devices, Inc. + * Author: Andreas Herrmann <herrmann.der.user@googlemail.com> + * + * + * This driver is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License; either + * version 2 of the License, or (at your option) any later version. + * + * This driver 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 driver; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/bitops.h> +#include <asm/processor.h> + +MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor"); +MODULE_AUTHOR("Andreas Herrmann <herrmann.der.user@googlemail.com>"); +MODULE_LICENSE("GPL"); + +/* Family 16h Northbridge's function 4 PCI ID */ +#define PCI_DEVICE_ID_AMD_16H_NB_F4 0x1534 + +/* D18F3 */ +#define REG_NORTHBRIDGE_CAP 0xe8 + +/* D18F4 */ +#define REG_PROCESSOR_TDP 0x1b8 + +/* D18F5 */ +#define REG_TDP_RUNNING_AVERAGE 0xe0 +#define REG_TDP_LIMIT3 0xe8 + +struct fam15h_power_data { + struct device *hwmon_dev; + unsigned int tdp_to_watts; + unsigned int base_tdp; + unsigned int processor_pwr_watts; +}; + +static ssize_t show_power(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 val, tdp_limit, running_avg_range; + s32 running_avg_capture; + u64 curr_pwr_watts; + struct pci_dev *f4 = to_pci_dev(dev); + struct fam15h_power_data *data = dev_get_drvdata(dev); + + pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5), + REG_TDP_RUNNING_AVERAGE, &val); + running_avg_capture = (val >> 4) & 0x3fffff; + running_avg_capture = sign_extend32(running_avg_capture, 21); + running_avg_range = (val & 0xf) + 1; + + pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5), + REG_TDP_LIMIT3, &val); + + tdp_limit = val >> 16; + curr_pwr_watts = ((u64)(tdp_limit + + data->base_tdp)) << running_avg_range; + curr_pwr_watts -= running_avg_capture; + curr_pwr_watts *= data->tdp_to_watts; + + /* + * Convert to microWatt + * + * power is in Watt provided as fixed point integer with + * scaling factor 1/(2^16). For conversion we use + * (10^6)/(2^16) = 15625/(2^10) + */ + curr_pwr_watts = (curr_pwr_watts * 15625) >> (10 + running_avg_range); + return sprintf(buf, "%u\n", (unsigned int) curr_pwr_watts); +} +static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL); + +static ssize_t show_power_crit(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fam15h_power_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", data->processor_pwr_watts); +} +static DEVICE_ATTR(power1_crit, S_IRUGO, show_power_crit, NULL); + +static ssize_t show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "fam15h_power\n"); +} +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static struct attribute *fam15h_power_attrs[] = { + &dev_attr_power1_input.attr, + &dev_attr_power1_crit.attr, + &dev_attr_name.attr, + NULL +}; + +static const struct attribute_group fam15h_power_attr_group = { + .attrs = fam15h_power_attrs, +}; + +static bool fam15h_power_is_internal_node0(struct pci_dev *f4) +{ + u32 val; + + pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3), + REG_NORTHBRIDGE_CAP, &val); + if ((val & BIT(29)) && ((val >> 30) & 3)) + return false; + + return true; +} + +/* + * Newer BKDG versions have an updated recommendation on how to properly + * initialize the running average range (was: 0xE, now: 0x9). This avoids + * counter saturations resulting in bogus power readings. + * We correct this value ourselves to cope with older BIOSes. + */ +static const struct pci_device_id affected_device[] = { + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) }, + { 0 } +}; + +static void tweak_runavg_range(struct pci_dev *pdev) +{ + u32 val; + + /* + * let this quirk apply only to the current version of the + * northbridge, since future versions may change the behavior + */ + if (!pci_match_id(affected_device, pdev)) + return; + + pci_bus_read_config_dword(pdev->bus, + PCI_DEVFN(PCI_SLOT(pdev->devfn), 5), + REG_TDP_RUNNING_AVERAGE, &val); + if ((val & 0xf) != 0xe) + return; + + val &= ~0xf; + val |= 0x9; + pci_bus_write_config_dword(pdev->bus, + PCI_DEVFN(PCI_SLOT(pdev->devfn), 5), + REG_TDP_RUNNING_AVERAGE, val); +} + +#ifdef CONFIG_PM +static int fam15h_power_resume(struct pci_dev *pdev) +{ + tweak_runavg_range(pdev); + return 0; +} +#else +#define fam15h_power_resume NULL +#endif + +static void fam15h_power_init_data(struct pci_dev *f4, + struct fam15h_power_data *data) +{ + u32 val; + u64 tmp; + + pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val); + data->base_tdp = val >> 16; + tmp = val & 0xffff; + + pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5), + REG_TDP_LIMIT3, &val); + + data->tdp_to_watts = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f); + tmp *= data->tdp_to_watts; + + /* result not allowed to be >= 256W */ + if ((tmp >> 16) >= 256) + dev_warn(&f4->dev, + "Bogus value for ProcessorPwrWatts (processor_pwr_watts>=%u)\n", + (unsigned int) (tmp >> 16)); + + /* convert to microWatt */ + data->processor_pwr_watts = (tmp * 15625) >> 10; +} + +static int fam15h_power_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct fam15h_power_data *data; + struct device *dev = &pdev->dev; + int err; + + /* + * though we ignore every other northbridge, we still have to + * do the tweaking on _each_ node in MCM processors as the counters + * are working hand-in-hand + */ + tweak_runavg_range(pdev); + + if (!fam15h_power_is_internal_node0(pdev)) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(struct fam15h_power_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + fam15h_power_init_data(pdev, data); + + dev_set_drvdata(dev, data); + err = sysfs_create_group(&dev->kobj, &fam15h_power_attr_group); + if (err) + return err; + + data->hwmon_dev = hwmon_device_register(dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove_group; + } + + return 0; + +exit_remove_group: + sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group); + return err; +} + +static void fam15h_power_remove(struct pci_dev *pdev) +{ + struct device *dev; + struct fam15h_power_data *data; + + dev = &pdev->dev; + data = dev_get_drvdata(dev); + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group); +} + +static const struct pci_device_id fam15h_power_id_table[] = { + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F4) }, + {} +}; +MODULE_DEVICE_TABLE(pci, fam15h_power_id_table); + +static struct pci_driver fam15h_power_driver = { + .name = "fam15h_power", + .id_table = fam15h_power_id_table, + .probe = fam15h_power_probe, + .remove = fam15h_power_remove, + .resume = fam15h_power_resume, +}; + +module_pci_driver(fam15h_power_driver); diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index fa0728232e7..d58abdc5a4c 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -1,4 +1,5 @@ -/* fschmd.c +/* + * fschmd.c * * Copyright (C) 2007 - 2009 Hans de Goede <hdegoede@redhat.com> * @@ -52,8 +53,8 @@ static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; /* Insmod parameters */ -static int nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, int, 0); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -76,12 +77,12 @@ enum chips { fscpos, fscher, fscscy, fschrc, fschmd, fschds, fscsyl }; #define FSCHMD_CONTROL_ALERT_LED 0x01 /* watchdog */ -static const u8 FSCHMD_REG_WDOG_CONTROL[7] = - { 0x21, 0x21, 0x21, 0x21, 0x21, 0x28, 0x28 }; -static const u8 FSCHMD_REG_WDOG_STATE[7] = - { 0x23, 0x23, 0x23, 0x23, 0x23, 0x29, 0x29 }; -static const u8 FSCHMD_REG_WDOG_PRESET[7] = - { 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x2a }; +static const u8 FSCHMD_REG_WDOG_CONTROL[7] = { + 0x21, 0x21, 0x21, 0x21, 0x21, 0x28, 0x28 }; +static const u8 FSCHMD_REG_WDOG_STATE[7] = { + 0x23, 0x23, 0x23, 0x23, 0x23, 0x29, 0x29 }; +static const u8 FSCHMD_REG_WDOG_PRESET[7] = { + 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x2a }; #define FSCHMD_WDOG_CONTROL_TRIGGER 0x10 #define FSCHMD_WDOG_CONTROL_STARTED 0x10 /* the same as trigger */ @@ -103,10 +104,12 @@ static const u8 FSCHMD_REG_VOLT[7][6] = { static const int FSCHMD_NO_VOLT_SENSORS[7] = { 3, 3, 3, 3, 3, 3, 6 }; -/* minimum pwm at which the fan is driven (pwm can by increased depending on - the temp. Notice that for the scy some fans share there minimum speed. - Also notice that with the scy the sensor order is different than with the - other chips, this order was in the 2.4 driver and kept for consistency. */ +/* + * minimum pwm at which the fan is driven (pwm can by increased depending on + * the temp. Notice that for the scy some fans share there minimum speed. + * Also notice that with the scy the sensor order is different than with the + * other chips, this order was in the 2.4 driver and kept for consistency. + */ static const u8 FSCHMD_REG_FAN_MIN[7][7] = { { 0x55, 0x65 }, /* pos */ { 0x55, 0x65, 0xb5 }, /* her */ @@ -182,11 +185,13 @@ static const u8 FSCHMD_REG_TEMP_STATE[7][11] = { 0xb9, 0xc9, 0xd9, 0xe9, 0xf9 }, }; -/* temperature high limit registers, FSC does not document these. Proven to be - there with field testing on the fscher and fschrc, already supported / used - in the fscscy 2.4 driver. FSC has confirmed that the fschmd has registers - at these addresses, but doesn't want to confirm they are the same as with - the fscher?? */ +/* + * temperature high limit registers, FSC does not document these. Proven to be + * there with field testing on the fscher and fschrc, already supported / used + * in the fscscy 2.4 driver. FSC has confirmed that the fschmd has registers + * at these addresses, but doesn't want to confirm they are the same as with + * the fscher?? + */ static const u8 FSCHMD_REG_TEMP_LIMIT[7][11] = { { 0, 0, 0 }, /* pos */ { 0x76, 0x86, 0x96 }, /* her */ @@ -198,13 +203,15 @@ static const u8 FSCHMD_REG_TEMP_LIMIT[7][11] = { 0xba, 0xca, 0xda, 0xea, 0xfa }, }; -/* These were found through experimenting with an fscher, currently they are - not used, but we keep them around for future reference. - On the fscsyl AUTOP1 lives at 0x#c (so 0x5c for fan1, 0x6c for fan2, etc), - AUTOP2 lives at 0x#e, and 0x#1 is a bitmask defining which temps influence - the fan speed. -static const u8 FSCHER_REG_TEMP_AUTOP1[] = { 0x73, 0x83, 0x93 }; -static const u8 FSCHER_REG_TEMP_AUTOP2[] = { 0x75, 0x85, 0x95 }; */ +/* + * These were found through experimenting with an fscher, currently they are + * not used, but we keep them around for future reference. + * On the fscsyl AUTOP1 lives at 0x#c (so 0x5c for fan1, 0x6c for fan2, etc), + * AUTOP2 lives at 0x#e, and 0x#1 is a bitmask defining which temps influence + * the fan speed. + * static const u8 FSCHER_REG_TEMP_AUTOP1[] = { 0x73, 0x83, 0x93 }; + * static const u8 FSCHER_REG_TEMP_AUTOP2[] = { 0x75, 0x85, 0x95 }; + */ static const int FSCHMD_NO_TEMP_SENSORS[7] = { 3, 3, 4, 3, 5, 5, 11 }; @@ -267,7 +274,7 @@ struct fschmd_data { struct list_head list; /* member of the watchdog_data_list */ struct kref kref; struct miscdevice watchdog_miscdev; - int kind; + enum chips kind; unsigned long watchdog_is_open; char watchdog_expect_close; char watchdog_name[10]; /* must be unique to avoid sysfs conflict */ @@ -290,24 +297,30 @@ struct fschmd_data { u8 fan_ripple[7]; /* divider for rps */ }; -/* Global variables to hold information read from special DMI tables, which are - available on FSC machines with an fscher or later chip. There is no need to - protect these with a lock as they are only modified from our attach function - which always gets called with the i2c-core lock held and never accessed - before the attach function is done with them. */ +/* + * Global variables to hold information read from special DMI tables, which are + * available on FSC machines with an fscher or later chip. There is no need to + * protect these with a lock as they are only modified from our attach function + * which always gets called with the i2c-core lock held and never accessed + * before the attach function is done with them. + */ static int dmi_mult[6] = { 490, 200, 100, 100, 200, 100 }; static int dmi_offset[6] = { 0, 0, 0, 0, 0, 0 }; static int dmi_vref = -1; -/* Somewhat ugly :( global data pointer list with all fschmd devices, so that - we can find our device data as when using misc_register there is no other - method to get to ones device data from the open fop. */ +/* + * Somewhat ugly :( global data pointer list with all fschmd devices, so that + * we can find our device data as when using misc_register there is no other + * method to get to ones device data from the open fop. + */ static LIST_HEAD(watchdog_data_list); /* Note this lock not only protect list access, but also data.kref access */ static DEFINE_MUTEX(watchdog_data_mutex); -/* Release our data struct when we're detached from the i2c client *and* all - references to our watchdog device are released */ +/* + * Release our data struct when we're detached from the i2c client *and* all + * references to our watchdog device are released + */ static void fschmd_release_resources(struct kref *ref) { struct fschmd_data *data = container_of(ref, struct fschmd_data, kref); @@ -325,8 +338,7 @@ static ssize_t show_in_value(struct device *dev, int index = to_sensor_dev_attr(devattr)->index; struct fschmd_data *data = fschmd_update_device(dev); - /* fscher / fschrc - 1 as data->kind is an array index, not a chips */ - if (data->kind == (fscher - 1) || data->kind >= (fschrc - 1)) + if (data->kind == fscher || data->kind >= fschrc) return sprintf(buf, "%d\n", (data->volt[index] * dmi_vref * dmi_mult[index]) / 255 + dmi_offset[index]); else @@ -360,9 +372,14 @@ static ssize_t store_temp_max(struct device *dev, struct device_attribute { int index = to_sensor_dev_attr(devattr)->index; struct fschmd_data *data = dev_get_drvdata(dev); - long v = simple_strtol(buf, NULL, 10) / 1000; + long v; + int err; - v = SENSORS_LIMIT(v, -128, 127) + 128; + err = kstrtol(buf, 10, &v); + if (err) + return err; + + v = clamp_val(v / 1000, -128, 127) + 128; mutex_lock(&data->update_lock); i2c_smbus_write_byte_data(to_i2c_client(dev), @@ -428,15 +445,27 @@ static ssize_t store_fan_div(struct device *dev, struct device_attribute int index = to_sensor_dev_attr(devattr)->index; struct fschmd_data *data = dev_get_drvdata(dev); /* supported values: 2, 4, 8 */ - unsigned long v = simple_strtoul(buf, NULL, 10); + unsigned long v; + int err; + + err = kstrtoul(buf, 10, &v); + if (err) + return err; switch (v) { - case 2: v = 1; break; - case 4: v = 2; break; - case 8: v = 3; break; + case 2: + v = 1; + break; + case 4: + v = 2; + break; + case 8: + v = 3; + break; default: - dev_err(dev, "fan_div value %lu not supported. " - "Choose one of 2, 4 or 8!\n", v); + dev_err(dev, + "fan_div value %lu not supported. Choose one of 2, 4 or 8!\n", + v); return -EINVAL; } @@ -492,7 +521,7 @@ static ssize_t show_pwm_auto_point1_pwm(struct device *dev, int val = data->fan_min[index]; /* 0 = allow turning off (except on the syl), 1-255 = 50-100% */ - if (val || data->kind == fscsyl - 1) + if (val || data->kind == fscsyl) val = val / 2 + 128; return sprintf(buf, "%d\n", val); @@ -503,11 +532,16 @@ static ssize_t store_pwm_auto_point1_pwm(struct device *dev, { int index = to_sensor_dev_attr(devattr)->index; struct fschmd_data *data = dev_get_drvdata(dev); - unsigned long v = simple_strtoul(buf, NULL, 10); + unsigned long v; + int err; + + err = kstrtoul(buf, 10, &v); + if (err) + return err; /* reg: 0 = allow turning off (except on the syl), 1-255 = 50-100% */ - if (v || data->kind == fscsyl - 1) { - v = SENSORS_LIMIT(v, 128, 255); + if (v || data->kind == fscsyl) { + v = clamp_val(v, 128, 255); v = (v - 128) * 2 + 1; } @@ -523,8 +557,10 @@ static ssize_t store_pwm_auto_point1_pwm(struct device *dev, } -/* The FSC hwmon family has the ability to force an attached alert led to flash - from software, we export this as an alert_led sysfs attr */ +/* + * The FSC hwmon family has the ability to force an attached alert led to flash + * from software, we export this as an alert_led sysfs attr + */ static ssize_t show_alert_led(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -541,7 +577,12 @@ static ssize_t store_alert_led(struct device *dev, { u8 reg; struct fschmd_data *data = dev_get_drvdata(dev); - unsigned long v = simple_strtoul(buf, NULL, 10); + unsigned long v; + int err; + + err = kstrtoul(buf, 10, &v); + if (err) + return err; mutex_lock(&data->update_lock); @@ -755,8 +796,10 @@ static int watchdog_stop(struct fschmd_data *data) } data->watchdog_control &= ~FSCHMD_WDOG_CONTROL_STARTED; - /* Don't store the stop flag in our watchdog control register copy, as - its a write only bit (read always returns 0) */ + /* + * Don't store the stop flag in our watchdog control register copy, as + * its a write only bit (read always returns 0) + */ i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_CONTROL[data->kind], data->watchdog_control | FSCHMD_WDOG_CONTROL_STOP); @@ -770,10 +813,12 @@ static int watchdog_open(struct inode *inode, struct file *filp) struct fschmd_data *pos, *data = NULL; int watchdog_is_open; - /* We get called from drivers/char/misc.c with misc_mtx hold, and we - call misc_register() from fschmd_probe() with watchdog_data_mutex - hold, as misc_register() takes the misc_mtx lock, this is a possible - deadlock, so we use mutex_trylock here. */ + /* + * We get called from drivers/char/misc.c with misc_mtx hold, and we + * call misc_register() from fschmd_probe() with watchdog_data_mutex + * hold, as misc_register() takes the misc_mtx lock, this is a possible + * deadlock, so we use mutex_trylock here. + */ if (!mutex_trylock(&watchdog_data_mutex)) return -ERESTARTSYS; list_for_each_entry(pos, &watchdog_data_list, list) { @@ -848,10 +893,10 @@ static ssize_t watchdog_write(struct file *filp, const char __user *buf, return count; } -static int watchdog_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) +static long watchdog_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) { - static struct watchdog_info ident = { + struct watchdog_info ident = { .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_CARDRESET, .identity = "FSC watchdog" @@ -915,7 +960,6 @@ static int watchdog_ioctl(struct inode *inode, struct file *filp, default: ret = -ENOTTY; } - return ret; } @@ -925,7 +969,7 @@ static const struct file_operations watchdog_fops = { .open = watchdog_open, .release = watchdog_release, .write = watchdog_write, - .ioctl = watchdog_ioctl, + .unlocked_ioctl = watchdog_ioctl, }; @@ -933,30 +977,38 @@ static const struct file_operations watchdog_fops = { * Detect, register, unregister and update device functions */ -/* DMI decode routine to read voltage scaling factors from special DMI tables, - which are available on FSC machines with an fscher or later chip. */ +/* + * DMI decode routine to read voltage scaling factors from special DMI tables, + * which are available on FSC machines with an fscher or later chip. + */ static void fschmd_dmi_decode(const struct dmi_header *header, void *dummy) { int i, mult[3] = { 0 }, offset[3] = { 0 }, vref = 0, found = 0; - /* dmi code ugliness, we get passed the address of the contents of - a complete DMI record, but in the form of a dmi_header pointer, in - reality this address holds header->length bytes of which the header - are the first 4 bytes */ + /* + * dmi code ugliness, we get passed the address of the contents of + * a complete DMI record, but in the form of a dmi_header pointer, in + * reality this address holds header->length bytes of which the header + * are the first 4 bytes + */ u8 *dmi_data = (u8 *)header; /* We are looking for OEM-specific type 185 */ if (header->type != 185) return; - /* we are looking for what Siemens calls "subtype" 19, the subtype - is stored in byte 5 of the dmi block */ + /* + * we are looking for what Siemens calls "subtype" 19, the subtype + * is stored in byte 5 of the dmi block + */ if (header->length < 5 || dmi_data[4] != 19) return; - /* After the subtype comes 1 unknown byte and then blocks of 5 bytes, - consisting of what Siemens calls an "Entity" number, followed by - 2 16-bit words in LSB first order */ + /* + * After the subtype comes 1 unknown byte and then blocks of 5 bytes, + * consisting of what Siemens calls an "Entity" number, followed by + * 2 16-bit words in LSB first order + */ for (i = 6; (i + 4) < header->length; i += 5) { /* entity 1 - 3: voltage multiplier and offset */ if (dmi_data[i] >= 1 && dmi_data[i] <= 3) { @@ -991,9 +1043,11 @@ static void fschmd_dmi_decode(const struct dmi_header *header, void *dummy) dmi_mult[i] = mult[i] * 10; dmi_offset[i] = offset[i] * 10; } - /* According to the docs there should be separate dmi entries - for the mult's and offsets of in3-5 of the syl, but on - my test machine these are not present */ + /* + * According to the docs there should be separate dmi entries + * for the mult's and offsets of in3-5 of the syl, but on + * my test machine these are not present + */ dmi_mult[3] = dmi_mult[2]; dmi_mult[4] = dmi_mult[1]; dmi_mult[5] = dmi_mult[2]; @@ -1037,7 +1091,7 @@ static int fschmd_detect(struct i2c_client *client, else return -ENODEV; - strlcpy(info->type, fschmd_id[kind - 1].name, I2C_NAME_SIZE); + strlcpy(info->type, fschmd_id[kind].name, I2C_NAME_SIZE); return 0; } @@ -1061,14 +1115,19 @@ static int fschmd_probe(struct i2c_client *client, mutex_init(&data->watchdog_lock); INIT_LIST_HEAD(&data->list); kref_init(&data->kref); - /* Store client pointer in our data struct for watchdog usage - (where the client is found through a data ptr instead of the - otherway around) */ + /* + * Store client pointer in our data struct for watchdog usage + * (where the client is found through a data ptr instead of the + * otherway around) + */ data->client = client; + data->kind = kind; if (kind == fscpos) { - /* The Poseidon has hardwired temp limits, fill these - in for the alarm resetting code */ + /* + * The Poseidon has hardwired temp limits, fill these + * in for the alarm resetting code + */ data->temp_max[0] = 70 + 128; data->temp_max[1] = 50 + 128; data->temp_max[2] = 50 + 128; @@ -1085,9 +1144,6 @@ static int fschmd_probe(struct i2c_client *client, } } - /* i2c kind goes from 1-6, we want from 0-5 to address arrays */ - data->kind = kind - 1; - /* Read in some never changing registers */ data->revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION); data->global_control = i2c_smbus_read_byte_data(client, @@ -1162,9 +1218,11 @@ static int fschmd_probe(struct i2c_client *client, goto exit_detach; } - /* We take the data_mutex lock early so that watchdog_open() cannot - run when misc_register() has completed, but we've not yet added - our data to the watchdog_data_list (and set the default timeout) */ + /* + * We take the data_mutex lock early so that watchdog_open() cannot + * run when misc_register() has completed, but we've not yet added + * our data to the watchdog_data_list (and set the default timeout) + */ mutex_lock(&watchdog_data_mutex); for (i = 0; i < ARRAY_SIZE(watchdog_minors); i++) { /* Register our watchdog part */ @@ -1192,8 +1250,8 @@ static int fschmd_probe(struct i2c_client *client, } if (i == ARRAY_SIZE(watchdog_minors)) { data->watchdog_miscdev.minor = 0; - dev_warn(&client->dev, "Couldn't register watchdog chardev " - "(due to no free minor)\n"); + dev_warn(&client->dev, + "Couldn't register watchdog chardev (due to no free minor)\n"); } mutex_unlock(&watchdog_data_mutex); @@ -1230,8 +1288,10 @@ static int fschmd_remove(struct i2c_client *client) mutex_unlock(&data->watchdog_lock); } - /* Check if registered in case we're called from fschmd_detect - to cleanup after an error */ + /* + * Check if registered in case we're called from fschmd_detect + * to cleanup after an error + */ if (data->hwmon_dev) hwmon_device_unregister(data->hwmon_dev); @@ -1274,8 +1334,10 @@ static struct fschmd_data *fschmd_update_device(struct device *dev) client, FSCHMD_REG_TEMP_LIMIT[data->kind][i]); - /* reset alarm if the alarm condition is gone, - the chip doesn't do this itself */ + /* + * reset alarm if the alarm condition is gone, + * the chip doesn't do this itself + */ if ((data->temp_status[i] & FSCHMD_TEMP_ALARM_MASK) == FSCHMD_TEMP_ALARM_MASK && data->temp_act[i] < data->temp_max[i]) @@ -1319,20 +1381,9 @@ static struct fschmd_data *fschmd_update_device(struct device *dev) return data; } -static int __init fschmd_init(void) -{ - return i2c_add_driver(&fschmd_driver); -} - -static void __exit fschmd_exit(void) -{ - i2c_del_driver(&fschmd_driver); -} +module_i2c_driver(fschmd_driver); MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); MODULE_DESCRIPTION("FSC Poseidon, Hermes, Scylla, Heracles, Heimdall, Hades " "and Syleus driver"); MODULE_LICENSE("GPL"); - -module_init(fschmd_init); -module_exit(fschmd_exit); diff --git a/drivers/hwmon/g760a.c b/drivers/hwmon/g760a.c index 19c01a49f6b..ea6480b80e7 100644 --- a/drivers/hwmon/g760a.c +++ b/drivers/hwmon/g760a.c @@ -1,17 +1,17 @@ /* - g760a - Driver for the Global Mixed-mode Technology Inc. G760A - fan speed PWM controller chip - - 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 - - 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. -*/ + * g760a - Driver for the Global Mixed-mode Technology Inc. G760A + * fan speed PWM controller chip + * + * Copyright (C) 2007 Herbert Valerio Riedel <hvr@gnu.org> + * + * Complete datasheet is available at GMT's website: + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ #include <linux/module.h> #include <linux/init.h> @@ -59,7 +59,8 @@ struct g760a_data { u8 act_cnt; /* formula: cnt = (CLK * 30)/(rpm * P) */ u8 fan_sta; /* bit 0: set when actual fan speed more than 20% * outside requested fan speed - * bit 1: set when fan speed below 1920 rpm */ + * bit 1: set when fan speed below 1920 rpm + */ }; #define G760A_DEFAULT_CLK 32768 @@ -68,7 +69,7 @@ struct g760a_data { #define PWM_FROM_CNT(cnt) (0xff-(cnt)) #define PWM_TO_CNT(pwm) (0xff-(pwm)) -unsigned int rpm_from_cnt(u8 val, u32 clk, u16 div) +static inline unsigned int rpm_from_cnt(u8 val, u32 clk, u16 div) { return ((val == 0x00) ? 0 : ((clk*30)/(val*div))); } @@ -99,7 +100,7 @@ static int g760a_write_value(struct i2c_client *client, enum g760a_regs reg, return i2c_smbus_write_byte_data(client, reg, value); } -/**************************************************************************** +/* * sysfs attributes */ @@ -166,11 +167,11 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *da, struct g760a_data *data = g760a_update_client(dev); unsigned long val; - if (strict_strtoul(buf, 10, &val)) + if (kstrtoul(buf, 10, &val)) return -EINVAL; mutex_lock(&data->update_lock); - data->set_cnt = PWM_TO_CNT(SENSORS_LIMIT(val, 0, 255)); + data->set_cnt = PWM_TO_CNT(clamp_val(val, 0, 255)); g760a_write_value(client, G760A_REG_SET_CNT, data->set_cnt); mutex_unlock(&data->update_lock); @@ -192,7 +193,7 @@ static const struct attribute_group g760a_group = { .attrs = g760a_attributes, }; -/**************************************************************************** +/* * new-style driver model code */ @@ -206,7 +207,8 @@ static int g760a_probe(struct i2c_client *client, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - data = kzalloc(sizeof(struct g760a_data), GFP_KERNEL); + data = devm_kzalloc(&client->dev, sizeof(struct g760a_data), + GFP_KERNEL); if (!data) return -ENOMEM; @@ -222,7 +224,7 @@ static int g760a_probe(struct i2c_client *client, /* Register sysfs hooks */ err = sysfs_create_group(&client->dev.kobj, &g760a_group); if (err) - goto error_sysfs_create_group; + return err; data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { @@ -234,10 +236,6 @@ static int g760a_probe(struct i2c_client *client, error_hwmon_device_register: sysfs_remove_group(&client->dev.kobj, &g760a_group); -error_sysfs_create_group: - kfree(data); - i2c_set_clientdata(client, NULL); - return err; } @@ -246,27 +244,11 @@ static int g760a_remove(struct i2c_client *client) struct g760a_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &g760a_group); - kfree(data); - i2c_set_clientdata(client, NULL); - return 0; } -/* module management */ - -static int __init g760a_init(void) -{ - return i2c_add_driver(&g760a_driver); -} - -static void __exit g760a_exit(void) -{ - i2c_del_driver(&g760a_driver); -} +module_i2c_driver(g760a_driver); MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>"); MODULE_DESCRIPTION("GMT G760A driver"); MODULE_LICENSE("GPL"); - -module_init(g760a_init); -module_exit(g760a_exit); diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c new file mode 100644 index 00000000000..98a8618d8fb --- /dev/null +++ b/drivers/hwmon/g762.c @@ -0,0 +1,1149 @@ +/* + * g762 - Driver for the Global Mixed-mode Technology Inc. fan speed + * PWM controller chips from G762 family, i.e. G762 and G763 + * + * Copyright (C) 2013, Arnaud EBALARD <arno@natisbad.org> + * + * This work is based on a basic version for 2.6.31 kernel developed + * by Olivier Mouchet for LaCie. Updates and correction have been + * performed to run on recent kernels. Additional features, like the + * ability to configure various characteristics via .dts file or + * board init file have been added. Detailed datasheet on which this + * development is based is available here: + * + * http://natisbad.org/NAS/refs/GMT_EDS-762_763-080710-0.2.pdf + * + * Headers from previous developments have been kept below: + * + * Copyright (c) 2009 LaCie + * + * Author: Olivier Mouchet <olivier.mouchet@gmail.com> + * + * based on g760a code written by Herbert Valerio Riedel <hvr@gnu.org> + * Copyright (C) 2007 Herbert Valerio Riedel <hvr@gnu.org> + * + * g762: minimal datasheet available at: + * http://www.gmt.com.tw/product/datasheet/EDS-762_3.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. + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_data/g762.h> + +#define DRVNAME "g762" + +static const struct i2c_device_id g762_id[] = { + { "g762", 0 }, + { "g763", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, g762_id); + +enum g762_regs { + G762_REG_SET_CNT = 0x00, + G762_REG_ACT_CNT = 0x01, + G762_REG_FAN_STA = 0x02, + G762_REG_SET_OUT = 0x03, + G762_REG_FAN_CMD1 = 0x04, + G762_REG_FAN_CMD2 = 0x05, +}; + +/* Config register bits */ +#define G762_REG_FAN_CMD1_DET_FAN_FAIL 0x80 /* enable fan_fail signal */ +#define G762_REG_FAN_CMD1_DET_FAN_OOC 0x40 /* enable fan_out_of_control */ +#define G762_REG_FAN_CMD1_OUT_MODE 0x20 /* out mode: PWM or DC */ +#define G762_REG_FAN_CMD1_FAN_MODE 0x10 /* fan mode: closed/open-loop */ +#define G762_REG_FAN_CMD1_CLK_DIV_ID1 0x08 /* clock divisor value */ +#define G762_REG_FAN_CMD1_CLK_DIV_ID0 0x04 +#define G762_REG_FAN_CMD1_PWM_POLARITY 0x02 /* PWM polarity */ +#define G762_REG_FAN_CMD1_PULSE_PER_REV 0x01 /* pulse per fan revolution */ + +#define G762_REG_FAN_CMD2_GEAR_MODE_1 0x08 /* fan gear mode */ +#define G762_REG_FAN_CMD2_GEAR_MODE_0 0x04 +#define G762_REG_FAN_CMD2_FAN_STARTV_1 0x02 /* fan startup voltage */ +#define G762_REG_FAN_CMD2_FAN_STARTV_0 0x01 + +#define G762_REG_FAN_STA_FAIL 0x02 /* fan fail */ +#define G762_REG_FAN_STA_OOC 0x01 /* fan out of control */ + +/* Config register values */ +#define G762_OUT_MODE_PWM 1 +#define G762_OUT_MODE_DC 0 + +#define G762_FAN_MODE_CLOSED_LOOP 2 +#define G762_FAN_MODE_OPEN_LOOP 1 + +#define G762_PWM_POLARITY_NEGATIVE 1 +#define G762_PWM_POLARITY_POSITIVE 0 + +/* Register data is read (and cached) at most once per second. */ +#define G762_UPDATE_INTERVAL HZ + +/* + * Extract pulse count per fan revolution value (2 or 4) from given + * FAN_CMD1 register value. + */ +#define G762_PULSE_FROM_REG(reg) \ + ((((reg) & G762_REG_FAN_CMD1_PULSE_PER_REV) + 1) << 1) + +/* + * Extract fan clock divisor (1, 2, 4 or 8) from given FAN_CMD1 + * register value. + */ +#define G762_CLKDIV_FROM_REG(reg) \ + (1 << (((reg) & (G762_REG_FAN_CMD1_CLK_DIV_ID0 | \ + G762_REG_FAN_CMD1_CLK_DIV_ID1)) >> 2)) + +/* + * Extract fan gear mode multiplier value (0, 2 or 4) from given + * FAN_CMD2 register value. + */ +#define G762_GEARMULT_FROM_REG(reg) \ + (1 << (((reg) & (G762_REG_FAN_CMD2_GEAR_MODE_0 | \ + G762_REG_FAN_CMD2_GEAR_MODE_1)) >> 2)) + +struct g762_data { + struct i2c_client *client; + struct device *hwmon_dev; + struct clk *clk; + + /* update mutex */ + struct mutex update_lock; + + /* board specific parameters. */ + u32 clk_freq; + + /* g762 register cache */ + bool valid; + unsigned long last_updated; /* in jiffies */ + + u8 set_cnt; /* controls fan rotation speed in closed-loop mode */ + u8 act_cnt; /* provides access to current fan RPM value */ + u8 fan_sta; /* bit 0: set when actual fan speed is more than + * 25% outside requested fan speed + * bit 1: set when no transition occurs on fan + * pin for 0.7s + */ + u8 set_out; /* controls fan rotation speed in open-loop mode */ + u8 fan_cmd1; /* 0: FG_PLS_ID0 FG pulses count per revolution + * 0: 2 counts per revolution + * 1: 4 counts per revolution + * 1: PWM_POLARITY 1: negative_duty + * 0: positive_duty + * 2,3: [FG_CLOCK_ID0, FG_CLK_ID1] + * 00: Divide fan clock by 1 + * 01: Divide fan clock by 2 + * 10: Divide fan clock by 4 + * 11: Divide fan clock by 8 + * 4: FAN_MODE 1:closed-loop, 0:open-loop + * 5: OUT_MODE 1:PWM, 0:DC + * 6: DET_FAN_OOC enable "fan ooc" status + * 7: DET_FAN_FAIL enable "fan fail" status + */ + u8 fan_cmd2; /* 0,1: FAN_STARTV 0,1,2,3 -> 0,32,64,96 dac_code + * 2,3: FG_GEAR_MODE + * 00: multiplier = 1 + * 01: multiplier = 2 + * 10: multiplier = 4 + * 4: Mask ALERT# (g763 only) + */ +}; + +/* + * Convert count value from fan controller register (FAN_SET_CNT) into fan + * speed RPM value. Note that the datasheet documents a basic formula; + * influence of additional parameters (fan clock divisor, fan gear mode) + * have been infered from examples in the datasheet and tests. + */ +static inline unsigned int rpm_from_cnt(u8 cnt, u32 clk_freq, u16 p, + u8 clk_div, u8 gear_mult) +{ + if (cnt == 0xff) /* setting cnt to 255 stops the fan */ + return 0; + + return (clk_freq * 30 * gear_mult) / ((cnt ? cnt : 1) * p * clk_div); +} + +/* + * Convert fan RPM value from sysfs into count value for fan controller + * register (FAN_SET_CNT). + */ +static inline unsigned char cnt_from_rpm(u32 rpm, u32 clk_freq, u16 p, + u8 clk_div, u8 gear_mult) +{ + if (!rpm) /* to stop the fan, set cnt to 255 */ + return 0xff; + + return clamp_val(((clk_freq * 30 * gear_mult) / (rpm * p * clk_div)), + 0, 255); +} + +/* helper to grab and cache data, at most one time per second */ +static struct g762_data *g762_update_client(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = i2c_get_clientdata(client); + int ret = 0; + + mutex_lock(&data->update_lock); + if (time_before(jiffies, data->last_updated + G762_UPDATE_INTERVAL) && + likely(data->valid)) + goto out; + + ret = i2c_smbus_read_byte_data(client, G762_REG_SET_CNT); + if (ret < 0) + goto out; + data->set_cnt = ret; + + ret = i2c_smbus_read_byte_data(client, G762_REG_ACT_CNT); + if (ret < 0) + goto out; + data->act_cnt = ret; + + ret = i2c_smbus_read_byte_data(client, G762_REG_FAN_STA); + if (ret < 0) + goto out; + data->fan_sta = ret; + + ret = i2c_smbus_read_byte_data(client, G762_REG_SET_OUT); + if (ret < 0) + goto out; + data->set_out = ret; + + ret = i2c_smbus_read_byte_data(client, G762_REG_FAN_CMD1); + if (ret < 0) + goto out; + data->fan_cmd1 = ret; + + ret = i2c_smbus_read_byte_data(client, G762_REG_FAN_CMD2); + if (ret < 0) + goto out; + data->fan_cmd2 = ret; + + data->last_updated = jiffies; + data->valid = true; + out: + mutex_unlock(&data->update_lock); + + if (ret < 0) /* upon error, encode it in return value */ + data = ERR_PTR(ret); + + return data; +} + +/* helpers for writing hardware parameters */ + +/* + * Set input clock frequency received on CLK pin of the chip. Accepted values + * are between 0 and 0xffffff. If zero is given, then default frequency + * (32,768Hz) is used. Note that clock frequency is a characteristic of the + * system but an internal parameter, i.e. value is not passed to the device. + */ +static int do_set_clk_freq(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = i2c_get_clientdata(client); + + if (val > 0xffffff) + return -EINVAL; + if (!val) + val = 32768; + + data->clk_freq = val; + + return 0; +} + +/* Set pwm mode. Accepts either 0 (PWM mode) or 1 (DC mode) */ +static int do_set_pwm_mode(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = g762_update_client(dev); + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + switch (val) { + case G762_OUT_MODE_PWM: + data->fan_cmd1 |= G762_REG_FAN_CMD1_OUT_MODE; + break; + case G762_OUT_MODE_DC: + data->fan_cmd1 &= ~G762_REG_FAN_CMD1_OUT_MODE; + break; + default: + ret = -EINVAL; + goto out; + } + ret = i2c_smbus_write_byte_data(client, G762_REG_FAN_CMD1, + data->fan_cmd1); + data->valid = false; + out: + mutex_unlock(&data->update_lock); + + return ret; +} + +/* Set fan clock divisor. Accepts either 1, 2, 4 or 8. */ +static int do_set_fan_div(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = g762_update_client(dev); + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + switch (val) { + case 1: + data->fan_cmd1 &= ~G762_REG_FAN_CMD1_CLK_DIV_ID0; + data->fan_cmd1 &= ~G762_REG_FAN_CMD1_CLK_DIV_ID1; + break; + case 2: + data->fan_cmd1 |= G762_REG_FAN_CMD1_CLK_DIV_ID0; + data->fan_cmd1 &= ~G762_REG_FAN_CMD1_CLK_DIV_ID1; + break; + case 4: + data->fan_cmd1 &= ~G762_REG_FAN_CMD1_CLK_DIV_ID0; + data->fan_cmd1 |= G762_REG_FAN_CMD1_CLK_DIV_ID1; + break; + case 8: + data->fan_cmd1 |= G762_REG_FAN_CMD1_CLK_DIV_ID0; + data->fan_cmd1 |= G762_REG_FAN_CMD1_CLK_DIV_ID1; + break; + default: + ret = -EINVAL; + goto out; + } + ret = i2c_smbus_write_byte_data(client, G762_REG_FAN_CMD1, + data->fan_cmd1); + data->valid = false; + out: + mutex_unlock(&data->update_lock); + + return ret; +} + +/* Set fan gear mode. Accepts either 0, 1 or 2. */ +static int do_set_fan_gear_mode(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = g762_update_client(dev); + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + switch (val) { + case 0: + data->fan_cmd2 &= ~G762_REG_FAN_CMD2_GEAR_MODE_0; + data->fan_cmd2 &= ~G762_REG_FAN_CMD2_GEAR_MODE_1; + break; + case 1: + data->fan_cmd2 |= G762_REG_FAN_CMD2_GEAR_MODE_0; + data->fan_cmd2 &= ~G762_REG_FAN_CMD2_GEAR_MODE_1; + break; + case 2: + data->fan_cmd2 &= ~G762_REG_FAN_CMD2_GEAR_MODE_0; + data->fan_cmd2 |= G762_REG_FAN_CMD2_GEAR_MODE_1; + break; + default: + ret = -EINVAL; + goto out; + } + ret = i2c_smbus_write_byte_data(client, G762_REG_FAN_CMD2, + data->fan_cmd2); + data->valid = false; + out: + mutex_unlock(&data->update_lock); + + return ret; +} + +/* Set number of fan pulses per revolution. Accepts either 2 or 4. */ +static int do_set_fan_pulses(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = g762_update_client(dev); + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + switch (val) { + case 2: + data->fan_cmd1 &= ~G762_REG_FAN_CMD1_PULSE_PER_REV; + break; + case 4: + data->fan_cmd1 |= G762_REG_FAN_CMD1_PULSE_PER_REV; + break; + default: + ret = -EINVAL; + goto out; + } + ret = i2c_smbus_write_byte_data(client, G762_REG_FAN_CMD1, + data->fan_cmd1); + data->valid = false; + out: + mutex_unlock(&data->update_lock); + + return ret; +} + +/* Set fan mode. Accepts either 1 (open-loop) or 2 (closed-loop). */ +static int do_set_pwm_enable(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = g762_update_client(dev); + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + switch (val) { + case G762_FAN_MODE_CLOSED_LOOP: + data->fan_cmd1 |= G762_REG_FAN_CMD1_FAN_MODE; + break; + case G762_FAN_MODE_OPEN_LOOP: + data->fan_cmd1 &= ~G762_REG_FAN_CMD1_FAN_MODE; + /* + * BUG FIX: if SET_CNT register value is 255 then, for some + * unknown reason, fan will not rotate as expected, no matter + * the value of SET_OUT (to be specific, this seems to happen + * only in PWM mode). To workaround this bug, we give SET_CNT + * value of 254 if it is 255 when switching to open-loop. + */ + if (data->set_cnt == 0xff) + i2c_smbus_write_byte_data(client, G762_REG_SET_CNT, + 254); + break; + default: + ret = -EINVAL; + goto out; + } + + ret = i2c_smbus_write_byte_data(client, G762_REG_FAN_CMD1, + data->fan_cmd1); + data->valid = false; + out: + mutex_unlock(&data->update_lock); + + return ret; +} + +/* Set PWM polarity. Accepts either 0 (positive duty) or 1 (negative duty) */ +static int do_set_pwm_polarity(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = g762_update_client(dev); + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + switch (val) { + case G762_PWM_POLARITY_POSITIVE: + data->fan_cmd1 &= ~G762_REG_FAN_CMD1_PWM_POLARITY; + break; + case G762_PWM_POLARITY_NEGATIVE: + data->fan_cmd1 |= G762_REG_FAN_CMD1_PWM_POLARITY; + break; + default: + ret = -EINVAL; + goto out; + } + ret = i2c_smbus_write_byte_data(client, G762_REG_FAN_CMD1, + data->fan_cmd1); + data->valid = false; + out: + mutex_unlock(&data->update_lock); + + return ret; +} + +/* + * Set pwm value. Accepts values between 0 (stops the fan) and + * 255 (full speed). This only makes sense in open-loop mode. + */ +static int do_set_pwm(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = i2c_get_clientdata(client); + int ret; + + if (val > 255) + return -EINVAL; + + mutex_lock(&data->update_lock); + ret = i2c_smbus_write_byte_data(client, G762_REG_SET_OUT, val); + data->valid = false; + mutex_unlock(&data->update_lock); + + return ret; +} + +/* + * Set fan RPM value. Can be called both in closed and open-loop mode + * but effect will only be seen after closed-loop mode is configured. + */ +static int do_set_fan_target(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = g762_update_client(dev); + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + data->set_cnt = cnt_from_rpm(val, data->clk_freq, + G762_PULSE_FROM_REG(data->fan_cmd1), + G762_CLKDIV_FROM_REG(data->fan_cmd1), + G762_GEARMULT_FROM_REG(data->fan_cmd2)); + ret = i2c_smbus_write_byte_data(client, G762_REG_SET_CNT, + data->set_cnt); + data->valid = false; + mutex_unlock(&data->update_lock); + + return ret; +} + +/* Set fan startup voltage. Accepted values are either 0, 1, 2 or 3. */ +static int do_set_fan_startv(struct device *dev, unsigned long val) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = g762_update_client(dev); + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + switch (val) { + case 0: + data->fan_cmd2 &= ~G762_REG_FAN_CMD2_FAN_STARTV_0; + data->fan_cmd2 &= ~G762_REG_FAN_CMD2_FAN_STARTV_1; + break; + case 1: + data->fan_cmd2 |= G762_REG_FAN_CMD2_FAN_STARTV_0; + data->fan_cmd2 &= ~G762_REG_FAN_CMD2_FAN_STARTV_1; + break; + case 2: + data->fan_cmd2 &= ~G762_REG_FAN_CMD2_FAN_STARTV_0; + data->fan_cmd2 |= G762_REG_FAN_CMD2_FAN_STARTV_1; + break; + case 3: + data->fan_cmd2 |= G762_REG_FAN_CMD2_FAN_STARTV_0; + data->fan_cmd2 |= G762_REG_FAN_CMD2_FAN_STARTV_1; + break; + default: + ret = -EINVAL; + goto out; + } + ret = i2c_smbus_write_byte_data(client, G762_REG_FAN_CMD2, + data->fan_cmd2); + data->valid = false; + out: + mutex_unlock(&data->update_lock); + + return ret; +} + +/* + * Helper to import hardware characteristics from .dts file and push + * those to the chip. + */ + +#ifdef CONFIG_OF +static const struct of_device_id g762_dt_match[] = { + { .compatible = "gmt,g762" }, + { .compatible = "gmt,g763" }, + { }, +}; + +/* + * Grab clock (a required property), enable it, get (fixed) clock frequency + * and store it. Note: upon success, clock has been prepared and enabled; it + * must later be unprepared and disabled (e.g. during module unloading) by a + * call to g762_of_clock_disable(). Note that a reference to clock is kept + * in our private data structure to be used in this function. + */ +static int g762_of_clock_enable(struct i2c_client *client) +{ + struct g762_data *data; + unsigned long clk_freq; + struct clk *clk; + int ret; + + if (!client->dev.of_node) + return 0; + + clk = of_clk_get(client->dev.of_node, 0); + if (IS_ERR(clk)) { + dev_err(&client->dev, "failed to get clock\n"); + return PTR_ERR(clk); + } + + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(&client->dev, "failed to enable clock\n"); + goto clk_put; + } + + clk_freq = clk_get_rate(clk); + ret = do_set_clk_freq(&client->dev, clk_freq); + if (ret) { + dev_err(&client->dev, "invalid clock freq %lu\n", clk_freq); + goto clk_unprep; + } + + data = i2c_get_clientdata(client); + data->clk = clk; + + return 0; + + clk_unprep: + clk_disable_unprepare(clk); + + clk_put: + clk_put(clk); + + return ret; +} + +static void g762_of_clock_disable(struct i2c_client *client) +{ + struct g762_data *data = i2c_get_clientdata(client); + + if (!data->clk) + return; + + clk_disable_unprepare(data->clk); + clk_put(data->clk); +} + +static int g762_of_prop_import_one(struct i2c_client *client, + const char *pname, + int (*psetter)(struct device *dev, + unsigned long val)) +{ + const __be32 *prop; + int len, ret; + u32 pval; + + prop = of_get_property(client->dev.of_node, pname, &len); + if (!prop || len != sizeof(u32)) + return 0; + + pval = be32_to_cpu(prop[0]); + dev_dbg(&client->dev, "found %s (%d)\n", pname, pval); + ret = (*psetter)(&client->dev, pval); + if (ret) + dev_err(&client->dev, "unable to set %s (%d)\n", pname, pval); + + return ret; +} + +static int g762_of_prop_import(struct i2c_client *client) +{ + int ret; + + if (!client->dev.of_node) + return 0; + + ret = g762_of_prop_import_one(client, "fan_gear_mode", + do_set_fan_gear_mode); + if (ret) + return ret; + + ret = g762_of_prop_import_one(client, "pwm_polarity", + do_set_pwm_polarity); + if (ret) + return ret; + + return g762_of_prop_import_one(client, "fan_startv", + do_set_fan_startv); +} + +#else +static int g762_of_prop_import(struct i2c_client *client) +{ + return 0; +} + +static int g762_of_clock_enable(struct i2c_client *client) +{ + return 0; +} + +static void g762_of_clock_disable(struct i2c_client *client) { } +#endif + +/* + * Helper to import hardware characteristics from .dts file and push + * those to the chip. + */ + +static int g762_pdata_prop_import(struct i2c_client *client) +{ + struct g762_platform_data *pdata = dev_get_platdata(&client->dev); + int ret; + + if (!pdata) + return 0; + + ret = do_set_fan_gear_mode(&client->dev, pdata->fan_gear_mode); + if (ret) + return ret; + + ret = do_set_pwm_polarity(&client->dev, pdata->pwm_polarity); + if (ret) + return ret; + + ret = do_set_fan_startv(&client->dev, pdata->fan_startv); + if (ret) + return ret; + + return do_set_clk_freq(&client->dev, pdata->clk_freq); +} + +/* + * sysfs attributes + */ + +/* + * Read function for fan1_input sysfs file. Return current fan RPM value, or + * 0 if fan is out of control. + */ +static ssize_t get_fan_rpm(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct g762_data *data = g762_update_client(dev); + unsigned int rpm = 0; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + /* reverse logic: fan out of control reporting is enabled low */ + if (data->fan_sta & G762_REG_FAN_STA_OOC) { + rpm = rpm_from_cnt(data->act_cnt, data->clk_freq, + G762_PULSE_FROM_REG(data->fan_cmd1), + G762_CLKDIV_FROM_REG(data->fan_cmd1), + G762_GEARMULT_FROM_REG(data->fan_cmd2)); + } + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%u\n", rpm); +} + +/* + * Read and write functions for pwm1_mode sysfs file. Get and set fan speed + * control mode i.e. PWM (1) or DC (0). + */ +static ssize_t get_pwm_mode(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct g762_data *data = g762_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", + !!(data->fan_cmd1 & G762_REG_FAN_CMD1_OUT_MODE)); +} + +static ssize_t set_pwm_mode(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + ret = do_set_pwm_mode(dev, val); + if (ret < 0) + return ret; + + return count; +} + +/* + * Read and write functions for fan1_div sysfs file. Get and set fan + * controller prescaler value + */ +static ssize_t get_fan_div(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct g762_data *data = g762_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", G762_CLKDIV_FROM_REG(data->fan_cmd1)); +} + +static ssize_t set_fan_div(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + ret = do_set_fan_div(dev, val); + if (ret < 0) + return ret; + + return count; +} + +/* + * Read and write functions for fan1_pulses sysfs file. Get and set number + * of tachometer pulses per fan revolution. + */ +static ssize_t get_fan_pulses(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct g762_data *data = g762_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", G762_PULSE_FROM_REG(data->fan_cmd1)); +} + +static ssize_t set_fan_pulses(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + ret = do_set_fan_pulses(dev, val); + if (ret < 0) + return ret; + + return count; +} + +/* + * Read and write functions for pwm1_enable. Get and set fan speed control mode + * (i.e. closed or open-loop). + * + * Following documentation about hwmon's sysfs interface, a pwm1_enable node + * should accept followings: + * + * 0 : no fan speed control (i.e. fan at full speed) + * 1 : manual fan speed control enabled (use pwm[1-*]) (open-loop) + * 2+: automatic fan speed control enabled (use fan[1-*]_target) (closed-loop) + * + * but we do not accept 0 as this mode is not natively supported by the chip + * and it is not emulated by g762 driver. -EINVAL is returned in this case. + */ +static ssize_t get_pwm_enable(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct g762_data *data = g762_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", + (!!(data->fan_cmd1 & G762_REG_FAN_CMD1_FAN_MODE)) + 1); +} + +static ssize_t set_pwm_enable(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + ret = do_set_pwm_enable(dev, val); + if (ret < 0) + return ret; + + return count; +} + +/* + * Read and write functions for pwm1 sysfs file. Get and set pwm value + * (which affects fan speed) in open-loop mode. 0 stops the fan and 255 + * makes it run at full speed. + */ +static ssize_t get_pwm(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct g762_data *data = g762_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", data->set_out); +} + +static ssize_t set_pwm(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + ret = do_set_pwm(dev, val); + if (ret < 0) + return ret; + + return count; +} + +/* + * Read and write function for fan1_target sysfs file. Get/set the fan speed in + * closed-loop mode. Speed is given as a RPM value; then the chip will regulate + * the fan speed using pulses from fan tachometer. + * + * Refer to rpm_from_cnt() implementation above to get info about count number + * calculation. + * + * Also note that due to rounding errors it is possible that you don't read + * back exactly the value you have set. + */ +static ssize_t get_fan_target(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct g762_data *data = g762_update_client(dev); + unsigned int rpm; + + if (IS_ERR(data)) + return PTR_ERR(data); + + mutex_lock(&data->update_lock); + rpm = rpm_from_cnt(data->set_cnt, data->clk_freq, + G762_PULSE_FROM_REG(data->fan_cmd1), + G762_CLKDIV_FROM_REG(data->fan_cmd1), + G762_GEARMULT_FROM_REG(data->fan_cmd2)); + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%u\n", rpm); +} + +static ssize_t set_fan_target(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + ret = do_set_fan_target(dev, val); + if (ret < 0) + return ret; + + return count; +} + +/* read function for fan1_fault sysfs file. */ +static ssize_t get_fan_failure(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct g762_data *data = g762_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%u\n", !!(data->fan_sta & G762_REG_FAN_STA_FAIL)); +} + +/* + * read function for fan1_alarm sysfs file. Note that OOC condition is + * enabled low + */ +static ssize_t get_fan_ooc(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct g762_data *data = g762_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%u\n", !(data->fan_sta & G762_REG_FAN_STA_OOC)); +} + +static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm); +static DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, get_pwm_mode, set_pwm_mode); +static DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + get_pwm_enable, set_pwm_enable); +static DEVICE_ATTR(fan1_input, S_IRUGO, get_fan_rpm, NULL); +static DEVICE_ATTR(fan1_alarm, S_IRUGO, get_fan_ooc, NULL); +static DEVICE_ATTR(fan1_fault, S_IRUGO, get_fan_failure, NULL); +static DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, + get_fan_target, set_fan_target); +static DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, get_fan_div, set_fan_div); +static DEVICE_ATTR(fan1_pulses, S_IWUSR | S_IRUGO, + get_fan_pulses, set_fan_pulses); + +/* Driver data */ +static struct attribute *g762_attributes[] = { + &dev_attr_fan1_input.attr, + &dev_attr_fan1_alarm.attr, + &dev_attr_fan1_fault.attr, + &dev_attr_fan1_target.attr, + &dev_attr_fan1_div.attr, + &dev_attr_fan1_pulses.attr, + &dev_attr_pwm1.attr, + &dev_attr_pwm1_mode.attr, + &dev_attr_pwm1_enable.attr, + NULL +}; + +static const struct attribute_group g762_group = { + .attrs = g762_attributes, +}; + +/* + * Enable both fan failure detection and fan out of control protection. The + * function does not protect change/access to data structure; it must thus + * only be called during initialization. + */ +static inline int g762_fan_init(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct g762_data *data = g762_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + data->fan_cmd1 |= G762_REG_FAN_CMD1_DET_FAN_FAIL; + data->fan_cmd1 |= G762_REG_FAN_CMD1_DET_FAN_OOC; + data->valid = false; + + return i2c_smbus_write_byte_data(client, G762_REG_FAN_CMD1, + data->fan_cmd1); +} + +static int g762_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct g762_data *data; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + data = devm_kzalloc(&client->dev, sizeof(struct g762_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + data->client = client; + mutex_init(&data->update_lock); + + /* Enable fan failure detection and fan out of control protection */ + ret = g762_fan_init(&client->dev); + if (ret) + return ret; + + /* Get configuration via DT ... */ + ret = g762_of_clock_enable(client); + if (ret) + return ret; + ret = g762_of_prop_import(client); + if (ret) + goto clock_dis; + /* ... or platform_data */ + ret = g762_pdata_prop_import(client); + if (ret) + goto clock_dis; + + /* Register sysfs hooks */ + ret = sysfs_create_group(&client->dev.kobj, &g762_group); + if (ret) + goto clock_dis; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + goto sysfs_rem; + } + + return 0; + + sysfs_rem: + sysfs_remove_group(&client->dev.kobj, &g762_group); + + clock_dis: + g762_of_clock_disable(client); + + return ret; +} + +static int g762_remove(struct i2c_client *client) +{ + struct g762_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &g762_group); + g762_of_clock_disable(client); + + return 0; +} + +static struct i2c_driver g762_driver = { + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(g762_dt_match), + }, + .probe = g762_probe, + .remove = g762_remove, + .id_table = g762_id, +}; + +module_i2c_driver(g762_driver); + +MODULE_AUTHOR("Arnaud EBALARD <arno@natisbad.org>"); +MODULE_DESCRIPTION("GMT G762/G763 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c index e7ae5743e18..1e983051304 100644 --- a/drivers/hwmon/gl518sm.c +++ b/drivers/hwmon/gl518sm.c @@ -4,7 +4,7 @@ * Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and * Kyosti Malkki <kmalkki@cc.hut.fi> * Copyright (C) 2004 Hong-Gunn Chew <hglinux@gunnet.org> and - * Jean Delvare <khali@linux-fr.org> + * Jean Delvare <jdelvare@suse.de> * * 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 @@ -83,11 +83,12 @@ enum chips { gl518sm_r00, gl518sm_r80 }; #define RAW_FROM_REG(val) val -#define BOOL_FROM_REG(val) ((val)?0:1) -#define BOOL_TO_REG(val) ((val)?0:1) +#define BOOL_FROM_REG(val) ((val) ? 0 : 1) +#define BOOL_TO_REG(val) ((val) ? 0 : 1) -#define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0? \ - (val)-500:(val)+500)/1000)+119),0,255)) +#define TEMP_TO_REG(val) clamp_val(((((val) < 0 ? \ + (val) - 500 : \ + (val) + 500) / 1000) + 119), 0, 255) #define TEMP_FROM_REG(val) (((val) - 119) * 1000) static inline u8 FAN_TO_REG(long rpm, int div) @@ -95,16 +96,16 @@ static inline u8 FAN_TO_REG(long rpm, int div) long rpmdiv; if (rpm == 0) return 0; - rpmdiv = SENSORS_LIMIT(rpm, 1, 960000) * div; - return SENSORS_LIMIT((480000 + rpmdiv / 2) / rpmdiv, 1, 255); + rpmdiv = clamp_val(rpm, 1, 960000) * div; + return clamp_val((480000 + rpmdiv / 2) / rpmdiv, 1, 255); } -#define FAN_FROM_REG(val,div) ((val)==0 ? 0 : (480000/((val)*(div)))) +#define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : (480000 / ((val) * (div)))) -#define IN_TO_REG(val) (SENSORS_LIMIT((((val)+9)/19),0,255)) -#define IN_FROM_REG(val) ((val)*19) +#define IN_TO_REG(val) clamp_val((((val) + 9) / 19), 0, 255) +#define IN_FROM_REG(val) ((val) * 19) -#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*4+47)/95),0,255)) -#define VDD_FROM_REG(val) (((val)*95+2)/4) +#define VDD_TO_REG(val) clamp_val((((val) * 4 + 47) / 95), 0, 255) +#define VDD_FROM_REG(val) (((val) * 95 + 2) / 4) #define DIV_FROM_REG(val) (1 << (val)) @@ -169,7 +170,8 @@ static struct i2c_driver gl518_driver = { */ #define show(type, suffix, value) \ -static ssize_t show_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \ +static ssize_t show_##suffix(struct device *dev, \ + struct device_attribute *attr, char *buf) \ { \ struct gl518_data *data = gl518_update_device(dev); \ return sprintf(buf, "%d\n", type##_FROM_REG(data->value)); \ @@ -222,12 +224,16 @@ static ssize_t show_fan_div(struct device *dev, } #define set(type, suffix, value, reg) \ -static ssize_t set_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ +static ssize_t set_##suffix(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ { \ struct i2c_client *client = to_i2c_client(dev); \ struct gl518_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ + long val; \ + int err = kstrtol(buf, 10, &val); \ + if (err) \ + return err; \ \ mutex_lock(&data->update_lock); \ data->value = type##_TO_REG(val); \ @@ -237,13 +243,17 @@ static ssize_t set_##suffix(struct device *dev, struct device_attribute *attr, c } #define set_bits(type, suffix, value, reg, mask, shift) \ -static ssize_t set_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ +static ssize_t set_##suffix(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ { \ struct i2c_client *client = to_i2c_client(dev); \ struct gl518_data *data = i2c_get_clientdata(client); \ int regvalue; \ - unsigned long val = simple_strtoul(buf, NULL, 10); \ + unsigned long val; \ + int err = kstrtoul(buf, 10, &val); \ + if (err) \ + return err; \ \ mutex_lock(&data->update_lock); \ regvalue = gl518_read_value(client, reg); \ @@ -280,7 +290,12 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, struct gl518_data *data = i2c_get_clientdata(client); int nr = to_sensor_dev_attr(attr)->index; int regvalue; - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); regvalue = gl518_read_value(client, GL518_REG_FAN_LIMIT); @@ -308,16 +323,30 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, struct gl518_data *data = i2c_get_clientdata(client); int nr = to_sensor_dev_attr(attr)->index; int regvalue; - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; switch (val) { - case 1: val = 0; break; - case 2: val = 1; break; - case 4: val = 2; break; - case 8: val = 3; break; + case 1: + val = 0; + break; + case 2: + val = 1; + break; + case 4: + val = 2; + break; + case 8: + val = 3; + break; default: - dev_err(dev, "Invalid fan clock divider %lu, choose one " - "of 1, 2, 4 or 8\n", val); + dev_err(dev, + "Invalid fan clock divider %lu, choose one of 1, 2, 4 or 8\n", + val); return -EINVAL; } @@ -395,8 +424,12 @@ static ssize_t set_beep(struct device *dev, struct device_attribute *attr, struct gl518_data *data = i2c_get_clientdata(client); int bitnr = to_sensor_dev_attr(attr)->index; unsigned long bit; + int err; + + err = kstrtoul(buf, 10, &bit); + if (err) + return err; - bit = simple_strtoul(buf, NULL, 10); if (bit & ~1) return -EINVAL; @@ -512,11 +545,10 @@ static int gl518_probe(struct i2c_client *client, struct gl518_data *data; int err, revision; - data = kzalloc(sizeof(struct gl518_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&client->dev, sizeof(struct gl518_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); revision = gl518_read_value(client, GL518_REG_REVISION); @@ -528,12 +560,14 @@ static int gl518_probe(struct i2c_client *client, gl518_init_client(client); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&client->dev.kobj, &gl518_group))) - goto exit_free; - if (data->type == gl518sm_r80) - if ((err = sysfs_create_group(&client->dev.kobj, - &gl518_group_r80))) + err = sysfs_create_group(&client->dev.kobj, &gl518_group); + if (err) + return err; + if (data->type == gl518sm_r80) { + err = sysfs_create_group(&client->dev.kobj, &gl518_group_r80); + if (err) goto exit_remove_files; + } data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { @@ -547,15 +581,14 @@ exit_remove_files: sysfs_remove_group(&client->dev.kobj, &gl518_group); if (data->type == gl518sm_r80) sysfs_remove_group(&client->dev.kobj, &gl518_group_r80); -exit_free: - kfree(data); -exit: return err; } -/* Called when we have found a new GL518SM. - Note that we preserve D4:NoFan2 and D2:beep_enable. */ +/* + * Called when we have found a new GL518SM. + * Note that we preserve D4:NoFan2 and D2:beep_enable. + */ static void gl518_init_client(struct i2c_client *client) { /* Make sure we leave D7:Reset untouched */ @@ -581,17 +614,18 @@ static int gl518_remove(struct i2c_client *client) if (data->type == gl518sm_r80) sysfs_remove_group(&client->dev.kobj, &gl518_group_r80); - kfree(data); return 0; } -/* Registers 0x07 to 0x0c are word-sized, others are byte-sized - GL518 uses a high-byte first convention, which is exactly opposite to - the SMBus standard. */ +/* + * Registers 0x07 to 0x0c are word-sized, others are byte-sized + * GL518 uses a high-byte first convention, which is exactly opposite to + * the SMBus standard. + */ static int gl518_read_value(struct i2c_client *client, u8 reg) { if ((reg >= 0x07) && (reg <= 0x0c)) - return swab16(i2c_smbus_read_word_data(client, reg)); + return i2c_smbus_read_word_swapped(client, reg); else return i2c_smbus_read_byte_data(client, reg); } @@ -599,7 +633,7 @@ static int gl518_read_value(struct i2c_client *client, u8 reg) static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value) { if ((reg >= 0x07) && (reg <= 0x0c)) - return i2c_smbus_write_word_data(client, reg, swab16(value)); + return i2c_smbus_write_word_swapped(client, reg, value); else return i2c_smbus_write_byte_data(client, reg, value); } @@ -676,21 +710,10 @@ static struct gl518_data *gl518_update_device(struct device *dev) return data; } -static int __init sensors_gl518sm_init(void) -{ - return i2c_add_driver(&gl518_driver); -} - -static void __exit sensors_gl518sm_exit(void) -{ - i2c_del_driver(&gl518_driver); -} +module_i2c_driver(gl518_driver); MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, " "Kyosti Malkki <kmalkki@cc.hut.fi> and " "Hong-Gunn Chew <hglinux@gunnet.org>"); MODULE_DESCRIPTION("GL518SM driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_gl518sm_init); -module_exit(sensors_gl518sm_exit); diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c index ec588026f0a..ed56e09c3dd 100644 --- a/drivers/hwmon/gl520sm.c +++ b/drivers/hwmon/gl520sm.c @@ -1,25 +1,25 @@ /* - gl520sm.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>, - Kyösti Mälkki <kmalkki@cc.hut.fi> - Copyright (c) 2005 Maarten Deprez <maartendeprez@users.sourceforge.net> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (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. - -*/ + * gl520sm.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>, + * Kyösti Mälkki <kmalkki@cc.hut.fi> + * Copyright (c) 2005 Maarten Deprez <maartendeprez@users.sourceforge.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (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> @@ -41,10 +41,11 @@ MODULE_PARM_DESC(extra_sensor_type, "Type of extra sensor (0=autodetect, 1=tempe /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END }; -/* Many GL520 constants specified below -One of the inputs can be configured as either temp or voltage. -That's why _TEMP2 and _IN4 access the same register -*/ +/* + * Many GL520 constants specified below + * One of the inputs can be configured as either temp or voltage. + * That's why _TEMP2 and _IN4 access the same register + */ /* The GL520 registers */ #define GL520_REG_CHIP_ID 0x00 @@ -142,11 +143,11 @@ static ssize_t get_cpu_vid(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(cpu0_vid, S_IRUGO, get_cpu_vid, NULL); -#define VDD_FROM_REG(val) (((val)*95+2)/4) -#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*4+47)/95),0,255)) +#define VDD_FROM_REG(val) (((val) * 95 + 2) / 4) +#define VDD_TO_REG(val) clamp_val((((val) * 4 + 47) / 95), 0, 255) -#define IN_FROM_REG(val) ((val)*19) -#define IN_TO_REG(val) (SENSORS_LIMIT((((val)+9)/19),0,255)) +#define IN_FROM_REG(val) ((val) * 19) +#define IN_TO_REG(val) clamp_val((((val) + 9) / 19), 0, 255) static ssize_t get_in_input(struct device *dev, struct device_attribute *attr, char *buf) @@ -193,8 +194,13 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, struct i2c_client *client = to_i2c_client(dev); struct gl520_data *data = i2c_get_clientdata(client); int n = to_sensor_dev_attr(attr)->index; - long v = simple_strtol(buf, NULL, 10); u8 r; + long v; + int err; + + err = kstrtol(buf, 10, &v); + if (err) + return err; mutex_lock(&data->update_lock); @@ -222,8 +228,13 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, struct i2c_client *client = to_i2c_client(dev); struct gl520_data *data = i2c_get_clientdata(client); int n = to_sensor_dev_attr(attr)->index; - long v = simple_strtol(buf, NULL, 10); u8 r; + long v; + int err; + + err = kstrtol(buf, 10, &v); + if (err) + return err; if (n == 0) r = VDD_TO_REG(v); @@ -272,8 +283,9 @@ static SENSOR_DEVICE_ATTR(in4_max, S_IRUGO | S_IWUSR, get_in_max, set_in_max, 4); #define DIV_FROM_REG(val) (1 << (val)) -#define FAN_FROM_REG(val,div) ((val)==0 ? 0 : (480000/((val) << (div)))) -#define FAN_TO_REG(val,div) ((val)<=0?0:SENSORS_LIMIT((480000 + ((val) << ((div)-1))) / ((val) << (div)), 1, 255)); +#define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : (480000 / ((val) << (div)))) +#define FAN_TO_REG(val, div) ((val) <= 0 ? 0 : \ + clamp_val((480000 + ((val) << ((div)-1))) / ((val) << (div)), 1, 255)) static ssize_t get_fan_input(struct device *dev, struct device_attribute *attr, char *buf) @@ -317,8 +329,13 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, struct i2c_client *client = to_i2c_client(dev); struct gl520_data *data = i2c_get_clientdata(client); int n = to_sensor_dev_attr(attr)->index; - unsigned long v = simple_strtoul(buf, NULL, 10); u8 r; + unsigned long v; + int err; + + err = kstrtoul(buf, 10, &v); + if (err) + return err; mutex_lock(&data->update_lock); r = FAN_TO_REG(v, data->fan_div[n]); @@ -351,16 +368,30 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, struct i2c_client *client = to_i2c_client(dev); struct gl520_data *data = i2c_get_clientdata(client); int n = to_sensor_dev_attr(attr)->index; - unsigned long v = simple_strtoul(buf, NULL, 10); u8 r; + unsigned long v; + int err; + + err = kstrtoul(buf, 10, &v); + if (err) + return err; switch (v) { - case 1: r = 0; break; - case 2: r = 1; break; - case 4: r = 2; break; - case 8: r = 3; break; + case 1: + r = 0; + break; + case 2: + r = 1; + break; + case 4: + r = 2; + break; + case 8: + r = 3; + break; default: - dev_err(&client->dev, "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n", v); + dev_err(&client->dev, + "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n", v); return -EINVAL; } @@ -385,7 +416,15 @@ static ssize_t set_fan_off(struct device *dev, struct device_attribute *attr, { struct i2c_client *client = to_i2c_client(dev); struct gl520_data *data = i2c_get_clientdata(client); - u8 r = simple_strtoul(buf, NULL, 10)?1:0; + u8 r; + unsigned long v; + int err; + + err = kstrtoul(buf, 10, &v); + if (err) + return err; + + r = (v ? 1 : 0); mutex_lock(&data->update_lock); data->fan_off = r; @@ -410,7 +449,8 @@ static DEVICE_ATTR(fan1_off, S_IRUGO | S_IWUSR, get_fan_off, set_fan_off); #define TEMP_FROM_REG(val) (((val) - 130) * 1000) -#define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0?(val)-500:(val)+500) / 1000)+130),0,255)) +#define TEMP_TO_REG(val) clamp_val(((((val) < 0 ? \ + (val) - 500 : (val) + 500) / 1000) + 130), 0, 255) static ssize_t get_temp_input(struct device *dev, struct device_attribute *attr, char *buf) @@ -430,8 +470,8 @@ static ssize_t get_temp_max(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[n])); } -static ssize_t get_temp_max_hyst(struct device *dev, struct device_attribute - *attr, char *buf) +static ssize_t get_temp_max_hyst(struct device *dev, + struct device_attribute *attr, char *buf) { int n = to_sensor_dev_attr(attr)->index; struct gl520_data *data = gl520_update_device(dev); @@ -445,7 +485,12 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, struct i2c_client *client = to_i2c_client(dev); struct gl520_data *data = i2c_get_clientdata(client); int n = to_sensor_dev_attr(attr)->index; - long v = simple_strtol(buf, NULL, 10); + long v; + int err; + + err = kstrtol(buf, 10, &v); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_max[n] = TEMP_TO_REG(v); @@ -460,7 +505,12 @@ static ssize_t set_temp_max_hyst(struct device *dev, struct device_attribute struct i2c_client *client = to_i2c_client(dev); struct gl520_data *data = i2c_get_clientdata(client); int n = to_sensor_dev_attr(attr)->index; - long v = simple_strtol(buf, NULL, 10); + long v; + int err; + + err = kstrtol(buf, 10, &v); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_max_hyst[n] = TEMP_TO_REG(v); @@ -507,7 +557,15 @@ static ssize_t set_beep_enable(struct device *dev, struct device_attribute { struct i2c_client *client = to_i2c_client(dev); struct gl520_data *data = i2c_get_clientdata(client); - u8 r = simple_strtoul(buf, NULL, 10)?0:1; + u8 r; + unsigned long v; + int err; + + err = kstrtoul(buf, 10, &v); + if (err) + return err; + + r = (v ? 0 : 1); mutex_lock(&data->update_lock); data->beep_enable = !r; @@ -523,7 +581,12 @@ static ssize_t set_beep_mask(struct device *dev, struct device_attribute *attr, { struct i2c_client *client = to_i2c_client(dev); struct gl520_data *data = i2c_get_clientdata(client); - u8 r = simple_strtoul(buf, NULL, 10); + unsigned long r; + int err; + + err = kstrtoul(buf, 10, &r); + if (err) + return err; mutex_lock(&data->update_lock); r &= data->alarm_mask; @@ -575,7 +638,11 @@ static ssize_t set_beep(struct device *dev, struct device_attribute *attr, int bitnr = to_sensor_dev_attr(attr)->index; unsigned long bit; - bit = simple_strtoul(buf, NULL, 10); + int err; + + err = kstrtoul(buf, 10, &bit); + if (err) + return err; if (bit & ~1) return -EINVAL; @@ -652,13 +719,16 @@ static const struct attribute_group gl520_group = { .attrs = gl520_attributes, }; -static struct attribute *gl520_attributes_opt[] = { +static struct attribute *gl520_attributes_in4[] = { &sensor_dev_attr_in4_input.dev_attr.attr, &sensor_dev_attr_in4_min.dev_attr.attr, &sensor_dev_attr_in4_max.dev_attr.attr, &sensor_dev_attr_in4_alarm.dev_attr.attr, &sensor_dev_attr_in4_beep.dev_attr.attr, + NULL +}; +static struct attribute *gl520_attributes_temp2[] = { &sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr, &sensor_dev_attr_temp2_max_hyst.dev_attr.attr, @@ -667,8 +737,12 @@ static struct attribute *gl520_attributes_opt[] = { NULL }; -static const struct attribute_group gl520_group_opt = { - .attrs = gl520_attributes_opt, +static const struct attribute_group gl520_group_in4 = { + .attrs = gl520_attributes_in4, +}; + +static const struct attribute_group gl520_group_temp2 = { + .attrs = gl520_attributes_temp2, }; @@ -704,11 +778,10 @@ static int gl520_probe(struct i2c_client *client, struct gl520_data *data; int err; - data = kzalloc(sizeof(struct gl520_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&client->dev, sizeof(struct gl520_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); mutex_init(&data->update_lock); @@ -717,35 +790,17 @@ static int gl520_probe(struct i2c_client *client, gl520_init_client(client); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&client->dev.kobj, &gl520_group))) - goto exit_free; - - if (data->two_temps) { - if ((err = device_create_file(&client->dev, - &sensor_dev_attr_temp2_input.dev_attr)) - || (err = device_create_file(&client->dev, - &sensor_dev_attr_temp2_max.dev_attr)) - || (err = device_create_file(&client->dev, - &sensor_dev_attr_temp2_max_hyst.dev_attr)) - || (err = device_create_file(&client->dev, - &sensor_dev_attr_temp2_alarm.dev_attr)) - || (err = device_create_file(&client->dev, - &sensor_dev_attr_temp2_beep.dev_attr))) - goto exit_remove_files; - } else { - if ((err = device_create_file(&client->dev, - &sensor_dev_attr_in4_input.dev_attr)) - || (err = device_create_file(&client->dev, - &sensor_dev_attr_in4_min.dev_attr)) - || (err = device_create_file(&client->dev, - &sensor_dev_attr_in4_max.dev_attr)) - || (err = device_create_file(&client->dev, - &sensor_dev_attr_in4_alarm.dev_attr)) - || (err = device_create_file(&client->dev, - &sensor_dev_attr_in4_beep.dev_attr))) - goto exit_remove_files; - } + err = sysfs_create_group(&client->dev.kobj, &gl520_group); + if (err) + return err; + if (data->two_temps) + err = sysfs_create_group(&client->dev.kobj, &gl520_group_temp2); + else + err = sysfs_create_group(&client->dev.kobj, &gl520_group_in4); + + if (err) + goto exit_remove_files; data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { @@ -757,10 +812,8 @@ static int gl520_probe(struct i2c_client *client, exit_remove_files: sysfs_remove_group(&client->dev.kobj, &gl520_group); - sysfs_remove_group(&client->dev.kobj, &gl520_group_opt); -exit_free: - kfree(data); -exit: + sysfs_remove_group(&client->dev.kobj, &gl520_group_in4); + sysfs_remove_group(&client->dev.kobj, &gl520_group_temp2); return err; } @@ -809,19 +862,21 @@ static int gl520_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &gl520_group); - sysfs_remove_group(&client->dev.kobj, &gl520_group_opt); + sysfs_remove_group(&client->dev.kobj, &gl520_group_in4); + sysfs_remove_group(&client->dev.kobj, &gl520_group_temp2); - kfree(data); return 0; } -/* Registers 0x07 to 0x0c are word-sized, others are byte-sized - GL520 uses a high-byte first convention */ +/* + * Registers 0x07 to 0x0c are word-sized, others are byte-sized + * GL520 uses a high-byte first convention + */ static int gl520_read_value(struct i2c_client *client, u8 reg) { if ((reg >= 0x07) && (reg <= 0x0c)) - return swab16(i2c_smbus_read_word_data(client, reg)); + return i2c_smbus_read_word_swapped(client, reg); else return i2c_smbus_read_byte_data(client, reg); } @@ -829,7 +884,7 @@ static int gl520_read_value(struct i2c_client *client, u8 reg) static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value) { if ((reg >= 0x07) && (reg <= 0x0c)) - return i2c_smbus_write_word_data(client, reg, swab16(value)); + return i2c_smbus_write_word_swapped(client, reg, value); else return i2c_smbus_write_byte_data(client, reg, value); } @@ -849,7 +904,8 @@ static struct gl520_data *gl520_update_device(struct device *dev) data->alarms = gl520_read_value(client, GL520_REG_ALARMS); data->beep_mask = gl520_read_value(client, GL520_REG_BEEP_MASK); - data->vid = gl520_read_value(client, GL520_REG_VID_INPUT) & 0x1f; + data->vid = gl520_read_value(client, + GL520_REG_VID_INPUT) & 0x1f; for (i = 0; i < 4; i++) { data->in_input[i] = gl520_read_value(client, @@ -910,23 +966,10 @@ static struct gl520_data *gl520_update_device(struct device *dev) return data; } - -static int __init sensors_gl520sm_init(void) -{ - return i2c_add_driver(&gl520_driver); -} - -static void __exit sensors_gl520sm_exit(void) -{ - i2c_del_driver(&gl520_driver); -} - +module_i2c_driver(gl520_driver); MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, " "Kyösti Mälkki <kmalkki@cc.hut.fi>, " "Maarten Deprez <maartendeprez@users.sourceforge.net>"); MODULE_DESCRIPTION("GL520SM driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_gl520sm_init); -module_exit(sensors_gl520sm_exit); diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c new file mode 100644 index 00000000000..2566c43dd1e --- /dev/null +++ b/drivers/hwmon/gpio-fan.c @@ -0,0 +1,606 @@ +/* + * 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> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.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_SLEEP + 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 = devm_gpio_request(&pdev->dev, alarm->gpio, "GPIO fan alarm"); + if (err) + return err; + + err = gpio_direction_input(alarm->gpio); + if (err) + return err; + + /* + * 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); + irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH); + err = devm_request_irq(&pdev->dev, alarm_irq, fan_alarm_irq_handler, + IRQF_SHARED, "GPIO fan alarm", fan_data); + return err; +} + +/* + * 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 -ENODEV; +} + +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 (kstrtoul(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 (kstrtoul(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 (kstrtoul(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 umode_t gpio_fan_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct gpio_fan_data *data = dev_get_drvdata(dev); + + if (index == 0 && !data->alarm) + return 0; + if (index > 0 && !data->ctrl) + return 0; + + return attr->mode; +} + +static struct attribute *gpio_fan_attributes[] = { + &dev_attr_fan1_alarm.attr, /* 0 */ + &dev_attr_pwm1.attr, /* 1 */ + &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_group = { + .attrs = gpio_fan_attributes, + .is_visible = gpio_fan_is_visible, +}; + +static const struct attribute_group *gpio_fan_groups[] = { + &gpio_fan_group, + NULL +}; + +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 = devm_gpio_request(&pdev->dev, ctrl[i], + "GPIO fan control"); + if (err) + return err; + + err = gpio_direction_output(ctrl[i], gpio_get_value(ctrl[i])); + if (err) + return err; + } + + 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) + return fan_data->speed_index; + + return 0; +} + +#ifdef CONFIG_OF_GPIO +/* + * Translate OpenFirmware node properties into platform_data + */ +static int gpio_fan_get_of_pdata(struct device *dev, + struct gpio_fan_platform_data *pdata) +{ + struct device_node *node; + struct gpio_fan_speed *speed; + unsigned *ctrl; + unsigned i; + u32 u; + struct property *prop; + const __be32 *p; + + node = dev->of_node; + + /* Fill GPIO pin array */ + pdata->num_ctrl = of_gpio_count(node); + if (pdata->num_ctrl <= 0) { + dev_err(dev, "gpios DT property empty / missing"); + return -ENODEV; + } + ctrl = devm_kzalloc(dev, pdata->num_ctrl * sizeof(unsigned), + GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + for (i = 0; i < pdata->num_ctrl; i++) { + int val; + + val = of_get_gpio(node, i); + if (val < 0) + return val; + ctrl[i] = val; + } + pdata->ctrl = ctrl; + + /* Get number of RPM/ctrl_val pairs in speed map */ + prop = of_find_property(node, "gpio-fan,speed-map", &i); + if (!prop) { + dev_err(dev, "gpio-fan,speed-map DT property missing"); + return -ENODEV; + } + i = i / sizeof(u32); + if (i == 0 || i & 1) { + dev_err(dev, "gpio-fan,speed-map contains zero/odd number of entries"); + return -ENODEV; + } + pdata->num_speed = i / 2; + + /* + * Populate speed map + * Speed map is in the form <RPM ctrl_val RPM ctrl_val ...> + * this needs splitting into pairs to create gpio_fan_speed structs + */ + speed = devm_kzalloc(dev, + pdata->num_speed * sizeof(struct gpio_fan_speed), + GFP_KERNEL); + if (!speed) + return -ENOMEM; + p = NULL; + for (i = 0; i < pdata->num_speed; i++) { + p = of_prop_next_u32(prop, p, &u); + if (!p) + return -ENODEV; + speed[i].rpm = u; + p = of_prop_next_u32(prop, p, &u); + if (!p) + return -ENODEV; + speed[i].ctrl_val = u; + } + pdata->speed = speed; + + /* Alarm GPIO if one exists */ + if (of_gpio_named_count(node, "alarm-gpios") > 0) { + struct gpio_fan_alarm *alarm; + int val; + enum of_gpio_flags flags; + + alarm = devm_kzalloc(dev, sizeof(struct gpio_fan_alarm), + GFP_KERNEL); + if (!alarm) + return -ENOMEM; + + val = of_get_named_gpio_flags(node, "alarm-gpios", 0, &flags); + if (val < 0) + return val; + alarm->gpio = val; + alarm->active_low = flags & OF_GPIO_ACTIVE_LOW; + + pdata->alarm = alarm; + } + + return 0; +} + +static const struct of_device_id of_gpio_fan_match[] = { + { .compatible = "gpio-fan", }, + {}, +}; +#endif /* CONFIG_OF_GPIO */ + +static int gpio_fan_probe(struct platform_device *pdev) +{ + int err; + struct gpio_fan_data *fan_data; + struct gpio_fan_platform_data *pdata = dev_get_platdata(&pdev->dev); + +#ifdef CONFIG_OF_GPIO + if (!pdata) { + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct gpio_fan_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + err = gpio_fan_get_of_pdata(&pdev->dev, pdata); + if (err) + return err; + } +#else /* CONFIG_OF_GPIO */ + if (!pdata) + return -EINVAL; +#endif /* CONFIG_OF_GPIO */ + + fan_data = devm_kzalloc(&pdev->dev, 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) + return err; + } + + /* Configure control GPIOs if available. */ + if (pdata->ctrl && pdata->num_ctrl > 0) { + if (!pdata->speed || pdata->num_speed <= 1) + return -EINVAL; + err = fan_ctrl_init(fan_data, pdata); + if (err) + return err; + } + + /* Make this driver part of hwmon class. */ + fan_data->hwmon_dev = hwmon_device_register_with_groups(&pdev->dev, + "gpio_fan", fan_data, + gpio_fan_groups); + if (IS_ERR(fan_data->hwmon_dev)) + return PTR_ERR(fan_data->hwmon_dev); + + dev_info(&pdev->dev, "GPIO fan initialized\n"); + + return 0; +} + +static int gpio_fan_remove(struct platform_device *pdev) +{ + struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); + + hwmon_device_unregister(fan_data->hwmon_dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int gpio_fan_suspend(struct device *dev) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + + 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 device *dev) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(dev); + + if (fan_data->ctrl) + set_fan_speed(fan_data, fan_data->resume_speed); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume); +#define GPIO_FAN_PM (&gpio_fan_pm) +#else +#define GPIO_FAN_PM NULL +#endif + +static struct platform_driver gpio_fan_driver = { + .probe = gpio_fan_probe, + .remove = gpio_fan_remove, + .driver = { + .name = "gpio-fan", + .pm = GPIO_FAN_PM, +#ifdef CONFIG_OF_GPIO + .of_match_table = of_match_ptr(of_gpio_fan_match), +#endif + }, +}; + +module_platform_driver(gpio_fan_driver); + +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 be2d131e405..00000000000 --- a/drivers/hwmon/hdaps.c +++ /dev/null @@ -1,636 +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 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/hih6130.c b/drivers/hwmon/hih6130.c new file mode 100644 index 00000000000..7d68a08baaa --- /dev/null +++ b/drivers/hwmon/hih6130.c @@ -0,0 +1,306 @@ +/* Honeywell HIH-6130/HIH-6131 humidity and temperature sensor driver + * + * Copyright (C) 2012 Iain Paton <ipaton0@gmail.com> + * + * heavily based on the sht21 driver + * Copyright (C) 2010 Urs Fleisch <urs.fleisch@sensirion.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., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA + * + * Data sheets available (2012-06-22) at + * http://sensing.honeywell.com/index.php?ci_id=3106&la_id=1&defId=44872 + */ + +#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/device.h> +#include <linux/delay.h> +#include <linux/jiffies.h> + +/** + * struct hih6130 - HIH-6130 device specific data + * @hwmon_dev: device registered with hwmon + * @lock: mutex to protect measurement values + * @valid: only false before first measurement is taken + * @last_update: time of last update (jiffies) + * @temperature: cached temperature measurement value + * @humidity: cached humidity measurement value + * @write_length: length for I2C measurement request + */ +struct hih6130 { + struct device *hwmon_dev; + struct mutex lock; + bool valid; + unsigned long last_update; + int temperature; + int humidity; + size_t write_length; +}; + +/** + * hih6130_temp_ticks_to_millicelsius() - convert raw temperature ticks to + * milli celsius + * @ticks: temperature ticks value received from sensor + */ +static inline int hih6130_temp_ticks_to_millicelsius(int ticks) +{ + + ticks = ticks >> 2; + /* + * from data sheet section 5.0 + * Formula T = ( ticks / ( 2^14 - 2 ) ) * 165 -40 + */ + return (DIV_ROUND_CLOSEST(ticks * 1650, 16382) - 400) * 100; +} + +/** + * hih6130_rh_ticks_to_per_cent_mille() - convert raw humidity ticks to + * one-thousandths of a percent relative humidity + * @ticks: humidity ticks value received from sensor + */ +static inline int hih6130_rh_ticks_to_per_cent_mille(int ticks) +{ + + ticks &= ~0xC000; /* clear status bits */ + /* + * from data sheet section 4.0 + * Formula RH = ( ticks / ( 2^14 -2 ) ) * 100 + */ + return DIV_ROUND_CLOSEST(ticks * 1000, 16382) * 100; +} + +/** + * hih6130_update_measurements() - get updated measurements from device + * @client: I2C client device + * + * Returns 0 on success, else negative errno. + */ +static int hih6130_update_measurements(struct i2c_client *client) +{ + int ret = 0; + int t; + struct hih6130 *hih6130 = i2c_get_clientdata(client); + unsigned char tmp[4]; + struct i2c_msg msgs[1] = { + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = 4, + .buf = tmp, + } + }; + + mutex_lock(&hih6130->lock); + + /* + * While the measurement can be completed in ~40ms the sensor takes + * much longer to react to a change in external conditions. How quickly + * it reacts depends on airflow and other factors outwith our control. + * The datasheet specifies maximum 'Response time' for humidity at 8s + * and temperature at 30s under specified conditions. + * We therefore choose to only read the sensor at most once per second. + * This trades off pointless activity polling the sensor much faster + * than it can react against better response times in conditions more + * favourable than specified in the datasheet. + */ + if (time_after(jiffies, hih6130->last_update + HZ) || !hih6130->valid) { + + /* + * Write to slave address to request a measurement. + * According with the datasheet it should be with no data, but + * for systems with I2C bus drivers that do not allow zero + * length packets we write one dummy byte to allow sensor + * measurements on them. + */ + tmp[0] = 0; + ret = i2c_master_send(client, tmp, hih6130->write_length); + if (ret < 0) + goto out; + + /* measurement cycle time is ~36.65msec */ + msleep(40); + + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + goto out; + + if ((tmp[0] & 0xC0) != 0) { + dev_err(&client->dev, "Error while reading measurement result\n"); + ret = -EIO; + goto out; + } + + t = (tmp[0] << 8) + tmp[1]; + hih6130->humidity = hih6130_rh_ticks_to_per_cent_mille(t); + + t = (tmp[2] << 8) + tmp[3]; + hih6130->temperature = hih6130_temp_ticks_to_millicelsius(t); + + hih6130->last_update = jiffies; + hih6130->valid = true; + } +out: + mutex_unlock(&hih6130->lock); + + return ret >= 0 ? 0 : ret; +} + +/** + * hih6130_show_temperature() - show temperature measurement value in sysfs + * @dev: device + * @attr: device attribute + * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to + * + * Will be called on read access to temp1_input sysfs attribute. + * Returns number of bytes written into buffer, negative errno on error. + */ +static ssize_t hih6130_show_temperature(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct hih6130 *hih6130 = i2c_get_clientdata(client); + int ret = hih6130_update_measurements(client); + if (ret < 0) + return ret; + return sprintf(buf, "%d\n", hih6130->temperature); +} + +/** + * hih6130_show_humidity() - show humidity measurement value in sysfs + * @dev: device + * @attr: device attribute + * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to + * + * Will be called on read access to humidity1_input sysfs attribute. + * Returns number of bytes written into buffer, negative errno on error. + */ +static ssize_t hih6130_show_humidity(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct hih6130 *hih6130 = i2c_get_clientdata(client); + int ret = hih6130_update_measurements(client); + if (ret < 0) + return ret; + return sprintf(buf, "%d\n", hih6130->humidity); +} + +/* sysfs attributes */ +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, hih6130_show_temperature, + NULL, 0); +static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO, hih6130_show_humidity, + NULL, 0); + +static struct attribute *hih6130_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_humidity1_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group hih6130_attr_group = { + .attrs = hih6130_attributes, +}; + +/** + * hih6130_probe() - probe device + * @client: I2C client device + * @id: device ID + * + * Called by the I2C core when an entry in the ID table matches a + * device's name. + * Returns 0 on success. + */ +static int hih6130_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct hih6130 *hih6130; + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "adapter does not support true I2C\n"); + return -ENODEV; + } + + hih6130 = devm_kzalloc(&client->dev, sizeof(*hih6130), GFP_KERNEL); + if (!hih6130) + return -ENOMEM; + + i2c_set_clientdata(client, hih6130); + + mutex_init(&hih6130->lock); + + err = sysfs_create_group(&client->dev.kobj, &hih6130_attr_group); + if (err) { + dev_dbg(&client->dev, "could not create sysfs files\n"); + return err; + } + + hih6130->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(hih6130->hwmon_dev)) { + dev_dbg(&client->dev, "unable to register hwmon device\n"); + err = PTR_ERR(hih6130->hwmon_dev); + goto fail_remove_sysfs; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_QUICK)) + hih6130->write_length = 1; + + return 0; + +fail_remove_sysfs: + sysfs_remove_group(&client->dev.kobj, &hih6130_attr_group); + return err; +} + +/** + * hih6130_remove() - remove device + * @client: I2C client device + */ +static int hih6130_remove(struct i2c_client *client) +{ + struct hih6130 *hih6130 = i2c_get_clientdata(client); + + hwmon_device_unregister(hih6130->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &hih6130_attr_group); + + return 0; +} + +/* Device ID table */ +static const struct i2c_device_id hih6130_id[] = { + { "hih6130", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, hih6130_id); + +static struct i2c_driver hih6130_driver = { + .driver.name = "hih6130", + .probe = hih6130_probe, + .remove = hih6130_remove, + .id_table = hih6130_id, +}; + +module_i2c_driver(hih6130_driver); + +MODULE_AUTHOR("Iain Paton <ipaton0@gmail.com>"); +MODULE_DESCRIPTION("Honeywell HIH-6130 humidity and temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c deleted file mode 100644 index be475e844c2..00000000000 --- a/drivers/hwmon/hp_accel.c +++ /dev/null @@ -1,392 +0,0 @@ -/* - * hp_accel.c - Interface between LIS3LV02DL driver and HP ACPI BIOS - * - * Copyright (C) 2007-2008 Yan Burman - * Copyright (C) 2008 Eric Piel - * Copyright (C) 2008-2009 Pavel Machek - * - * 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/kernel.h> -#include <linux/init.h> -#include <linux/dmi.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/wait.h> -#include <linux/poll.h> -#include <linux/freezer.h> -#include <linux/uaccess.h> -#include <linux/leds.h> -#include <acpi/acpi_drivers.h> -#include <asm/atomic.h> -#include "lis3lv02d.h" - -#define DRIVER_NAME "lis3lv02d" -#define ACPI_MDPS_CLASS "accelerometer" - -/* Delayed LEDs infrastructure ------------------------------------ */ - -/* Special LED class that can defer work */ -struct delayed_led_classdev { - struct led_classdev led_classdev; - struct work_struct work; - enum led_brightness new_brightness; - - unsigned int led; /* For driver */ - void (*set_brightness)(struct delayed_led_classdev *data, enum led_brightness value); -}; - -static inline void delayed_set_status_worker(struct work_struct *work) -{ - struct delayed_led_classdev *data = - container_of(work, struct delayed_led_classdev, work); - - data->set_brightness(data, data->new_brightness); -} - -static inline void delayed_sysfs_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct delayed_led_classdev *data = container_of(led_cdev, - struct delayed_led_classdev, led_classdev); - data->new_brightness = brightness; - schedule_work(&data->work); -} - -/* HP-specific accelerometer driver ------------------------------------ */ - -/* For automatic insertion of the module */ -static struct acpi_device_id lis3lv02d_device_ids[] = { - {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); - - -/** - * lis3lv02d_acpi_init - ACPI _INI method: initialize the device. - * @lis3: pointer to the device struct - * - * Returns 0 on success. - */ -int lis3lv02d_acpi_init(struct lis3lv02d *lis3) -{ - struct acpi_device *dev = lis3->bus_priv; - if (acpi_evaluate_object(dev->handle, METHOD_NAME__INI, - NULL, NULL) != AE_OK) - return -EINVAL; - - return 0; -} - -/** - * lis3lv02d_acpi_read - ACPI ALRD method: read a register - * @lis3: pointer to the device struct - * @reg: the register to read - * @ret: result of the operation - * - * Returns 0 on success. - */ -int lis3lv02d_acpi_read(struct lis3lv02d *lis3, int reg, u8 *ret) -{ - struct acpi_device *dev = lis3->bus_priv; - union acpi_object arg0 = { ACPI_TYPE_INTEGER }; - struct acpi_object_list args = { 1, &arg0 }; - unsigned long long lret; - acpi_status status; - - arg0.integer.value = reg; - - status = acpi_evaluate_integer(dev->handle, "ALRD", &args, &lret); - *ret = lret; - return (status != AE_OK) ? -EINVAL : 0; -} - -/** - * lis3lv02d_acpi_write - ACPI ALWR method: write to a register - * @lis3: pointer to the device struct - * @reg: the register to write to - * @val: the value to write - * - * Returns 0 on success. - */ -int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val) -{ - struct acpi_device *dev = lis3->bus_priv; - unsigned long long ret; /* Not used when writting */ - union acpi_object in_obj[2]; - struct acpi_object_list args = { 2, in_obj }; - - in_obj[0].type = ACPI_TYPE_INTEGER; - in_obj[0].integer.value = reg; - in_obj[1].type = ACPI_TYPE_INTEGER; - in_obj[1].integer.value = val; - - if (acpi_evaluate_integer(dev->handle, "ALWR", &args, &ret) != AE_OK) - return -EINVAL; - - return 0; -} - -static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) -{ - lis3_dev.ac = *((struct axis_conversion *)dmi->driver_data); - printk(KERN_INFO DRIVER_NAME ": hardware type %s found.\n", dmi->ident); - - return 1; -} - -/* 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 AXIS_DMI_MATCH(_ident, _name, _axis) { \ - .ident = _ident, \ - .callback = lis3lv02d_dmi_matched, \ - .matches = { \ - DMI_MATCH(DMI_PRODUCT_NAME, _name) \ - }, \ - .driver_data = &lis3lv02d_axis_##_axis \ -} - -#define AXIS_DMI_MATCH2(_ident, _class1, _name1, \ - _class2, _name2, \ - _axis) { \ - .ident = _ident, \ - .callback = lis3lv02d_dmi_matched, \ - .matches = { \ - DMI_MATCH(DMI_##_class1, _name1), \ - DMI_MATCH(DMI_##_class2, _name2), \ - }, \ - .driver_data = &lis3lv02d_axis_##_axis \ -} -static struct dmi_system_id lis3lv02d_dmi_ids[] = { - /* product names are truncated to match all kinds of a same model */ - AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), - AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), - AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), - AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), - AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), - AXIS_DMI_MATCH("NC2710", "HP Compaq 2710", xy_swap), - AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), - AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), - AXIS_DMI_MATCH("HP2140", "HP 2140", xy_swap_inverted), - AXIS_DMI_MATCH("NC653x", "HP Compaq 653", xy_rotated_left_usd), - AXIS_DMI_MATCH("NC6730b", "HP Compaq 6730b", xy_rotated_left_usd), - AXIS_DMI_MATCH("NC6730s", "HP Compaq 6730s", xy_swap), - AXIS_DMI_MATCH("NC651xx", "HP Compaq 651", xy_rotated_right), - AXIS_DMI_MATCH("NC6710x", "HP Compaq 6710", xy_swap_yz_inverted), - AXIS_DMI_MATCH("NC6715x", "HP Compaq 6715", y_inverted), - AXIS_DMI_MATCH("NC693xx", "HP EliteBook 693", xy_rotated_right), - AXIS_DMI_MATCH("NC693xx", "HP EliteBook 853", xy_swap), - /* Intel-based HP Pavilion dv5 */ - AXIS_DMI_MATCH2("HPDV5_I", - PRODUCT_NAME, "HP Pavilion dv5", - BOARD_NAME, "3603", - x_inverted), - /* AMD-based HP Pavilion dv5 */ - AXIS_DMI_MATCH2("HPDV5_A", - PRODUCT_NAME, "HP Pavilion dv5", - BOARD_NAME, "3600", - y_inverted), - AXIS_DMI_MATCH("DV7", "HP Pavilion dv7", x_inverted), - AXIS_DMI_MATCH("HP8710", "HP Compaq 8710", y_inverted), - AXIS_DMI_MATCH("HDX18", "HP HDX 18", x_inverted), - { NULL, } -/* Laptop models without axis info (yet): - * "NC6910" "HP Compaq 6910" - * "NC2400" "HP Compaq nc2400" - * "NX74x0" "HP Compaq nx74" - * "NX6325" "HP Compaq nx6325" - * "NC4400" "HP Compaq nc4400" - */ -}; - -static void hpled_set(struct delayed_led_classdev *led_cdev, enum led_brightness value) -{ - struct acpi_device *dev = lis3_dev.bus_priv; - unsigned long long ret; /* Not used when writing */ - union acpi_object in_obj[1]; - struct acpi_object_list args = { 1, in_obj }; - - in_obj[0].type = ACPI_TYPE_INTEGER; - in_obj[0].integer.value = !!value; - - acpi_evaluate_integer(dev->handle, "ALED", &args, &ret); -} - -static struct delayed_led_classdev hpled_led = { - .led_classdev = { - .name = "hp::hddprotect", - .default_trigger = "none", - .brightness_set = delayed_sysfs_set, - .flags = LED_CORE_SUSPENDRESUME, - }, - .set_brightness = hpled_set, -}; - -static acpi_status -lis3lv02d_get_resource(struct acpi_resource *resource, void *context) -{ - if (resource->type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) { - struct acpi_resource_extended_irq *irq; - u32 *device_irq = context; - - irq = &resource->data.extended_irq; - *device_irq = irq->interrupts[0]; - } - - return AE_OK; -} - -static void lis3lv02d_enum_resources(struct acpi_device *device) -{ - acpi_status status; - - status = acpi_walk_resources(device->handle, METHOD_NAME__CRS, - lis3lv02d_get_resource, &lis3_dev.irq); - if (ACPI_FAILURE(status)) - printk(KERN_DEBUG DRIVER_NAME ": Error getting resources\n"); -} - -static int lis3lv02d_add(struct acpi_device *device) -{ - int ret; - - if (!device) - return -EINVAL; - - lis3_dev.bus_priv = device; - lis3_dev.init = lis3lv02d_acpi_init; - lis3_dev.read = lis3lv02d_acpi_read; - lis3_dev.write = lis3lv02d_acpi_write; - strcpy(acpi_device_name(device), DRIVER_NAME); - strcpy(acpi_device_class(device), ACPI_MDPS_CLASS); - device->driver_data = &lis3_dev; - - /* obtain IRQ number of our device from ACPI */ - lis3lv02d_enum_resources(device); - - /* If possible use a "standard" axes order */ - 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; - } - - /* call the core layer do its init */ - ret = lis3lv02d_init_device(&lis3_dev); - if (ret) - return ret; - - INIT_WORK(&hpled_led.work, delayed_set_status_worker); - ret = led_classdev_register(NULL, &hpled_led.led_classdev); - if (ret) { - lis3lv02d_joystick_disable(); - lis3lv02d_poweroff(&lis3_dev); - flush_work(&hpled_led.work); - return ret; - } - - return ret; -} - -static int lis3lv02d_remove(struct acpi_device *device, int type) -{ - if (!device) - return -EINVAL; - - lis3lv02d_joystick_disable(); - lis3lv02d_poweroff(&lis3_dev); - - flush_work(&hpled_led.work); - led_classdev_unregister(&hpled_led.led_classdev); - - return lis3lv02d_remove_fs(&lis3_dev); -} - - -#ifdef CONFIG_PM -static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) -{ - /* make sure the device is off when we suspend */ - lis3lv02d_poweroff(&lis3_dev); - return 0; -} - -static int lis3lv02d_resume(struct acpi_device *device) -{ - lis3lv02d_poweron(&lis3_dev); - return 0; -} -#else -#define lis3lv02d_suspend NULL -#define lis3lv02d_resume NULL -#endif - -/* For the HP MDPS aka 3D Driveguard */ -static struct acpi_driver lis3lv02d_driver = { - .name = DRIVER_NAME, - .class = ACPI_MDPS_CLASS, - .ids = lis3lv02d_device_ids, - .ops = { - .add = lis3lv02d_add, - .remove = lis3lv02d_remove, - .suspend = lis3lv02d_suspend, - .resume = lis3lv02d_resume, - } -}; - -static int __init lis3lv02d_init_module(void) -{ - int ret; - - if (acpi_disabled) - return -ENODEV; - - ret = acpi_bus_register_driver(&lis3lv02d_driver); - if (ret < 0) - return ret; - - printk(KERN_INFO DRIVER_NAME " driver loaded.\n"); - - return 0; -} - -static void __exit lis3lv02d_exit_module(void) -{ - acpi_bus_unregister_driver(&lis3lv02d_driver); -} - -MODULE_DESCRIPTION("Glue between LIS3LV02Dx and HP ACPI BIOS and support for disk protection LED."); -MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); -MODULE_LICENSE("GPL"); - -module_init(lis3lv02d_init_module); -module_exit(lis3lv02d_exit_module); - diff --git a/drivers/hwmon/htu21.c b/drivers/hwmon/htu21.c new file mode 100644 index 00000000000..839086e0e95 --- /dev/null +++ b/drivers/hwmon/htu21.c @@ -0,0 +1,199 @@ +/* + * Measurement Specialties HTU21D humidity and temperature sensor driver + * + * Copyright (C) 2013 William Markezana <william.markezana@meas-spec.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/jiffies.h> + +/* HTU21 Commands */ +#define HTU21_T_MEASUREMENT_HM 0xE3 +#define HTU21_RH_MEASUREMENT_HM 0xE5 + +struct htu21 { + struct device *hwmon_dev; + struct mutex lock; + bool valid; + unsigned long last_update; + int temperature; + int humidity; +}; + +static inline int htu21_temp_ticks_to_millicelsius(int ticks) +{ + ticks &= ~0x0003; /* clear status bits */ + /* + * Formula T = -46.85 + 175.72 * ST / 2^16 from datasheet p14, + * optimized for integer fixed point (3 digits) arithmetic + */ + return ((21965 * ticks) >> 13) - 46850; +} + +static inline int htu21_rh_ticks_to_per_cent_mille(int ticks) +{ + ticks &= ~0x0003; /* clear status bits */ + /* + * Formula RH = -6 + 125 * SRH / 2^16 from datasheet p14, + * optimized for integer fixed point (3 digits) arithmetic + */ + return ((15625 * ticks) >> 13) - 6000; +} + +static int htu21_update_measurements(struct i2c_client *client) +{ + int ret = 0; + struct htu21 *htu21 = i2c_get_clientdata(client); + + mutex_lock(&htu21->lock); + + if (time_after(jiffies, htu21->last_update + HZ / 2) || + !htu21->valid) { + ret = i2c_smbus_read_word_swapped(client, + HTU21_T_MEASUREMENT_HM); + if (ret < 0) + goto out; + htu21->temperature = htu21_temp_ticks_to_millicelsius(ret); + ret = i2c_smbus_read_word_swapped(client, + HTU21_RH_MEASUREMENT_HM); + if (ret < 0) + goto out; + htu21->humidity = htu21_rh_ticks_to_per_cent_mille(ret); + htu21->last_update = jiffies; + htu21->valid = true; + } +out: + mutex_unlock(&htu21->lock); + + return ret >= 0 ? 0 : ret; +} + +static ssize_t htu21_show_temperature(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct htu21 *htu21 = i2c_get_clientdata(client); + int ret = htu21_update_measurements(client); + if (ret < 0) + return ret; + return sprintf(buf, "%d\n", htu21->temperature); +} + +static ssize_t htu21_show_humidity(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct htu21 *htu21 = i2c_get_clientdata(client); + int ret = htu21_update_measurements(client); + if (ret < 0) + return ret; + return sprintf(buf, "%d\n", htu21->humidity); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, + htu21_show_temperature, NULL, 0); +static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO, + htu21_show_humidity, NULL, 0); + +static struct attribute *htu21_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_humidity1_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group htu21_group = { + .attrs = htu21_attributes, +}; + +static int htu21_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct htu21 *htu21; + int err; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) { + dev_err(&client->dev, + "adapter does not support SMBus word transactions\n"); + return -ENODEV; + } + + htu21 = devm_kzalloc(&client->dev, sizeof(*htu21), GFP_KERNEL); + if (!htu21) + return -ENOMEM; + + i2c_set_clientdata(client, htu21); + + mutex_init(&htu21->lock); + + err = sysfs_create_group(&client->dev.kobj, &htu21_group); + if (err) { + dev_dbg(&client->dev, "could not create sysfs files\n"); + return err; + } + htu21->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(htu21->hwmon_dev)) { + dev_dbg(&client->dev, "unable to register hwmon device\n"); + err = PTR_ERR(htu21->hwmon_dev); + goto error; + } + + dev_info(&client->dev, "initialized\n"); + + return 0; + +error: + sysfs_remove_group(&client->dev.kobj, &htu21_group); + return err; +} + +static int htu21_remove(struct i2c_client *client) +{ + struct htu21 *htu21 = i2c_get_clientdata(client); + + hwmon_device_unregister(htu21->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &htu21_group); + + return 0; +} + +static const struct i2c_device_id htu21_id[] = { + { "htu21", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, htu21_id); + +static struct i2c_driver htu21_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "htu21", + }, + .probe = htu21_probe, + .remove = htu21_remove, + .id_table = htu21_id, +}; + +module_i2c_driver(htu21_driver); + +MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>"); +MODULE_DESCRIPTION("MEAS HTU21D humidity and temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/hwmon-vid.c b/drivers/hwmon/hwmon-vid.c index bf0862a803c..ef91b8a6754 100644 --- a/drivers/hwmon/hwmon-vid.c +++ b/drivers/hwmon/hwmon-vid.c @@ -22,6 +22,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/kernel.h> #include <linux/hwmon-vid.h> @@ -38,7 +40,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: @@ -81,27 +83,27 @@ int vid_from_reg(int val, u8 vrm) { int vid; - switch(vrm) { + switch (vrm) { - case 100: /* VRD 10.0 */ + case 100: /* VRD 10.0 */ /* compute in uV, round to mV */ val &= 0x3f; - if((val & 0x1f) == 0x1f) + if ((val & 0x1f) == 0x1f) return 0; - if((val & 0x1f) <= 0x09 || val == 0x0a) + if ((val & 0x1f) <= 0x09 || val == 0x0a) vid = 1087500 - (val & 0x1f) * 25000; else vid = 1862500 - (val & 0x1f) * 25000; - if(val & 0x20) + if (val & 0x20) vid -= 12500; - return((vid + 500) / 1000); + return (vid + 500) / 1000; case 110: /* Intel Conroe */ /* compute in uV, round to mV */ val &= 0xff; if (val < 0x02 || val > 0xb2) return 0; - return((1600000 - (val - 2) * 6250 + 500) / 1000); + return (1600000 - (val - 2) * 6250 + 500) / 1000; case 24: /* Athlon64 & Opteron */ val &= 0x1f; @@ -113,45 +115,55 @@ int vid_from_reg(int val, u8 vrm) return (val < 32) ? 1550 - 25 * val : 775 - (25 * (val - 31)) / 2; + case 26: /* AMD family 10h to 15h, serial VID */ + val &= 0x7f; + if (val >= 0x7c) + return 0; + return DIV_ROUND_CLOSEST(15500 - 125 * val, 10); + case 91: /* VRM 9.1 */ case 90: /* VRM 9.0 */ val &= 0x1f; - return(val == 0x1f ? 0 : - 1850 - val * 25); + return val == 0x1f ? 0 : + 1850 - val * 25; case 85: /* VRM 8.5 */ val &= 0x1f; - return((val & 0x10 ? 25 : 0) + + return (val & 0x10 ? 25 : 0) + ((val & 0x0f) > 0x04 ? 2050 : 1250) - - ((val & 0x0f) * 50)); + ((val & 0x0f) * 50); case 84: /* VRM 8.4 */ val &= 0x0f; /* fall through */ case 82: /* VRM 8.2 */ val &= 0x1f; - return(val == 0x1f ? 0 : + return val == 0x1f ? 0 : val & 0x10 ? 5100 - (val) * 100 : - 2050 - (val) * 50); + 2050 - (val) * 50; case 17: /* Intel IMVP-II */ val &= 0x1f; - return(val & 0x10 ? 975 - (val & 0xF) * 25 : - 1750 - val * 50); + return val & 0x10 ? 975 - (val & 0xF) * 25 : + 1750 - val * 50; case 13: + case 131: val &= 0x3f; - return(1708 - val * 16); + /* Exception for Eden ULV 500 MHz */ + if (vrm == 131 && val == 0x3f) + val++; + return 1708 - val * 16; case 14: /* Intel Core */ /* compute in uV, round to mV */ val &= 0x7f; - return(val > 0x77 ? 0 : (1500000 - (val * 12500) + 500) / 1000); + return val > 0x77 ? 0 : (1500000 - (val * 12500) + 500) / 1000; default: /* report 0 for unknown */ if (vrm) - printk(KERN_WARNING "hwmon-vid: Requested unsupported " - "VRM version (%u)\n", (unsigned int)vrm); + pr_warn("Requested unsupported VRM version (%u)\n", + (unsigned int)vrm); return 0; } } - +EXPORT_SYMBOL(vid_from_reg); /* * After this point is the code to automatically determine which @@ -160,9 +172,10 @@ int vid_from_reg(int val, u8 vrm) struct vrm_model { u8 vendor; - u8 eff_family; - u8 eff_model; - u8 eff_stepping; + u8 family; + u8 model_from; + u8 model_to; + u8 stepping_to; u8 vrm_type; }; @@ -171,57 +184,102 @@ struct vrm_model { #ifdef CONFIG_X86 /* - * The stepping parameter is highest acceptable stepping for current line. + * The stepping_to parameter is highest acceptable stepping for current line. * The model match must be exact for 4-bit values. For model values 0x10 * and above (extended model), all models below the parameter will match. */ static struct vrm_model vrm_models[] = { - {X86_VENDOR_AMD, 0x6, ANY, ANY, 90}, /* Athlon Duron etc */ - {X86_VENDOR_AMD, 0xF, 0x3F, ANY, 24}, /* Athlon 64, Opteron */ - /* In theory, all NPT family 0Fh processors have 6 VID pins and should - thus use vrm 25, however in practice not all mainboards route the - 6th VID pin because it is never needed. So we use the 5 VID pin - variant (vrm 24) for the models which exist today. */ - {X86_VENDOR_AMD, 0xF, 0x7F, ANY, 24}, /* NPT family 0Fh */ - {X86_VENDOR_AMD, 0xF, ANY, ANY, 25}, /* future fam. 0Fh */ - {X86_VENDOR_AMD, 0x10, ANY, ANY, 25}, /* NPT family 10h */ - - {X86_VENDOR_INTEL, 0x6, 0x9, ANY, 13}, /* Pentium M (130 nm) */ - {X86_VENDOR_INTEL, 0x6, 0xB, ANY, 85}, /* Tualatin */ - {X86_VENDOR_INTEL, 0x6, 0xD, ANY, 13}, /* Pentium M (90 nm) */ - {X86_VENDOR_INTEL, 0x6, 0xE, ANY, 14}, /* Intel Core (65 nm) */ - {X86_VENDOR_INTEL, 0x6, 0xF, ANY, 110}, /* Intel Conroe */ - {X86_VENDOR_INTEL, 0x6, ANY, ANY, 82}, /* any P6 */ - {X86_VENDOR_INTEL, 0xF, 0x0, ANY, 90}, /* P4 */ - {X86_VENDOR_INTEL, 0xF, 0x1, ANY, 90}, /* P4 Willamette */ - {X86_VENDOR_INTEL, 0xF, 0x2, ANY, 90}, /* P4 Northwood */ - {X86_VENDOR_INTEL, 0xF, ANY, ANY, 100}, /* Prescott and above assume VRD 10 */ - - {X86_VENDOR_CENTAUR, 0x6, 0x7, ANY, 85}, /* Eden ESP/Ezra */ - {X86_VENDOR_CENTAUR, 0x6, 0x8, 0x7, 85}, /* Ezra T */ - {X86_VENDOR_CENTAUR, 0x6, 0x9, 0x7, 85}, /* Nemiah */ - {X86_VENDOR_CENTAUR, 0x6, 0x9, ANY, 17}, /* C3-M, Eden-N */ - {X86_VENDOR_CENTAUR, 0x6, 0xA, 0x7, 0}, /* No information */ - {X86_VENDOR_CENTAUR, 0x6, 0xA, ANY, 13}, /* C7, Esther */ - - {X86_VENDOR_UNKNOWN, ANY, ANY, ANY, 0} /* stop here */ + {X86_VENDOR_AMD, 0x6, 0x0, ANY, ANY, 90}, /* Athlon Duron etc */ + {X86_VENDOR_AMD, 0xF, 0x0, 0x3F, ANY, 24}, /* Athlon 64, Opteron */ + /* + * In theory, all NPT family 0Fh processors have 6 VID pins and should + * thus use vrm 25, however in practice not all mainboards route the + * 6th VID pin because it is never needed. So we use the 5 VID pin + * variant (vrm 24) for the models which exist today. + */ + {X86_VENDOR_AMD, 0xF, 0x40, 0x7F, ANY, 24}, /* NPT family 0Fh */ + {X86_VENDOR_AMD, 0xF, 0x80, ANY, ANY, 25}, /* future fam. 0Fh */ + {X86_VENDOR_AMD, 0x10, 0x0, ANY, ANY, 25}, /* NPT family 10h */ + {X86_VENDOR_AMD, 0x11, 0x0, ANY, ANY, 26}, /* family 11h */ + {X86_VENDOR_AMD, 0x12, 0x0, ANY, ANY, 26}, /* family 12h */ + {X86_VENDOR_AMD, 0x14, 0x0, ANY, ANY, 26}, /* family 14h */ + {X86_VENDOR_AMD, 0x15, 0x0, ANY, ANY, 26}, /* family 15h */ + + {X86_VENDOR_INTEL, 0x6, 0x0, 0x6, ANY, 82}, /* Pentium Pro, + * Pentium II, Xeon, + * Mobile Pentium, + * Celeron */ + {X86_VENDOR_INTEL, 0x6, 0x7, 0x7, ANY, 84}, /* Pentium III, Xeon */ + {X86_VENDOR_INTEL, 0x6, 0x8, 0x8, ANY, 82}, /* Pentium III, Xeon */ + {X86_VENDOR_INTEL, 0x6, 0x9, 0x9, ANY, 13}, /* Pentium M (130 nm) */ + {X86_VENDOR_INTEL, 0x6, 0xA, 0xA, ANY, 82}, /* Pentium III Xeon */ + {X86_VENDOR_INTEL, 0x6, 0xB, 0xB, ANY, 85}, /* Tualatin */ + {X86_VENDOR_INTEL, 0x6, 0xD, 0xD, ANY, 13}, /* Pentium M (90 nm) */ + {X86_VENDOR_INTEL, 0x6, 0xE, 0xE, ANY, 14}, /* Intel Core (65 nm) */ + {X86_VENDOR_INTEL, 0x6, 0xF, ANY, ANY, 110}, /* Intel Conroe and + * later */ + {X86_VENDOR_INTEL, 0xF, 0x0, 0x0, ANY, 90}, /* P4 */ + {X86_VENDOR_INTEL, 0xF, 0x1, 0x1, ANY, 90}, /* P4 Willamette */ + {X86_VENDOR_INTEL, 0xF, 0x2, 0x2, ANY, 90}, /* P4 Northwood */ + {X86_VENDOR_INTEL, 0xF, 0x3, ANY, ANY, 100}, /* Prescott and above + * assume VRD 10 */ + + {X86_VENDOR_CENTAUR, 0x6, 0x7, 0x7, ANY, 85}, /* Eden ESP/Ezra */ + {X86_VENDOR_CENTAUR, 0x6, 0x8, 0x8, 0x7, 85}, /* Ezra T */ + {X86_VENDOR_CENTAUR, 0x6, 0x9, 0x9, 0x7, 85}, /* Nehemiah */ + {X86_VENDOR_CENTAUR, 0x6, 0x9, 0x9, ANY, 17}, /* C3-M, Eden-N */ + {X86_VENDOR_CENTAUR, 0x6, 0xA, 0xA, 0x7, 0}, /* No information */ + {X86_VENDOR_CENTAUR, 0x6, 0xA, 0xA, ANY, 13}, /* C7-M, C7, + * Eden (Esther) */ + {X86_VENDOR_CENTAUR, 0x6, 0xD, 0xD, ANY, 134}, /* C7-D, C7-M, C7, + * Eden (Esther) */ }; -static u8 find_vrm(u8 eff_family, u8 eff_model, u8 eff_stepping, u8 vendor) +/* + * Special case for VIA model D: there are two different possible + * VID tables, so we have to figure out first, which one must be + * used. This resolves temporary drm value 134 to 14 (Intel Core + * 7-bit VID), 13 (Pentium M 6-bit VID) or 131 (Pentium M 6-bit VID + * + quirk for Eden ULV 500 MHz). + * Note: something similar might be needed for model A, I'm not sure. + */ +static u8 get_via_model_d_vrm(void) +{ + unsigned int vid, brand, __maybe_unused dummy; + static const char *brands[4] = { + "C7-M", "C7", "Eden", "C7-D" + }; + + rdmsr(0x198, dummy, vid); + vid &= 0xff; + + rdmsr(0x1154, brand, dummy); + brand = ((brand >> 4) ^ (brand >> 2)) & 0x03; + + if (vid > 0x3f) { + pr_info("Using %d-bit VID table for VIA %s CPU\n", + 7, brands[brand]); + return 14; + } else { + pr_info("Using %d-bit VID table for VIA %s CPU\n", + 6, brands[brand]); + /* Enable quirk for Eden */ + return brand == 2 ? 131 : 13; + } +} + +static u8 find_vrm(u8 family, u8 model, u8 stepping, u8 vendor) { - int i = 0; - - while (vrm_models[i].vendor!=X86_VENDOR_UNKNOWN) { - if (vrm_models[i].vendor==vendor) - if ((vrm_models[i].eff_family==eff_family) - && ((vrm_models[i].eff_model==eff_model) || - (vrm_models[i].eff_model >= 0x10 && - eff_model <= vrm_models[i].eff_model) || - (vrm_models[i].eff_model==ANY)) && - (eff_stepping <= vrm_models[i].eff_stepping)) - return vrm_models[i].vrm_type; - i++; + int i; + + for (i = 0; i < ARRAY_SIZE(vrm_models); i++) { + if (vendor == vrm_models[i].vendor && + family == vrm_models[i].family && + model >= vrm_models[i].model_from && + model <= vrm_models[i].model_to && + stepping <= vrm_models[i].stepping_to) + return vrm_models[i].vrm_type; } return 0; @@ -230,24 +288,16 @@ static u8 find_vrm(u8 eff_family, u8 eff_model, u8 eff_stepping, u8 vendor) u8 vid_which_vrm(void) { struct cpuinfo_x86 *c = &cpu_data(0); - u32 eax; - u8 eff_family, eff_model, eff_stepping, vrm_ret; + u8 vrm_ret; if (c->x86 < 6) /* Any CPU with family lower than 6 */ - return 0; /* doesn't have VID and/or CPUID */ - - eax = cpuid_eax(1); - eff_family = ((eax & 0x00000F00)>>8); - eff_model = ((eax & 0x000000F0)>>4); - eff_stepping = eax & 0xF; - if (eff_family == 0xF) { /* use extended model & family */ - eff_family += ((eax & 0x00F00000)>>20); - eff_model += ((eax & 0x000F0000)>>16)<<4; - } - vrm_ret = find_vrm(eff_family, eff_model, eff_stepping, c->x86_vendor); + return 0; /* doesn't have VID */ + + vrm_ret = find_vrm(c->x86, c->x86_model, c->x86_mask, c->x86_vendor); + if (vrm_ret == 134) + vrm_ret = get_via_model_d_vrm(); if (vrm_ret == 0) - printk(KERN_INFO "hwmon-vid: Unknown VRM version of your " - "x86 CPU\n"); + pr_info("Unknown VRM version of your x86 CPU\n"); return vrm_ret; } @@ -255,12 +305,10 @@ u8 vid_which_vrm(void) #else u8 vid_which_vrm(void) { - printk(KERN_INFO "hwmon-vid: Unknown VRM version of your CPU\n"); + pr_info("Unknown VRM version of your CPU\n"); return 0; } #endif - -EXPORT_SYMBOL(vid_from_reg); EXPORT_SYMBOL(vid_which_vrm); MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>"); diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 29ea6753f3b..a26c385a435 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -1,72 +1,154 @@ /* - hwmon.c - part of lm_sensors, Linux kernel modules for hardware monitoring - - This file defines the sysfs class "hwmon", for use by sensors drivers. - - Copyright (C) 2005 Mark M. Hoffman <mhoffman@lightlink.com> + * hwmon.c - part of lm_sensors, Linux kernel modules for hardware monitoring + * + * This file defines the sysfs class "hwmon", for use by sensors drivers. + * + * Copyright (C) 2005 Mark M. Hoffman <mhoffman@lightlink.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; version 2 of the License. + */ - 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 of the License. -*/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/device.h> #include <linux/err.h> +#include <linux/slab.h> #include <linux/kdev_t.h> #include <linux/idr.h> #include <linux/hwmon.h> #include <linux/gfp.h> #include <linux/spinlock.h> #include <linux/pci.h> +#include <linux/string.h> #define HWMON_ID_PREFIX "hwmon" #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" -static struct class *hwmon_class; +struct hwmon_device { + const char *name; + struct device dev; +}; +#define to_hwmon_device(d) container_of(d, struct hwmon_device, dev) + +static ssize_t +show_name(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", to_hwmon_device(dev)->name); +} +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static struct attribute *hwmon_dev_attrs[] = { + &dev_attr_name.attr, + NULL +}; + +static umode_t hwmon_dev_name_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + + if (to_hwmon_device(dev)->name == NULL) + return 0; + + return attr->mode; +} + +static struct attribute_group hwmon_dev_attr_group = { + .attrs = hwmon_dev_attrs, + .is_visible = hwmon_dev_name_is_visible, +}; + +static const struct attribute_group *hwmon_dev_attr_groups[] = { + &hwmon_dev_attr_group, + NULL +}; + +static void hwmon_dev_release(struct device *dev) +{ + kfree(to_hwmon_device(dev)); +} + +static struct class hwmon_class = { + .name = "hwmon", + .owner = THIS_MODULE, + .dev_groups = hwmon_dev_attr_groups, + .dev_release = hwmon_dev_release, +}; -static DEFINE_IDR(hwmon_idr); -static DEFINE_SPINLOCK(idr_lock); +static DEFINE_IDA(hwmon_ida); /** - * hwmon_device_register - register w/ hwmon - * @dev: the device to register + * hwmon_device_register_with_groups - register w/ hwmon + * @dev: the parent device + * @name: hwmon name attribute + * @drvdata: driver data to attach to created device + * @groups: List of attribute groups to create * * hwmon_device_unregister() must be called when the device is no * longer needed. * * Returns the pointer to the new device. */ -struct device *hwmon_device_register(struct device *dev) +struct device * +hwmon_device_register_with_groups(struct device *dev, const char *name, + void *drvdata, + const struct attribute_group **groups) { - struct device *hwdev; - int id, err; + struct hwmon_device *hwdev; + int err, id; -again: - if (unlikely(idr_pre_get(&hwmon_idr, GFP_KERNEL) == 0)) - return ERR_PTR(-ENOMEM); + /* Do not accept invalid characters in hwmon name attribute */ + if (name && (!strlen(name) || strpbrk(name, "-* \t\n"))) + return ERR_PTR(-EINVAL); - spin_lock(&idr_lock); - err = idr_get_new(&hwmon_idr, NULL, &id); - spin_unlock(&idr_lock); + id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL); + if (id < 0) + return ERR_PTR(id); - if (unlikely(err == -EAGAIN)) - goto again; - else if (unlikely(err)) - return ERR_PTR(err); + hwdev = kzalloc(sizeof(*hwdev), GFP_KERNEL); + if (hwdev == NULL) { + err = -ENOMEM; + goto ida_remove; + } - id = id & MAX_ID_MASK; - hwdev = device_create(hwmon_class, dev, MKDEV(0, 0), NULL, - HWMON_ID_FORMAT, id); + hwdev->name = name; + hwdev->dev.class = &hwmon_class; + hwdev->dev.parent = dev; + hwdev->dev.groups = groups; + hwdev->dev.of_node = dev ? dev->of_node : NULL; + dev_set_drvdata(&hwdev->dev, drvdata); + dev_set_name(&hwdev->dev, HWMON_ID_FORMAT, id); + err = device_register(&hwdev->dev); + if (err) + goto free; - if (IS_ERR(hwdev)) { - spin_lock(&idr_lock); - idr_remove(&hwmon_idr, id); - spin_unlock(&idr_lock); - } + return &hwdev->dev; - return hwdev; +free: + kfree(hwdev); +ida_remove: + ida_simple_remove(&hwmon_ida, id); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups); + +/** + * hwmon_device_register - register w/ hwmon + * @dev: the device to register + * + * hwmon_device_unregister() must be called when the device is no + * longer needed. + * + * Returns the pointer to the new device. + */ +struct device *hwmon_device_register(struct device *dev) +{ + return hwmon_device_register_with_groups(dev, NULL, NULL, NULL); } +EXPORT_SYMBOL_GPL(hwmon_device_register); /** * hwmon_device_unregister - removes the previously registered class device @@ -79,13 +161,75 @@ void hwmon_device_unregister(struct device *dev) if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) { device_unregister(dev); - spin_lock(&idr_lock); - idr_remove(&hwmon_idr, id); - spin_unlock(&idr_lock); + ida_simple_remove(&hwmon_ida, id); } else dev_dbg(dev->parent, "hwmon_device_unregister() failed: bad class ID!\n"); } +EXPORT_SYMBOL_GPL(hwmon_device_unregister); + +static void devm_hwmon_release(struct device *dev, void *res) +{ + struct device *hwdev = *(struct device **)res; + + hwmon_device_unregister(hwdev); +} + +/** + * devm_hwmon_device_register_with_groups - register w/ hwmon + * @dev: the parent device + * @name: hwmon name attribute + * @drvdata: driver data to attach to created device + * @groups: List of attribute groups to create + * + * Returns the pointer to the new device. The new device is automatically + * unregistered with the parent device. + */ +struct device * +devm_hwmon_device_register_with_groups(struct device *dev, const char *name, + void *drvdata, + const struct attribute_group **groups) +{ + struct device **ptr, *hwdev; + + if (!dev) + return ERR_PTR(-EINVAL); + + ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + hwdev = hwmon_device_register_with_groups(dev, name, drvdata, groups); + if (IS_ERR(hwdev)) + goto error; + + *ptr = hwdev; + devres_add(dev, ptr); + return hwdev; + +error: + devres_free(ptr); + return hwdev; +} +EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups); + +static int devm_hwmon_match(struct device *dev, void *res, void *data) +{ + struct device **hwdev = res; + + return *hwdev == data; +} + +/** + * devm_hwmon_device_unregister - removes a previously registered hwmon device + * + * @dev: the parent device of the device to unregister + */ +void devm_hwmon_device_unregister(struct device *dev) +{ + WARN_ON(devres_release(dev, devm_hwmon_release, devm_hwmon_match, dev)); +} +EXPORT_SYMBOL_GPL(devm_hwmon_device_unregister); static void __init hwmon_pci_quirks(void) { @@ -96,46 +240,47 @@ static void __init hwmon_pci_quirks(void) /* Open access to 0x295-0x296 on MSI MS-7031 */ sb = pci_get_device(PCI_VENDOR_ID_ATI, 0x436c, NULL); - if (sb && - (sb->subsystem_vendor == 0x1462 && /* MSI */ - sb->subsystem_device == 0x0031)) { /* MS-7031 */ - - pci_read_config_byte(sb, 0x48, &enable); - pci_read_config_word(sb, 0x64, &base); - - if (base == 0 && !(enable & BIT(2))) { - dev_info(&sb->dev, - "Opening wide generic port at 0x295\n"); - pci_write_config_word(sb, 0x64, 0x295); - pci_write_config_byte(sb, 0x48, enable | BIT(2)); + if (sb) { + if (sb->subsystem_vendor == 0x1462 && /* MSI */ + sb->subsystem_device == 0x0031) { /* MS-7031 */ + pci_read_config_byte(sb, 0x48, &enable); + pci_read_config_word(sb, 0x64, &base); + + if (base == 0 && !(enable & BIT(2))) { + dev_info(&sb->dev, + "Opening wide generic port at 0x295\n"); + pci_write_config_word(sb, 0x64, 0x295); + pci_write_config_byte(sb, 0x48, + enable | BIT(2)); + } } + pci_dev_put(sb); } #endif } static int __init hwmon_init(void) { + int err; + hwmon_pci_quirks(); - hwmon_class = class_create(THIS_MODULE, "hwmon"); - if (IS_ERR(hwmon_class)) { - printk(KERN_ERR "hwmon.c: couldn't create sysfs class\n"); - return PTR_ERR(hwmon_class); + err = class_register(&hwmon_class); + if (err) { + pr_err("couldn't register hwmon sysfs class\n"); + return err; } return 0; } static void __exit hwmon_exit(void) { - class_destroy(hwmon_class); + class_unregister(&hwmon_class); } subsys_initcall(hwmon_init); module_exit(hwmon_exit); -EXPORT_SYMBOL_GPL(hwmon_device_register); -EXPORT_SYMBOL_GPL(hwmon_device_unregister); - MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); MODULE_DESCRIPTION("hardware monitoring sysfs/class support"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/i5k_amb.c b/drivers/hwmon/i5k_amb.c index 27d7f72a5f1..6c0080a3b90 100644 --- a/drivers/hwmon/i5k_amb.c +++ b/drivers/hwmon/i5k_amb.c @@ -3,7 +3,7 @@ * temperature sensors * Copyright (C) 2007 IBM * - * Author: Darrick J. Wong <djwong@us.ibm.com> + * Author: Darrick J. Wong <darrick.wong@oracle.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 @@ -21,15 +21,14 @@ */ #include <linux/module.h> -#include <linux/jiffies.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> -#include <linux/delay.h> #include <linux/log2.h> #include <linux/pci.h> #include <linux/platform_device.h> +#include <linux/slab.h> #define DRVNAME "i5k_amb" @@ -113,7 +112,6 @@ struct i5k_amb_data { void __iomem *amb_mmio; struct i5k_device_attribute *attrs; unsigned int num_attrs; - unsigned long chipset_id; }; static ssize_t show_name(struct device *dev, struct device_attribute *devattr, @@ -159,8 +157,12 @@ static ssize_t store_amb_min(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i5k_amb_data *data = dev_get_drvdata(dev); - unsigned long temp = simple_strtoul(buf, NULL, 10) / 500; + unsigned long temp; + int ret = kstrtoul(buf, 10, &temp); + if (ret < 0) + return ret; + temp = temp / 500; if (temp > 255) temp = 255; @@ -175,8 +177,12 @@ static ssize_t store_amb_mid(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i5k_amb_data *data = dev_get_drvdata(dev); - unsigned long temp = simple_strtoul(buf, NULL, 10) / 500; + unsigned long temp; + int ret = kstrtoul(buf, 10, &temp); + if (ret < 0) + return ret; + temp = temp / 500; if (temp > 255) temp = 255; @@ -191,8 +197,12 @@ static ssize_t store_amb_max(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i5k_amb_data *data = dev_get_drvdata(dev); - unsigned long temp = simple_strtoul(buf, NULL, 10) / 500; + unsigned long temp; + int ret = kstrtoul(buf, 10, &temp); + if (ret < 0) + return ret; + temp = temp / 500; if (temp > 255) temp = 255; @@ -250,7 +260,7 @@ static ssize_t show_label(struct device *dev, attr->index & DIMM_MASK); } -static int __devinit i5k_amb_hwmon_init(struct platform_device *pdev) +static int i5k_amb_hwmon_init(struct platform_device *pdev) { int i, j, k, d = 0; u16 c; @@ -288,6 +298,7 @@ static int __devinit i5k_amb_hwmon_init(struct platform_device *pdev) iattr->s_attr.dev_attr.attr.mode = S_IRUGO; iattr->s_attr.dev_attr.show = show_label; iattr->s_attr.index = k; + sysfs_attr_init(&iattr->s_attr.dev_attr.attr); res = device_create_file(&pdev->dev, &iattr->s_attr.dev_attr); if (res) @@ -302,6 +313,7 @@ static int __devinit i5k_amb_hwmon_init(struct platform_device *pdev) iattr->s_attr.dev_attr.attr.mode = S_IRUGO; iattr->s_attr.dev_attr.show = show_amb_temp; iattr->s_attr.index = k; + sysfs_attr_init(&iattr->s_attr.dev_attr.attr); res = device_create_file(&pdev->dev, &iattr->s_attr.dev_attr); if (res) @@ -317,6 +329,7 @@ static int __devinit i5k_amb_hwmon_init(struct platform_device *pdev) iattr->s_attr.dev_attr.show = show_amb_min; iattr->s_attr.dev_attr.store = store_amb_min; iattr->s_attr.index = k; + sysfs_attr_init(&iattr->s_attr.dev_attr.attr); res = device_create_file(&pdev->dev, &iattr->s_attr.dev_attr); if (res) @@ -332,6 +345,7 @@ static int __devinit i5k_amb_hwmon_init(struct platform_device *pdev) iattr->s_attr.dev_attr.show = show_amb_mid; iattr->s_attr.dev_attr.store = store_amb_mid; iattr->s_attr.index = k; + sysfs_attr_init(&iattr->s_attr.dev_attr.attr); res = device_create_file(&pdev->dev, &iattr->s_attr.dev_attr); if (res) @@ -347,6 +361,7 @@ static int __devinit i5k_amb_hwmon_init(struct platform_device *pdev) iattr->s_attr.dev_attr.show = show_amb_max; iattr->s_attr.dev_attr.store = store_amb_max; iattr->s_attr.index = k; + sysfs_attr_init(&iattr->s_attr.dev_attr.attr); res = device_create_file(&pdev->dev, &iattr->s_attr.dev_attr); if (res) @@ -361,6 +376,7 @@ static int __devinit i5k_amb_hwmon_init(struct platform_device *pdev) iattr->s_attr.dev_attr.attr.mode = S_IRUGO; iattr->s_attr.dev_attr.show = show_amb_alarm; iattr->s_attr.index = k; + sysfs_attr_init(&iattr->s_attr.dev_attr.attr); res = device_create_file(&pdev->dev, &iattr->s_attr.dev_attr); if (res) @@ -390,7 +406,7 @@ exit_remove: return res; } -static int __devinit i5k_amb_add(void) +static int i5k_amb_add(void) { int res = -ENODEV; @@ -409,7 +425,7 @@ err: return res; } -static int __devinit i5k_find_amb_registers(struct i5k_amb_data *data, +static int i5k_find_amb_registers(struct i5k_amb_data *data, unsigned long devid) { struct pci_dev *pcidev; @@ -437,15 +453,13 @@ static int __devinit i5k_find_amb_registers(struct i5k_amb_data *data, goto out; } - data->chipset_id = devid; - res = 0; out: pci_dev_put(pcidev); return res; } -static int __devinit i5k_channel_probe(u16 *amb_present, unsigned long dev_id) +static int i5k_channel_probe(u16 *amb_present, unsigned long dev_id) { struct pci_dev *pcidev; u16 val16; @@ -471,38 +485,29 @@ out: return res; } -static unsigned long i5k_channel_pci_id(struct i5k_amb_data *data, - unsigned long channel) -{ - switch (data->chipset_id) { - case PCI_DEVICE_ID_INTEL_5000_ERR: - return PCI_DEVICE_ID_INTEL_5000_FBD0 + channel; - case PCI_DEVICE_ID_INTEL_5400_ERR: - return PCI_DEVICE_ID_INTEL_5400_FBD0 + channel; - default: - BUG(); - } -} - -static unsigned long chipset_ids[] = { - PCI_DEVICE_ID_INTEL_5000_ERR, - PCI_DEVICE_ID_INTEL_5400_ERR, - 0 +static struct { + unsigned long err; + unsigned long fbd0; +} chipset_ids[] = { + { PCI_DEVICE_ID_INTEL_5000_ERR, PCI_DEVICE_ID_INTEL_5000_FBD0 }, + { PCI_DEVICE_ID_INTEL_5400_ERR, PCI_DEVICE_ID_INTEL_5400_FBD0 }, + { 0, 0 } }; -static struct pci_device_id i5k_amb_ids[] __devinitdata = { +#ifdef MODULE +static struct pci_device_id i5k_amb_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5000_ERR) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5400_ERR) }, { 0, } }; MODULE_DEVICE_TABLE(pci, i5k_amb_ids); +#endif -static int __devinit i5k_amb_probe(struct platform_device *pdev) +static int i5k_amb_probe(struct platform_device *pdev) { struct i5k_amb_data *data; struct resource *reso; - int i; - int res = -ENODEV; + int i, res; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) @@ -511,22 +516,22 @@ static int __devinit i5k_amb_probe(struct platform_device *pdev) /* Figure out where the AMB registers live */ i = 0; do { - res = i5k_find_amb_registers(data, chipset_ids[i]); + res = i5k_find_amb_registers(data, chipset_ids[i].err); + if (res == 0) + break; i++; - } while (res && chipset_ids[i]); + } while (chipset_ids[i].err); if (res) goto err; /* Copy the DIMM presence map for the first two channels */ - res = i5k_channel_probe(&data->amb_present[0], - i5k_channel_pci_id(data, 0)); + res = i5k_channel_probe(&data->amb_present[0], chipset_ids[i].fbd0); if (res) goto err; /* Copy the DIMM presence map for the optional second two channels */ - i5k_channel_probe(&data->amb_present[2], - i5k_channel_pci_id(data, 1)); + i5k_channel_probe(&data->amb_present[2], chipset_ids[i].fbd0 + 1); /* Set up resource regions */ reso = request_mem_region(data->amb_base, data->amb_len, DRVNAME); @@ -551,7 +556,6 @@ static int __devinit i5k_amb_probe(struct platform_device *pdev) err_init_failed: iounmap(data->amb_mmio); - platform_set_drvdata(pdev, NULL); err_map_failed: release_mem_region(data->amb_base, data->amb_len); err: @@ -559,7 +563,7 @@ err: return res; } -static int __devexit i5k_amb_remove(struct platform_device *pdev) +static int i5k_amb_remove(struct platform_device *pdev) { int i; struct i5k_amb_data *data = platform_get_drvdata(pdev); @@ -571,7 +575,6 @@ static int __devexit i5k_amb_remove(struct platform_device *pdev) kfree(data->attrs); iounmap(data->amb_mmio); release_mem_region(data->amb_base, data->amb_len); - platform_set_drvdata(pdev, NULL); kfree(data); return 0; } @@ -582,7 +585,7 @@ static struct platform_driver i5k_amb_driver = { .name = DRVNAME, }, .probe = i5k_amb_probe, - .remove = __devexit_p(i5k_amb_remove), + .remove = i5k_amb_remove, }; static int __init i5k_amb_init(void) @@ -606,7 +609,7 @@ static void __exit i5k_amb_exit(void) platform_driver_unregister(&i5k_amb_driver); } -MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); +MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>"); MODULE_DESCRIPTION("Intel 5000 chipset FB-DIMM AMB temperature sensor"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c index 405d3fb5d76..632f1dc0fe1 100644 --- a/drivers/hwmon/ibmaem.c +++ b/drivers/hwmon/ibmaem.c @@ -3,7 +3,7 @@ * temperature/power/energy sensors and capping functionality. * Copyright (C) 2008 IBM * - * Author: Darrick J. Wong <djwong@us.ibm.com> + * Author: Darrick J. Wong <darrick.wong@oracle.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 @@ -20,6 +20,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/ipmi.h> #include <linux/module.h> #include <linux/hwmon.h> @@ -29,10 +31,12 @@ #include <linux/kdev_t.h> #include <linux/spinlock.h> #include <linux/idr.h> +#include <linux/slab.h> #include <linux/sched.h> #include <linux/platform_device.h> #include <linux/math64.h> #include <linux/time.h> +#include <linux/err.h> #define REFRESH_INTERVAL (HZ) #define IPMI_TIMEOUT (30 * HZ) @@ -85,8 +89,7 @@ #define AEM_MIN_POWER_INTERVAL 200 #define UJ_PER_MJ 1000L -static DEFINE_IDR(aem_idr); -static DEFINE_SPINLOCK(aem_idr_lock); +static DEFINE_IDA(aem_ida); static struct platform_driver aem_driver = { .driver = { @@ -145,8 +148,9 @@ struct aem_data { int id; struct aem_ipmi_data ipmi; - /* Function to update sensors */ + /* Function and buffer to update sensors */ void (*update)(struct aem_data *data); + struct aem_read_sensor_resp *rs_resp; /* * AEM 1.x sensors: @@ -243,8 +247,6 @@ static void aem_bmc_gone(int iface); static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data); static void aem_remove_sensors(struct aem_data *data); -static int aem_init_aem1(struct aem_ipmi_data *probe); -static int aem_init_aem2(struct aem_ipmi_data *probe); static int aem1_find_sensors(struct aem_data *data); static int aem2_find_sensors(struct aem_data *data); static void update_aem1_sensors(struct aem_data *data); @@ -287,9 +289,10 @@ static int aem_init_ipmi_data(struct aem_ipmi_data *data, int iface, err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs, data, &data->user); if (err < 0) { - dev_err(bmc, "Unable to register user with IPMI " - "interface %d\n", data->interface); - return -EACCES; + dev_err(bmc, + "Unable to register user with IPMI interface %d\n", + data->interface); + return err; } return 0; @@ -326,8 +329,8 @@ static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) struct aem_ipmi_data *data = user_msg_data; if (msg->msgid != data->tx_msgid) { - dev_err(data->bmc_device, "Mismatch between received msgid " - "(%02x) and transmitted msgid (%02x)!\n", + dev_err(data->bmc_device, + "Mismatch between received msgid (%02x) and transmitted msgid (%02x)!\n", (int)msg->msgid, (int)data->tx_msgid); ipmi_free_recv_msg(msg); @@ -353,47 +356,16 @@ static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) complete(&data->read_complete); } -/* ID functions */ - -/* Obtain an id */ -static int aem_idr_get(int *id) -{ - int i, err; - -again: - if (unlikely(!idr_pre_get(&aem_idr, GFP_KERNEL))) - return -ENOMEM; - - spin_lock(&aem_idr_lock); - err = idr_get_new(&aem_idr, NULL, &i); - spin_unlock(&aem_idr_lock); - - if (unlikely(err == -EAGAIN)) - goto again; - else if (unlikely(err)) - return err; - - *id = i & MAX_ID_MASK; - return 0; -} - -/* Release an object ID */ -static void aem_idr_put(int id) -{ - spin_lock(&aem_idr_lock); - idr_remove(&aem_idr, id); - spin_unlock(&aem_idr_lock); -} - /* Sensor support functions */ -/* Read a sensor value */ +/* Read a sensor value; must be called with data->lock held */ static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg, void *buf, size_t size) { int rs_size, res; struct aem_read_sensor_req rs_req; - struct aem_read_sensor_resp *rs_resp; + /* Use preallocated rx buffer */ + struct aem_read_sensor_resp *rs_resp = data->rs_resp; struct aem_ipmi_data *ipmi = &data->ipmi; /* AEM registers are 1, 2, 4 or 8 bytes */ @@ -419,23 +391,21 @@ static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg, ipmi->tx_message.data_len = sizeof(rs_req); rs_size = sizeof(*rs_resp) + size; - rs_resp = kzalloc(rs_size, GFP_KERNEL); - if (!rs_resp) - return -ENOMEM; - ipmi->rx_msg_data = rs_resp; ipmi->rx_msg_len = rs_size; aem_send_message(ipmi); res = wait_for_completion_timeout(&ipmi->read_complete, IPMI_TIMEOUT); - if (!res) - return -ETIMEDOUT; + if (!res) { + res = -ETIMEDOUT; + goto out; + } if (ipmi->rx_result || ipmi->rx_msg_len != rs_size || memcmp(&rs_resp->id, &system_x_id, sizeof(system_x_id))) { - kfree(rs_resp); - return -ENOENT; + res = -ENOENT; + goto out; } switch (size) { @@ -460,8 +430,10 @@ static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg, break; } } + res = 0; - return 0; +out: + return res; } /* Update AEM energy registers */ @@ -518,11 +490,12 @@ static void aem_delete(struct aem_data *data) { list_del(&data->list); aem_remove_sensors(data); + kfree(data->rs_resp); hwmon_device_unregister(data->hwmon_dev); ipmi_destroy_user(data->ipmi.user); - dev_set_drvdata(&data->pdev->dev, NULL); + platform_set_drvdata(data->pdev, NULL); platform_device_unregister(data->pdev); - aem_idr_put(data->id); + ida_simple_remove(&aem_ida, data->id); kfree(data); } @@ -579,7 +552,8 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle) data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL; /* Create sub-device for this fw instance */ - if (aem_idr_get(&data->id)) + data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL); + if (data->id < 0) goto id_err; data->pdev = platform_device_alloc(DRVNAME, data->id); @@ -591,27 +565,34 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle) if (res) goto ipmi_err; - dev_set_drvdata(&data->pdev->dev, data); + platform_set_drvdata(data->pdev, data); /* Set up IPMI interface */ - if (aem_init_ipmi_data(&data->ipmi, probe->interface, - probe->bmc_device)) + res = aem_init_ipmi_data(&data->ipmi, probe->interface, + probe->bmc_device); + if (res) goto ipmi_err; /* Register with hwmon */ data->hwmon_dev = hwmon_device_register(&data->pdev->dev); - if (IS_ERR(data->hwmon_dev)) { - dev_err(&data->pdev->dev, "Unable to register hwmon " - "device for IPMI interface %d\n", + dev_err(&data->pdev->dev, + "Unable to register hwmon device for IPMI interface %d\n", probe->interface); + res = PTR_ERR(data->hwmon_dev); goto hwmon_reg_err; } data->update = update_aem1_sensors; + data->rs_resp = kzalloc(sizeof(*(data->rs_resp)) + 8, GFP_KERNEL); + if (!data->rs_resp) { + res = -ENOMEM; + goto alloc_resp_err; + } /* Find sensors */ - if (aem1_find_sensors(data)) + res = aem1_find_sensors(data); + if (res) goto sensor_err; /* Add to our list of AEM devices */ @@ -623,14 +604,16 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle) return 0; sensor_err: + kfree(data->rs_resp); +alloc_resp_err: hwmon_device_unregister(data->hwmon_dev); hwmon_reg_err: ipmi_destroy_user(data->ipmi.user); ipmi_err: - dev_set_drvdata(&data->pdev->dev, NULL); + platform_set_drvdata(data->pdev, NULL); platform_device_unregister(data->pdev); dev_err: - aem_idr_put(data->id); + ida_simple_remove(&aem_ida, data->id); id_err: kfree(data); @@ -638,7 +621,7 @@ id_err: } /* Find and initialize all AEM1 instances */ -static int aem_init_aem1(struct aem_ipmi_data *probe) +static void aem_init_aem1(struct aem_ipmi_data *probe) { int num, i, err; @@ -649,11 +632,8 @@ static int aem_init_aem1(struct aem_ipmi_data *probe) dev_err(probe->bmc_device, "Error %d initializing AEM1 0x%X\n", err, i); - return err; } } - - return 0; } /* Probe functions for AEM2 devices */ @@ -712,7 +692,8 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe, data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL; /* Create sub-device for this fw instance */ - if (aem_idr_get(&data->id)) + data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL); + if (data->id < 0) goto id_err; data->pdev = platform_device_alloc(DRVNAME, data->id); @@ -724,27 +705,34 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe, if (res) goto ipmi_err; - dev_set_drvdata(&data->pdev->dev, data); + platform_set_drvdata(data->pdev, data); /* Set up IPMI interface */ - if (aem_init_ipmi_data(&data->ipmi, probe->interface, - probe->bmc_device)) + res = aem_init_ipmi_data(&data->ipmi, probe->interface, + probe->bmc_device); + if (res) goto ipmi_err; /* Register with hwmon */ data->hwmon_dev = hwmon_device_register(&data->pdev->dev); - if (IS_ERR(data->hwmon_dev)) { - dev_err(&data->pdev->dev, "Unable to register hwmon " - "device for IPMI interface %d\n", + dev_err(&data->pdev->dev, + "Unable to register hwmon device for IPMI interface %d\n", probe->interface); + res = PTR_ERR(data->hwmon_dev); goto hwmon_reg_err; } data->update = update_aem2_sensors; + data->rs_resp = kzalloc(sizeof(*(data->rs_resp)) + 8, GFP_KERNEL); + if (!data->rs_resp) { + res = -ENOMEM; + goto alloc_resp_err; + } /* Find sensors */ - if (aem2_find_sensors(data)) + res = aem2_find_sensors(data); + if (res) goto sensor_err; /* Add to our list of AEM devices */ @@ -756,14 +744,16 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe, return 0; sensor_err: + kfree(data->rs_resp); +alloc_resp_err: hwmon_device_unregister(data->hwmon_dev); hwmon_reg_err: ipmi_destroy_user(data->ipmi.user); ipmi_err: - dev_set_drvdata(&data->pdev->dev, NULL); + platform_set_drvdata(data->pdev, NULL); platform_device_unregister(data->pdev); dev_err: - aem_idr_put(data->id); + ida_simple_remove(&aem_ida, data->id); id_err: kfree(data); @@ -771,7 +761,7 @@ id_err: } /* Find and initialize all AEM2 instances */ -static int aem_init_aem2(struct aem_ipmi_data *probe) +static void aem_init_aem2(struct aem_ipmi_data *probe) { struct aem_find_instance_resp fi_resp; int err; @@ -779,8 +769,8 @@ static int aem_init_aem2(struct aem_ipmi_data *probe) while (!aem_find_aem2(probe, &fi_resp, i)) { if (fi_resp.major != 2) { - dev_err(probe->bmc_device, "Unknown AEM v%d; please " - "report this to the maintainer.\n", + dev_err(probe->bmc_device, + "Unknown AEM v%d; please report this to the maintainer.\n", fi_resp.major); i++; continue; @@ -790,12 +780,9 @@ static int aem_init_aem2(struct aem_ipmi_data *probe) dev_err(probe->bmc_device, "Error %d initializing AEM2 0x%X\n", err, fi_resp.module_handle); - return err; } i++; } - - return 0; } /* Probe a BMC for AEM firmware instances */ @@ -919,7 +906,7 @@ static ssize_t aem_set_power_period(struct device *dev, unsigned long temp; int res; - res = strict_strtoul(buf, 10, &temp); + res = kstrtoul(buf, 10, &temp); if (res) return res; @@ -944,6 +931,7 @@ static int aem_register_sensors(struct aem_data *data, /* Set up read-only sensors */ while (ro->label) { + sysfs_attr_init(&sensors->dev_attr.attr); sensors->dev_attr.attr.name = ro->label; sensors->dev_attr.attr.mode = S_IRUGO; sensors->dev_attr.show = ro->show; @@ -960,6 +948,7 @@ static int aem_register_sensors(struct aem_data *data, /* Set up read-write sensors */ while (rw->label) { + sysfs_attr_init(&sensors->dev_attr.attr); sensors->dev_attr.attr.name = rw->label; sensors->dev_attr.attr.mode = S_IRUGO | S_IWUSR; sensors->dev_attr.show = rw->show; @@ -1058,7 +1047,7 @@ static struct aem_ro_sensor_template aem2_ro_sensors[] = { {"power6_average", aem2_show_pcap_value, POWER_CAP_MIN_WARNING}, {"power7_average", aem2_show_pcap_value, POWER_CAP_MIN}, -{"power3_average", aem2_show_pcap_value, POWER_AUX}, +{"power3_average", aem2_show_pcap_value, POWER_AUX}, {"power_cap", aem2_show_pcap_value, POWER_CAP}, {NULL, NULL, 0}, }; @@ -1089,7 +1078,7 @@ static int __init aem_init(void) res = driver_register(&aem_driver.driver); if (res) { - printk(KERN_ERR "Can't register aem driver\n"); + pr_err("Can't register aem driver\n"); return res; } @@ -1114,7 +1103,7 @@ static void __exit aem_exit(void) aem_delete(p1); } -MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); +MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>"); MODULE_DESCRIPTION("IBM AEM power/temp/energy sensor driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c index a36363312f2..030e7ff589b 100644 --- a/drivers/hwmon/ibmpex.c +++ b/drivers/hwmon/ibmpex.c @@ -2,7 +2,7 @@ * A hwmon driver for the IBM PowerExecutive temperature/power sensors * Copyright (C) 2007 IBM * - * Author: Darrick J. Wong <djwong@us.ibm.com> + * Author: Darrick J. Wong <darrick.wong@oracle.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 @@ -25,6 +25,8 @@ #include <linux/hwmon-sysfs.h> #include <linux/jiffies.h> #include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/err.h> #define REFRESH_INTERVAL (2 * HZ) #define DRVNAME "ibmpex" @@ -161,8 +163,8 @@ static int ibmpex_ver_check(struct ibmpex_bmc_data *data) data->sensor_major = data->rx_msg_data[0]; data->sensor_minor = data->rx_msg_data[1]; - dev_info(data->bmc_device, "Found BMC with sensor interface " - "v%d.%d %d-%02d-%02d on interface %d\n", + dev_info(data->bmc_device, + "Found BMC with sensor interface v%d.%d %d-%02d-%02d on interface %d\n", data->sensor_major, data->sensor_minor, extract_value(data->rx_msg_data, 2), @@ -357,6 +359,7 @@ static int create_sensor(struct ibmpex_bmc_data *data, int type, else if (type == POWER_SENSOR) sprintf(n, power_sensor_name_templates[func], "power", counter); + sysfs_attr_init(&data->sensors[sensor].attr[func].dev_attr.attr); data->sensors[sensor].attr[func].dev_attr.attr.name = n; data->sensors[sensor].attr[func].dev_attr.attr.mode = S_IRUGO; data->sensors[sensor].attr[func].dev_attr.show = ibmpex_show_sensor; @@ -460,10 +463,8 @@ static void ibmpex_register_bmc(int iface, struct device *dev) int err; data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - dev_err(dev, "Insufficient memory for BMC interface.\n"); + if (!data) return; - } data->address.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; data->address.channel = IPMI_BMC_CHANNEL; @@ -475,8 +476,9 @@ static void ibmpex_register_bmc(int iface, struct device *dev) err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs, data, &data->user); if (err < 0) { - dev_err(dev, "Unable to register user with IPMI " - "interface %d\n", data->interface); + dev_err(dev, + "Unable to register user with IPMI interface %d\n", + data->interface); goto out; } @@ -498,8 +500,8 @@ static void ibmpex_register_bmc(int iface, struct device *dev) data->hwmon_dev = hwmon_device_register(data->bmc_device); if (IS_ERR(data->hwmon_dev)) { - dev_err(data->bmc_device, "Unable to register hwmon " - "device for IPMI interface %d\n", + dev_err(data->bmc_device, + "Unable to register hwmon device for IPMI interface %d\n", data->interface); goto out_user; } @@ -564,8 +566,8 @@ static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data; if (msg->msgid != data->tx_msgid) { - dev_err(data->bmc_device, "Mismatch between received msgid " - "(%02x) and transmitted msgid (%02x)!\n", + dev_err(data->bmc_device, + "Mismatch between received msgid (%02x) and transmitted msgid (%02x)!\n", (int)msg->msgid, (int)data->tx_msgid); ipmi_free_recv_msg(msg); @@ -602,7 +604,7 @@ static void __exit ibmpex_exit(void) ibmpex_bmc_delete(p); } -MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); +MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>"); MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c new file mode 100644 index 00000000000..14c82daab01 --- /dev/null +++ b/drivers/hwmon/iio_hwmon.c @@ -0,0 +1,186 @@ +/* Hwmon client for industrial I/O devices + * + * Copyright (c) 2011 Jonathan Cameron + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/hwmon.h> +#include <linux/of.h> +#include <linux/hwmon-sysfs.h> +#include <linux/iio/consumer.h> +#include <linux/iio/types.h> + +/** + * struct iio_hwmon_state - device instance state + * @channels: filled with array of channels from iio + * @num_channels: number of channels in channels (saves counting twice) + * @hwmon_dev: associated hwmon device + * @attr_group: the group of attributes + * @attrs: null terminated array of attribute pointers. + */ +struct iio_hwmon_state { + struct iio_channel *channels; + int num_channels; + struct device *hwmon_dev; + struct attribute_group attr_group; + const struct attribute_group *groups[2]; + struct attribute **attrs; +}; + +/* + * Assumes that IIO and hwmon operate in the same base units. + * This is supposed to be true, but needs verification for + * new channel types. + */ +static ssize_t iio_hwmon_read_val(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int result; + int ret; + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct iio_hwmon_state *state = dev_get_drvdata(dev); + + ret = iio_read_channel_processed(&state->channels[sattr->index], + &result); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", result); +} + +static int iio_hwmon_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_hwmon_state *st; + struct sensor_device_attribute *a; + int ret, i; + int in_i = 1, temp_i = 1, curr_i = 1; + enum iio_chan_type type; + struct iio_channel *channels; + const char *name = "iio_hwmon"; + + if (dev->of_node && dev->of_node->name) + name = dev->of_node->name; + + channels = iio_channel_get_all(dev); + if (IS_ERR(channels)) + return PTR_ERR(channels); + + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (st == NULL) { + ret = -ENOMEM; + goto error_release_channels; + } + + st->channels = channels; + + /* count how many attributes we have */ + while (st->channels[st->num_channels].indio_dev) + st->num_channels++; + + st->attrs = devm_kzalloc(dev, + sizeof(*st->attrs) * (st->num_channels + 1), + GFP_KERNEL); + if (st->attrs == NULL) { + ret = -ENOMEM; + goto error_release_channels; + } + + for (i = 0; i < st->num_channels; i++) { + a = devm_kzalloc(dev, sizeof(*a), GFP_KERNEL); + if (a == NULL) { + ret = -ENOMEM; + goto error_release_channels; + } + + sysfs_attr_init(&a->dev_attr.attr); + ret = iio_get_channel_type(&st->channels[i], &type); + if (ret < 0) + goto error_release_channels; + + switch (type) { + case IIO_VOLTAGE: + a->dev_attr.attr.name = kasprintf(GFP_KERNEL, + "in%d_input", + in_i++); + break; + case IIO_TEMP: + a->dev_attr.attr.name = kasprintf(GFP_KERNEL, + "temp%d_input", + temp_i++); + break; + case IIO_CURRENT: + a->dev_attr.attr.name = kasprintf(GFP_KERNEL, + "curr%d_input", + curr_i++); + break; + default: + ret = -EINVAL; + goto error_release_channels; + } + if (a->dev_attr.attr.name == NULL) { + ret = -ENOMEM; + goto error_release_channels; + } + a->dev_attr.show = iio_hwmon_read_val; + a->dev_attr.attr.mode = S_IRUGO; + a->index = i; + st->attrs[i] = &a->dev_attr.attr; + } + + st->attr_group.attrs = st->attrs; + st->groups[0] = &st->attr_group; + st->hwmon_dev = hwmon_device_register_with_groups(dev, name, st, + st->groups); + if (IS_ERR(st->hwmon_dev)) { + ret = PTR_ERR(st->hwmon_dev); + goto error_release_channels; + } + platform_set_drvdata(pdev, st); + return 0; + +error_release_channels: + iio_channel_release_all(channels); + return ret; +} + +static int iio_hwmon_remove(struct platform_device *pdev) +{ + struct iio_hwmon_state *st = platform_get_drvdata(pdev); + + hwmon_device_unregister(st->hwmon_dev); + iio_channel_release_all(st->channels); + + return 0; +} + +static const struct of_device_id iio_hwmon_of_match[] = { + { .compatible = "iio-hwmon", }, + { } +}; +MODULE_DEVICE_TABLE(of, iio_hwmon_of_match); + +static struct platform_driver __refdata iio_hwmon_driver = { + .driver = { + .name = "iio_hwmon", + .owner = THIS_MODULE, + .of_match_table = iio_hwmon_of_match, + }, + .probe = iio_hwmon_probe, + .remove = iio_hwmon_remove, +}; + +module_platform_driver(iio_hwmon_driver); + +MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); +MODULE_DESCRIPTION("IIO to hwmon driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/ina209.c b/drivers/hwmon/ina209.c new file mode 100644 index 00000000000..5378fdefc1f --- /dev/null +++ b/drivers/hwmon/ina209.c @@ -0,0 +1,626 @@ +/* + * Driver for the Texas Instruments / Burr Brown INA209 + * Bidirectional Current/Power Monitor + * + * Copyright (C) 2012 Guenter Roeck <linux@roeck-us.net> + * + * Derived from Ira W. Snyder's original driver submission + * Copyright (C) 2008 Paul Hays <Paul.Hays@cattail.ca> + * Copyright (C) 2008-2009 Ira W. Snyder <iws@ovro.caltech.edu> + * + * Aligned with ina2xx driver + * Copyright (C) 2012 Lothar Felten <l-felten@ti.com> + * Thanks to Jan Volkering + * + * 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 of the License. + * + * Datasheet: + * http://www.ti.com/lit/gpn/ina209 + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/bug.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> + +#include <linux/platform_data/ina2xx.h> + +/* register definitions */ +#define INA209_CONFIGURATION 0x00 +#define INA209_STATUS 0x01 +#define INA209_STATUS_MASK 0x02 +#define INA209_SHUNT_VOLTAGE 0x03 +#define INA209_BUS_VOLTAGE 0x04 +#define INA209_POWER 0x05 +#define INA209_CURRENT 0x06 +#define INA209_SHUNT_VOLTAGE_POS_PEAK 0x07 +#define INA209_SHUNT_VOLTAGE_NEG_PEAK 0x08 +#define INA209_BUS_VOLTAGE_MAX_PEAK 0x09 +#define INA209_BUS_VOLTAGE_MIN_PEAK 0x0a +#define INA209_POWER_PEAK 0x0b +#define INA209_SHUNT_VOLTAGE_POS_WARN 0x0c +#define INA209_SHUNT_VOLTAGE_NEG_WARN 0x0d +#define INA209_POWER_WARN 0x0e +#define INA209_BUS_VOLTAGE_OVER_WARN 0x0f +#define INA209_BUS_VOLTAGE_UNDER_WARN 0x10 +#define INA209_POWER_OVER_LIMIT 0x11 +#define INA209_BUS_VOLTAGE_OVER_LIMIT 0x12 +#define INA209_BUS_VOLTAGE_UNDER_LIMIT 0x13 +#define INA209_CRITICAL_DAC_POS 0x14 +#define INA209_CRITICAL_DAC_NEG 0x15 +#define INA209_CALIBRATION 0x16 + +#define INA209_REGISTERS 0x17 + +#define INA209_CONFIG_DEFAULT 0x3c47 /* PGA=8, full range */ +#define INA209_SHUNT_DEFAULT 10000 /* uOhm */ + +struct ina209_data { + struct i2c_client *client; + + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + + u16 regs[INA209_REGISTERS]; /* All chip registers */ + + u16 config_orig; /* Original configuration */ + u16 calibration_orig; /* Original calibration */ + u16 update_interval; +}; + +static struct ina209_data *ina209_update_device(struct device *dev) +{ + struct ina209_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + struct ina209_data *ret = data; + s32 val; + int i; + + mutex_lock(&data->update_lock); + + if (!data->valid || + time_after(jiffies, data->last_updated + data->update_interval)) { + for (i = 0; i < ARRAY_SIZE(data->regs); i++) { + val = i2c_smbus_read_word_swapped(client, i); + if (val < 0) { + ret = ERR_PTR(val); + goto abort; + } + data->regs[i] = val; + } + data->last_updated = jiffies; + data->valid = true; + } +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +/* + * Read a value from a device register and convert it to the + * appropriate sysfs units + */ +static long ina209_from_reg(const u8 reg, const u16 val) +{ + switch (reg) { + case INA209_SHUNT_VOLTAGE: + case INA209_SHUNT_VOLTAGE_POS_PEAK: + case INA209_SHUNT_VOLTAGE_NEG_PEAK: + case INA209_SHUNT_VOLTAGE_POS_WARN: + case INA209_SHUNT_VOLTAGE_NEG_WARN: + /* LSB=10 uV. Convert to mV. */ + return DIV_ROUND_CLOSEST(val, 100); + + case INA209_BUS_VOLTAGE: + case INA209_BUS_VOLTAGE_MAX_PEAK: + case INA209_BUS_VOLTAGE_MIN_PEAK: + case INA209_BUS_VOLTAGE_OVER_WARN: + case INA209_BUS_VOLTAGE_UNDER_WARN: + case INA209_BUS_VOLTAGE_OVER_LIMIT: + case INA209_BUS_VOLTAGE_UNDER_LIMIT: + /* LSB=4 mV, last 3 bits unused */ + return (val >> 3) * 4; + + case INA209_CRITICAL_DAC_POS: + /* LSB=1 mV, in the upper 8 bits */ + return val >> 8; + + case INA209_CRITICAL_DAC_NEG: + /* LSB=1 mV, in the upper 8 bits */ + return -1 * (val >> 8); + + case INA209_POWER: + case INA209_POWER_PEAK: + case INA209_POWER_WARN: + case INA209_POWER_OVER_LIMIT: + /* LSB=20 mW. Convert to uW */ + return val * 20 * 1000L; + + case INA209_CURRENT: + /* LSB=1 mA (selected). Is in mA */ + return val; + } + + /* programmer goofed */ + WARN_ON_ONCE(1); + return 0; +} + +/* + * Take a value and convert it to register format, clamping the value + * to the appropriate range. + */ +static int ina209_to_reg(u8 reg, u16 old, long val) +{ + switch (reg) { + case INA209_SHUNT_VOLTAGE_POS_WARN: + case INA209_SHUNT_VOLTAGE_NEG_WARN: + /* Limit to +- 320 mV, 10 uV LSB */ + return clamp_val(val, -320, 320) * 100; + + case INA209_BUS_VOLTAGE_OVER_WARN: + case INA209_BUS_VOLTAGE_UNDER_WARN: + case INA209_BUS_VOLTAGE_OVER_LIMIT: + case INA209_BUS_VOLTAGE_UNDER_LIMIT: + /* + * Limit to 0-32000 mV, 4 mV LSB + * + * The last three bits aren't part of the value, but we'll + * preserve them in their original state. + */ + return (DIV_ROUND_CLOSEST(clamp_val(val, 0, 32000), 4) << 3) + | (old & 0x7); + + case INA209_CRITICAL_DAC_NEG: + /* + * Limit to -255-0 mV, 1 mV LSB + * Convert the value to a positive value for the register + * + * The value lives in the top 8 bits only, be careful + * and keep original value of other bits. + */ + return (clamp_val(-val, 0, 255) << 8) | (old & 0xff); + + case INA209_CRITICAL_DAC_POS: + /* + * Limit to 0-255 mV, 1 mV LSB + * + * The value lives in the top 8 bits only, be careful + * and keep original value of other bits. + */ + return (clamp_val(val, 0, 255) << 8) | (old & 0xff); + + case INA209_POWER_WARN: + case INA209_POWER_OVER_LIMIT: + /* 20 mW LSB */ + return DIV_ROUND_CLOSEST(val, 20 * 1000); + } + + /* Other registers are read-only, return access error */ + return -EACCES; +} + +static int ina209_interval_from_reg(u16 reg) +{ + return 68 >> (15 - ((reg >> 3) & 0x0f)); +} + +static u16 ina209_reg_from_interval(u16 config, long interval) +{ + int i, adc; + + if (interval <= 0) { + adc = 8; + } else { + adc = 15; + for (i = 34 + 34 / 2; i; i >>= 1) { + if (i < interval) + break; + adc--; + } + } + return (config & 0xf807) | (adc << 3) | (adc << 7); +} + +static ssize_t ina209_set_interval(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct ina209_data *data = ina209_update_device(dev); + long val; + u16 regval; + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + mutex_lock(&data->update_lock); + regval = ina209_reg_from_interval(data->regs[INA209_CONFIGURATION], + val); + i2c_smbus_write_word_swapped(data->client, INA209_CONFIGURATION, + regval); + data->regs[INA209_CONFIGURATION] = regval; + data->update_interval = ina209_interval_from_reg(regval); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t ina209_show_interval(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct ina209_data *data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", data->update_interval); +} + +/* + * History is reset by writing 1 into bit 0 of the respective peak register. + * Since more than one peak register may be affected by the scope of a + * reset_history attribute write, use a bit mask in attr->index to identify + * which registers are affected. + */ +static u16 ina209_reset_history_regs[] = { + INA209_SHUNT_VOLTAGE_POS_PEAK, + INA209_SHUNT_VOLTAGE_NEG_PEAK, + INA209_BUS_VOLTAGE_MAX_PEAK, + INA209_BUS_VOLTAGE_MIN_PEAK, + INA209_POWER_PEAK +}; + +static ssize_t ina209_reset_history(struct device *dev, + struct device_attribute *da, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ina209_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + u32 mask = attr->index; + long val; + int i, ret; + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + mutex_lock(&data->update_lock); + for (i = 0; i < ARRAY_SIZE(ina209_reset_history_regs); i++) { + if (mask & (1 << i)) + i2c_smbus_write_word_swapped(client, + ina209_reset_history_regs[i], 1); + } + data->valid = false; + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t ina209_set_value(struct device *dev, + struct device_attribute *da, + const char *buf, + size_t count) +{ + struct ina209_data *data = ina209_update_device(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int reg = attr->index; + long val; + int ret; + + if (IS_ERR(data)) + return PTR_ERR(data); + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + mutex_lock(&data->update_lock); + ret = ina209_to_reg(reg, data->regs[reg], val); + if (ret < 0) { + count = ret; + goto abort; + } + i2c_smbus_write_word_swapped(data->client, reg, ret); + data->regs[reg] = ret; +abort: + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t ina209_show_value(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ina209_data *data = ina209_update_device(dev); + long val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = ina209_from_reg(attr->index, data->regs[attr->index]); + return snprintf(buf, PAGE_SIZE, "%ld\n", val); +} + +static ssize_t ina209_show_alarm(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ina209_data *data = ina209_update_device(dev); + const unsigned int mask = attr->index; + u16 status; + + if (IS_ERR(data)) + return PTR_ERR(data); + + status = data->regs[INA209_STATUS]; + + /* + * All alarms are in the INA209_STATUS register. To avoid a long + * switch statement, the mask is passed in attr->index + */ + return snprintf(buf, PAGE_SIZE, "%u\n", !!(status & mask)); +} + +/* Shunt voltage, history, limits, alarms */ +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ina209_show_value, NULL, + INA209_SHUNT_VOLTAGE); +static SENSOR_DEVICE_ATTR(in0_input_highest, S_IRUGO, ina209_show_value, NULL, + INA209_SHUNT_VOLTAGE_POS_PEAK); +static SENSOR_DEVICE_ATTR(in0_input_lowest, S_IRUGO, ina209_show_value, NULL, + INA209_SHUNT_VOLTAGE_NEG_PEAK); +static SENSOR_DEVICE_ATTR(in0_reset_history, S_IWUSR, NULL, + ina209_reset_history, (1 << 0) | (1 << 1)); +static SENSOR_DEVICE_ATTR(in0_max, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_SHUNT_VOLTAGE_POS_WARN); +static SENSOR_DEVICE_ATTR(in0_min, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_SHUNT_VOLTAGE_NEG_WARN); +static SENSOR_DEVICE_ATTR(in0_crit_max, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_CRITICAL_DAC_POS); +static SENSOR_DEVICE_ATTR(in0_crit_min, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_CRITICAL_DAC_NEG); + +static SENSOR_DEVICE_ATTR(in0_min_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 11); +static SENSOR_DEVICE_ATTR(in0_max_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 12); +static SENSOR_DEVICE_ATTR(in0_crit_min_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 6); +static SENSOR_DEVICE_ATTR(in0_crit_max_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 7); + +/* Bus voltage, history, limits, alarms */ +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ina209_show_value, NULL, + INA209_BUS_VOLTAGE); +static SENSOR_DEVICE_ATTR(in1_input_highest, S_IRUGO, ina209_show_value, NULL, + INA209_BUS_VOLTAGE_MAX_PEAK); +static SENSOR_DEVICE_ATTR(in1_input_lowest, S_IRUGO, ina209_show_value, NULL, + INA209_BUS_VOLTAGE_MIN_PEAK); +static SENSOR_DEVICE_ATTR(in1_reset_history, S_IWUSR, NULL, + ina209_reset_history, (1 << 2) | (1 << 3)); +static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_BUS_VOLTAGE_OVER_WARN); +static SENSOR_DEVICE_ATTR(in1_min, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_BUS_VOLTAGE_UNDER_WARN); +static SENSOR_DEVICE_ATTR(in1_crit_max, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_BUS_VOLTAGE_OVER_LIMIT); +static SENSOR_DEVICE_ATTR(in1_crit_min, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_BUS_VOLTAGE_UNDER_LIMIT); + +static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 14); +static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 15); +static SENSOR_DEVICE_ATTR(in1_crit_min_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 9); +static SENSOR_DEVICE_ATTR(in1_crit_max_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 10); + +/* Power */ +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ina209_show_value, NULL, + INA209_POWER); +static SENSOR_DEVICE_ATTR(power1_input_highest, S_IRUGO, ina209_show_value, + NULL, INA209_POWER_PEAK); +static SENSOR_DEVICE_ATTR(power1_reset_history, S_IWUSR, NULL, + ina209_reset_history, 1 << 4); +static SENSOR_DEVICE_ATTR(power1_max, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_POWER_WARN); +static SENSOR_DEVICE_ATTR(power1_crit, S_IRUGO | S_IWUSR, ina209_show_value, + ina209_set_value, INA209_POWER_OVER_LIMIT); + +static SENSOR_DEVICE_ATTR(power1_max_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 13); +static SENSOR_DEVICE_ATTR(power1_crit_alarm, S_IRUGO, ina209_show_alarm, NULL, + 1 << 8); + +/* Current */ +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ina209_show_value, NULL, + INA209_CURRENT); + +static SENSOR_DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, + ina209_show_interval, ina209_set_interval, 0); + +/* + * Finally, construct an array of pointers to members of the above objects, + * as required for sysfs_create_group() + */ +static struct attribute *ina209_attrs[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_input_highest.dev_attr.attr, + &sensor_dev_attr_in0_input_lowest.dev_attr.attr, + &sensor_dev_attr_in0_reset_history.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in0_crit_max.dev_attr.attr, + &sensor_dev_attr_in0_crit_min.dev_attr.attr, + &sensor_dev_attr_in0_max_alarm.dev_attr.attr, + &sensor_dev_attr_in0_min_alarm.dev_attr.attr, + &sensor_dev_attr_in0_crit_max_alarm.dev_attr.attr, + &sensor_dev_attr_in0_crit_min_alarm.dev_attr.attr, + + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_input_highest.dev_attr.attr, + &sensor_dev_attr_in1_input_lowest.dev_attr.attr, + &sensor_dev_attr_in1_reset_history.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in1_crit_max.dev_attr.attr, + &sensor_dev_attr_in1_crit_min.dev_attr.attr, + &sensor_dev_attr_in1_max_alarm.dev_attr.attr, + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, + &sensor_dev_attr_in1_crit_max_alarm.dev_attr.attr, + &sensor_dev_attr_in1_crit_min_alarm.dev_attr.attr, + + &sensor_dev_attr_power1_input.dev_attr.attr, + &sensor_dev_attr_power1_input_highest.dev_attr.attr, + &sensor_dev_attr_power1_reset_history.dev_attr.attr, + &sensor_dev_attr_power1_max.dev_attr.attr, + &sensor_dev_attr_power1_crit.dev_attr.attr, + &sensor_dev_attr_power1_max_alarm.dev_attr.attr, + &sensor_dev_attr_power1_crit_alarm.dev_attr.attr, + + &sensor_dev_attr_curr1_input.dev_attr.attr, + + &sensor_dev_attr_update_interval.dev_attr.attr, + + NULL, +}; +ATTRIBUTE_GROUPS(ina209); + +static void ina209_restore_conf(struct i2c_client *client, + struct ina209_data *data) +{ + /* Restore initial configuration */ + i2c_smbus_write_word_swapped(client, INA209_CONFIGURATION, + data->config_orig); + i2c_smbus_write_word_swapped(client, INA209_CALIBRATION, + data->calibration_orig); +} + +static int ina209_init_client(struct i2c_client *client, + struct ina209_data *data) +{ + struct ina2xx_platform_data *pdata = dev_get_platdata(&client->dev); + u32 shunt; + int reg; + + reg = i2c_smbus_read_word_swapped(client, INA209_CALIBRATION); + if (reg < 0) + return reg; + data->calibration_orig = reg; + + reg = i2c_smbus_read_word_swapped(client, INA209_CONFIGURATION); + if (reg < 0) + return reg; + data->config_orig = reg; + + if (pdata) { + if (pdata->shunt_uohms <= 0) + return -EINVAL; + shunt = pdata->shunt_uohms; + } else if (!of_property_read_u32(client->dev.of_node, "shunt-resistor", + &shunt)) { + if (shunt == 0) + return -EINVAL; + } else { + shunt = data->calibration_orig ? + 40960000 / data->calibration_orig : INA209_SHUNT_DEFAULT; + } + + i2c_smbus_write_word_swapped(client, INA209_CONFIGURATION, + INA209_CONFIG_DEFAULT); + data->update_interval = ina209_interval_from_reg(INA209_CONFIG_DEFAULT); + + /* + * Calibrate current LSB to 1mA. Shunt is in uOhms. + * See equation 13 in datasheet. + */ + i2c_smbus_write_word_swapped(client, INA209_CALIBRATION, + clamp_val(40960000 / shunt, 1, 65535)); + + /* Clear status register */ + i2c_smbus_read_word_swapped(client, INA209_STATUS); + + return 0; +} + +static int ina209_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct ina209_data *data; + struct device *hwmon_dev; + int ret; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + data->client = client; + mutex_init(&data->update_lock); + + ret = ina209_init_client(client, data); + if (ret) + return ret; + + hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, + client->name, + data, ina209_groups); + if (IS_ERR(hwmon_dev)) { + ret = PTR_ERR(hwmon_dev); + goto out_restore_conf; + } + + return 0; + +out_restore_conf: + ina209_restore_conf(client, data); + return ret; +} + +static int ina209_remove(struct i2c_client *client) +{ + struct ina209_data *data = i2c_get_clientdata(client); + + ina209_restore_conf(client, data); + + return 0; +} + +static const struct i2c_device_id ina209_id[] = { + { "ina209", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ina209_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver ina209_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "ina209", + }, + .probe = ina209_probe, + .remove = ina209_remove, + .id_table = ina209_id, +}; + +module_i2c_driver(ina209_driver); + +MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>, Paul Hays <Paul.Hays@cattail.ca>, Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("INA209 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c new file mode 100644 index 00000000000..bfd3f3eeabc --- /dev/null +++ b/drivers/hwmon/ina2xx.c @@ -0,0 +1,292 @@ +/* + * Driver for Texas Instruments INA219, INA226 power monitor chips + * + * INA219: + * Zero Drift Bi-Directional Current/Power Monitor with I2C Interface + * Datasheet: http://www.ti.com/product/ina219 + * + * INA220: + * Bi-Directional Current/Power Monitor with I2C Interface + * Datasheet: http://www.ti.com/product/ina220 + * + * INA226: + * Bi-Directional Current/Power Monitor with I2C Interface + * Datasheet: http://www.ti.com/product/ina226 + * + * INA230: + * Bi-directional Current/Power Monitor with I2C Interface + * Datasheet: http://www.ti.com/product/ina230 + * + * Copyright (C) 2012 Lothar Felten <l-felten@ti.com> + * Thanks to Jan Volkering + * + * 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 of the License. + */ + +#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> +#include <linux/jiffies.h> +#include <linux/of.h> + +#include <linux/platform_data/ina2xx.h> + +/* common register definitions */ +#define INA2XX_CONFIG 0x00 +#define INA2XX_SHUNT_VOLTAGE 0x01 /* readonly */ +#define INA2XX_BUS_VOLTAGE 0x02 /* readonly */ +#define INA2XX_POWER 0x03 /* readonly */ +#define INA2XX_CURRENT 0x04 /* readonly */ +#define INA2XX_CALIBRATION 0x05 + +/* INA226 register definitions */ +#define INA226_MASK_ENABLE 0x06 +#define INA226_ALERT_LIMIT 0x07 +#define INA226_DIE_ID 0xFF + + +/* register count */ +#define INA219_REGISTERS 6 +#define INA226_REGISTERS 8 + +#define INA2XX_MAX_REGISTERS 8 + +/* settings - depend on use case */ +#define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */ +#define INA226_CONFIG_DEFAULT 0x4527 /* averages=16 */ + +/* worst case is 68.10 ms (~14.6Hz, ina219) */ +#define INA2XX_CONVERSION_RATE 15 + +enum ina2xx_ids { ina219, ina226 }; + +struct ina2xx_config { + u16 config_default; + int calibration_factor; + int registers; + int shunt_div; + int bus_voltage_shift; + int bus_voltage_lsb; /* uV */ + int power_lsb; /* uW */ +}; + +struct ina2xx_data { + struct i2c_client *client; + const struct ina2xx_config *config; + + struct mutex update_lock; + bool valid; + unsigned long last_updated; + + int kind; + u16 regs[INA2XX_MAX_REGISTERS]; +}; + +static const struct ina2xx_config ina2xx_config[] = { + [ina219] = { + .config_default = INA219_CONFIG_DEFAULT, + .calibration_factor = 40960000, + .registers = INA219_REGISTERS, + .shunt_div = 100, + .bus_voltage_shift = 3, + .bus_voltage_lsb = 4000, + .power_lsb = 20000, + }, + [ina226] = { + .config_default = INA226_CONFIG_DEFAULT, + .calibration_factor = 5120000, + .registers = INA226_REGISTERS, + .shunt_div = 400, + .bus_voltage_shift = 0, + .bus_voltage_lsb = 1250, + .power_lsb = 25000, + }, +}; + +static struct ina2xx_data *ina2xx_update_device(struct device *dev) +{ + struct ina2xx_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + struct ina2xx_data *ret = data; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + + HZ / INA2XX_CONVERSION_RATE) || !data->valid) { + + int i; + + dev_dbg(&client->dev, "Starting ina2xx update\n"); + + /* Read all registers */ + for (i = 0; i < data->config->registers; i++) { + int rv = i2c_smbus_read_word_swapped(client, i); + if (rv < 0) { + ret = ERR_PTR(rv); + goto abort; + } + data->regs[i] = rv; + } + data->last_updated = jiffies; + data->valid = 1; + } +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +static int ina2xx_get_value(struct ina2xx_data *data, u8 reg) +{ + int val; + + switch (reg) { + case INA2XX_SHUNT_VOLTAGE: + /* signed register */ + val = DIV_ROUND_CLOSEST((s16)data->regs[reg], + data->config->shunt_div); + break; + case INA2XX_BUS_VOLTAGE: + val = (data->regs[reg] >> data->config->bus_voltage_shift) + * data->config->bus_voltage_lsb; + val = DIV_ROUND_CLOSEST(val, 1000); + break; + case INA2XX_POWER: + val = data->regs[reg] * data->config->power_lsb; + break; + case INA2XX_CURRENT: + /* signed register, LSB=1mA (selected), in mA */ + val = (s16)data->regs[reg]; + break; + default: + /* programmer goofed */ + WARN_ON_ONCE(1); + val = 0; + break; + } + + return val; +} + +static ssize_t ina2xx_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ina2xx_data *data = ina2xx_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return snprintf(buf, PAGE_SIZE, "%d\n", + ina2xx_get_value(data, attr->index)); +} + +/* shunt voltage */ +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ina2xx_show_value, NULL, + INA2XX_SHUNT_VOLTAGE); + +/* bus voltage */ +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ina2xx_show_value, NULL, + INA2XX_BUS_VOLTAGE); + +/* calculated current */ +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ina2xx_show_value, NULL, + INA2XX_CURRENT); + +/* calculated power */ +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ina2xx_show_value, NULL, + INA2XX_POWER); + +/* pointers to created device attributes */ +static struct attribute *ina2xx_attrs[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_power1_input.dev_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(ina2xx); + +static int ina2xx_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct ina2xx_platform_data *pdata; + struct device *dev = &client->dev; + struct ina2xx_data *data; + struct device *hwmon_dev; + long shunt = 10000; /* default shunt value 10mOhms */ + u32 val; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (dev_get_platdata(dev)) { + pdata = dev_get_platdata(dev); + shunt = pdata->shunt_uohms; + } else if (!of_property_read_u32(dev->of_node, + "shunt-resistor", &val)) { + shunt = val; + } + + if (shunt <= 0) + return -ENODEV; + + /* set the device type */ + data->kind = id->driver_data; + data->config = &ina2xx_config[data->kind]; + + /* device configuration */ + i2c_smbus_write_word_swapped(client, INA2XX_CONFIG, + data->config->config_default); + /* set current LSB to 1mA, shunt is in uOhms */ + /* (equation 13 in datasheet) */ + i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION, + data->config->calibration_factor / shunt); + + data->client = client; + mutex_init(&data->update_lock); + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, ina2xx_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + dev_info(dev, "power monitor %s (Rshunt = %li uOhm)\n", + id->name, shunt); + + return 0; +} + +static const struct i2c_device_id ina2xx_id[] = { + { "ina219", ina219 }, + { "ina220", ina219 }, + { "ina226", ina226 }, + { "ina230", ina226 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ina2xx_id); + +static struct i2c_driver ina2xx_driver = { + .driver = { + .name = "ina2xx", + }, + .probe = ina2xx_probe, + .id_table = ina2xx_id, +}; + +module_i2c_driver(ina2xx_driver); + +MODULE_AUTHOR("Lothar Felten <l-felten@ti.com>"); +MODULE_DESCRIPTION("ina2xx driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 0ffe84d190b..a327fd3402a 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -1,40 +1,51 @@ /* - it87.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring. - - The IT8705F is an LPC-based Super I/O part that contains UARTs, a - parallel port, an IR port, a MIDI port, a floppy controller, etc., in - addition to an Environment Controller (Enhanced Hardware Monitor and - Fan Controller) - - This driver supports only the Environment Controller in the IT8705F and - similar parts. The other devices are supported by different drivers. - - Supports: IT8705F Super I/O chip w/LPC interface - IT8712F Super I/O chip w/LPC interface - IT8716F Super I/O chip w/LPC interface - IT8718F Super I/O chip w/LPC interface - IT8720F Super I/O chip w/LPC interface - IT8726F Super I/O chip w/LPC interface - Sis950 A clone of the IT8705F - - Copyright (C) 2001 Chris Gauthron - Copyright (C) 2005-2007 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; 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. -*/ + * it87.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring. + * + * The IT8705F is an LPC-based Super I/O part that contains UARTs, a + * parallel port, an IR port, a MIDI port, a floppy controller, etc., in + * addition to an Environment Controller (Enhanced Hardware Monitor and + * Fan Controller) + * + * This driver supports only the Environment Controller in the IT8705F and + * similar parts. The other devices are supported by different drivers. + * + * Supports: IT8603E Super I/O chip w/LPC interface + * IT8623E Super I/O chip w/LPC interface + * IT8705F Super I/O chip w/LPC interface + * IT8712F Super I/O chip w/LPC interface + * 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 + * IT8728F Super I/O chip w/LPC interface + * IT8758E Super I/O chip w/LPC interface + * IT8771E Super I/O chip w/LPC interface + * IT8772E Super I/O chip w/LPC interface + * IT8782F Super I/O chip w/LPC interface + * IT8783E/F Super I/O chip w/LPC interface + * Sis950 A clone of the IT8705F + * + * Copyright (C) 2001 Chris Gauthron + * Copyright (C) 2005-2010 Jean Delvare <jdelvare@suse.de> + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/init.h> @@ -54,7 +65,8 @@ #define DRVNAME "it87" -enum chips { it87, it8712, it8716, it8718, it8720 }; +enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8771, + it8772, it8782, it8783, it8603 }; static unsigned short force_id; module_param(force_id, ushort, 0); @@ -73,13 +85,18 @@ static struct platform_device *pdev; #define DEVID 0x20 /* Register: Device ID */ #define DEVREV 0x22 /* Register: Device Revision */ -static inline int -superio_inb(int reg) +static inline int superio_inb(int reg) { outb(reg, REG); return inb(VAL); } +static inline void superio_outb(int reg, int val) +{ + outb(reg, REG); + outb(val, VAL); +} + static int superio_inw(int reg) { int val; @@ -90,27 +107,32 @@ static int superio_inw(int reg) return val; } -static inline void -superio_select(int ldn) +static inline void superio_select(int ldn) { outb(DEV, REG); outb(ldn, VAL); } -static inline void -superio_enter(void) +static inline int superio_enter(void) { + /* + * Try to reserve REG and REG + 1 for exclusive access. + */ + if (!request_muxed_region(REG, 2, DRVNAME)) + return -EBUSY; + outb(0x87, REG); outb(0x01, REG); outb(0x55, REG); outb(0x55, REG); + return 0; } -static inline void -superio_exit(void) +static inline void superio_exit(void) { outb(0x02, REG); outb(0x02, VAL); + release_region(REG, 2); } /* Logical device 4 registers */ @@ -119,21 +141,33 @@ superio_exit(void) #define IT8716F_DEVID 0x8716 #define IT8718F_DEVID 0x8718 #define IT8720F_DEVID 0x8720 +#define IT8721F_DEVID 0x8721 #define IT8726F_DEVID 0x8726 +#define IT8728F_DEVID 0x8728 +#define IT8771E_DEVID 0x8771 +#define IT8772E_DEVID 0x8772 +#define IT8782F_DEVID 0x8782 +#define IT8783E_DEVID 0x8783 +#define IT8603E_DEVID 0x8603 +#define IT8623E_DEVID 0x8623 #define IT87_ACT_REG 0x30 #define IT87_BASE_REG 0x60 /* Logical device 7 registers (IT8712F and later) */ +#define IT87_SIO_GPIO1_REG 0x25 #define IT87_SIO_GPIO3_REG 0x27 #define IT87_SIO_GPIO5_REG 0x29 +#define IT87_SIO_PINX1_REG 0x2a /* Pin selection */ #define IT87_SIO_PINX2_REG 0x2c /* Pin selection */ +#define IT87_SIO_SPI_REG 0xef /* SPI function pin select */ #define IT87_SIO_VID_REG 0xfc /* VID value */ +#define IT87_SIO_BEEP_PIN_REG 0xf6 /* Beep pin mapping */ /* Update battery voltage after every reading if true */ -static int update_vbat; +static bool update_vbat; /* Not all BIOSes properly configure the PWM registers */ -static int fix_pwm_polarity; +static bool fix_pwm_polarity; /* Many IT87 constants specified below */ @@ -158,12 +192,16 @@ static int fix_pwm_polarity; #define IT87_REG_ALARM2 0x02 #define IT87_REG_ALARM3 0x03 -/* The IT8718F and IT8720F have the VID value in a different register, in - Super-I/O configuration space. */ +/* + * The IT8718F and IT8720F have the VID value in a different register, in + * Super-I/O configuration space. + */ #define IT87_REG_VID 0x0a -/* The IT8705F and IT8712F earlier than revision 0x08 use register 0x0b - for fan divisors. Later IT8712F revisions must use 16-bit tachometer - mode. */ +/* + * The IT8705F and IT8712F earlier than revision 0x08 use register 0x0b + * for fan divisors. Later IT8712F revisions must use 16-bit tachometer + * mode. + */ #define IT87_REG_FAN_DIV 0x0b #define IT87_REG_FAN_16BIT 0x0c @@ -173,9 +211,12 @@ static const u8 IT87_REG_FAN[] = { 0x0d, 0x0e, 0x0f, 0x80, 0x82 }; static const u8 IT87_REG_FAN_MIN[] = { 0x10, 0x11, 0x12, 0x84, 0x86 }; static const u8 IT87_REG_FANX[] = { 0x18, 0x19, 0x1a, 0x81, 0x83 }; static const u8 IT87_REG_FANX_MIN[] = { 0x1b, 0x1c, 0x1d, 0x85, 0x87 }; +static const u8 IT87_REG_TEMP_OFFSET[] = { 0x56, 0x57, 0x59 }; + #define IT87_REG_FAN_MAIN_CTRL 0x13 #define IT87_REG_FAN_CTL 0x14 #define IT87_REG_PWM(nr) (0x15 + (nr)) +#define IT87_REG_PWM_DUTY(nr) (0x63 + (nr) * 8) #define IT87_REG_VIN(nr) (0x20 + (nr)) #define IT87_REG_TEMP(nr) (0x29 + (nr)) @@ -187,77 +228,141 @@ static const u8 IT87_REG_FANX_MIN[] = { 0x1b, 0x1c, 0x1d, 0x85, 0x87 }; #define IT87_REG_VIN_ENABLE 0x50 #define IT87_REG_TEMP_ENABLE 0x51 +#define IT87_REG_TEMP_EXTRA 0x55 +#define IT87_REG_BEEP_ENABLE 0x5c #define IT87_REG_CHIPID 0x58 -#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) +#define IT87_REG_AUTO_TEMP(nr, i) (0x60 + (nr) * 8 + (i)) +#define IT87_REG_AUTO_PWM(nr, i) (0x65 + (nr) * 8 + (i)) -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)) +struct it87_devices { + const char *name; + u16 features; + u8 peci_mask; + u8 old_peci_mask; +}; -static const unsigned int pwm_freq[8] = { - 48000000 / 128, - 24000000 / 128, - 12000000 / 128, - 8000000 / 128, - 6000000 / 128, - 3000000 / 128, - 1500000 / 128, - 750000 / 128, +#define FEAT_12MV_ADC (1 << 0) +#define FEAT_NEWER_AUTOPWM (1 << 1) +#define FEAT_OLD_AUTOPWM (1 << 2) +#define FEAT_16BIT_FANS (1 << 3) +#define FEAT_TEMP_OFFSET (1 << 4) +#define FEAT_TEMP_PECI (1 << 5) +#define FEAT_TEMP_OLD_PECI (1 << 6) + +static const struct it87_devices it87_devices[] = { + [it87] = { + .name = "it87", + .features = FEAT_OLD_AUTOPWM, /* may need to overwrite */ + }, + [it8712] = { + .name = "it8712", + .features = FEAT_OLD_AUTOPWM, /* may need to overwrite */ + }, + [it8716] = { + .name = "it8716", + .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET, + }, + [it8718] = { + .name = "it8718", + .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET + | FEAT_TEMP_OLD_PECI, + .old_peci_mask = 0x4, + }, + [it8720] = { + .name = "it8720", + .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET + | FEAT_TEMP_OLD_PECI, + .old_peci_mask = 0x4, + }, + [it8721] = { + .name = "it8721", + .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS + | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI, + .peci_mask = 0x05, + .old_peci_mask = 0x02, /* Actually reports PCH */ + }, + [it8728] = { + .name = "it8728", + .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI, + .peci_mask = 0x07, + }, + [it8771] = { + .name = "it8771", + .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI, + /* PECI: guesswork */ + /* 12mV ADC (OHM) */ + /* 16 bit fans (OHM) */ + .peci_mask = 0x07, + }, + [it8772] = { + .name = "it8772", + .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI, + /* PECI (coreboot) */ + /* 12mV ADC (HWSensors4, OHM) */ + /* 16 bit fans (HWSensors4, OHM) */ + .peci_mask = 0x07, + }, + [it8782] = { + .name = "it8782", + .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET + | FEAT_TEMP_OLD_PECI, + .old_peci_mask = 0x4, + }, + [it8783] = { + .name = "it8783", + .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET + | FEAT_TEMP_OLD_PECI, + .old_peci_mask = 0x4, + }, + [it8603] = { + .name = "it8603", + .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI, + .peci_mask = 0x07, + }, }; +#define has_16bit_fans(data) ((data)->features & FEAT_16BIT_FANS) +#define has_12mv_adc(data) ((data)->features & FEAT_12MV_ADC) +#define has_newer_autopwm(data) ((data)->features & FEAT_NEWER_AUTOPWM) +#define has_old_autopwm(data) ((data)->features & FEAT_OLD_AUTOPWM) +#define has_temp_offset(data) ((data)->features & FEAT_TEMP_OFFSET) +#define has_temp_peci(data, nr) (((data)->features & FEAT_TEMP_PECI) && \ + ((data)->peci_mask & (1 << nr))) +#define has_temp_old_peci(data, nr) \ + (((data)->features & FEAT_TEMP_OLD_PECI) && \ + ((data)->old_peci_mask & (1 << nr))) struct it87_sio_data { enum chips type; /* Values read from Super-I/O config space */ u8 revision; u8 vid_value; + u8 beep_pin; + u8 internal; /* Internal sensors can be labeled */ /* Features skipped based on config or DMI */ + u16 skip_in; u8 skip_vid; u8 skip_fan; u8 skip_pwm; + u8 skip_temp; }; -/* For each registered chip, we need to keep some data in memory. - The structure is dynamically allocated. */ +/* + * For each registered chip, we need to keep some data in memory. + * The structure is dynamically allocated. + */ struct it87_data { struct device *hwmon_dev; enum chips type; - u8 revision; + u16 features; + u8 peci_mask; + u8 old_peci_mask; unsigned short addr; const char *name; @@ -265,39 +370,124 @@ struct it87_data { char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ - u8 in[9]; /* Register value */ - u8 in_max[8]; /* Register value */ - u8 in_min[8]; /* Register value */ + u16 in_scaled; /* Internal voltage sensors are scaled */ + u8 in[10][3]; /* [nr][0]=in, [1]=min, [2]=max */ u8 has_fan; /* Bitfield, fans enabled */ - u16 fan[5]; /* Register values, possibly combined */ - u16 fan_min[5]; /* Register values, possibly combined */ - s8 temp[3]; /* Register value */ - s8 temp_high[3]; /* Register value */ - s8 temp_low[3]; /* Register value */ - u8 sensor; /* Register value */ + u16 fan[5][2]; /* Register values, [nr][0]=fan, [1]=min */ + u8 has_temp; /* Bitfield, temp sensors enabled */ + s8 temp[3][4]; /* [nr][0]=temp, [1]=min, [2]=max, [3]=offset */ + u8 sensor; /* Register value (IT87_REG_TEMP_ENABLE) */ + u8 extra; /* Register value (IT87_REG_TEMP_EXTRA) */ u8 fan_div[3]; /* Register encoding, shifted right */ u8 vid; /* Register encoding, combined */ u8 vrm; u32 alarms; /* Register encoding, combined */ + u8 beeps; /* Register encoding */ u8 fan_main_ctrl; /* Register value */ u8 fan_ctl; /* Register value */ - u8 manual_pwm_ctl[3]; /* manual PWM value set by user */ + + /* + * The following 3 arrays correspond to the same registers up to + * the IT8720F. The meaning of bits 6-0 depends on the value of bit + * 7, and we want to preserve settings on mode changes, so we have + * to track all values separately. + * Starting with the IT8721F, the manual PWM duty cycles are stored + * in separate registers (8-bit values), so the separate tracking + * is no longer needed, but it is still done to keep the driver + * simple. + */ + u8 pwm_ctrl[3]; /* Register value */ + u8 pwm_duty[3]; /* Manual PWM value set by user */ + u8 pwm_temp_map[3]; /* PWM to temp. chan. mapping (bits 1-0) */ + + /* Automatic fan speed control registers */ + u8 auto_pwm[3][4]; /* [nr][3] is hard-coded */ + s8 auto_temp[3][5]; /* [nr][0] is point1_temp_hyst */ }; -static inline int has_16bit_fans(const struct it87_data *data) +static int adc_lsb(const struct it87_data *data, int nr) +{ + int lsb = has_12mv_adc(data) ? 12 : 16; + if (data->in_scaled & (1 << nr)) + lsb <<= 1; + return lsb; +} + +static u8 in_to_reg(const struct it87_data *data, int nr, long val) +{ + val = DIV_ROUND_CLOSEST(val, adc_lsb(data, nr)); + return clamp_val(val, 0, 255); +} + +static int in_from_reg(const struct it87_data *data, int nr, int val) +{ + return val * adc_lsb(data, nr); +} + +static inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = clamp_val(rpm, 1, 1000000); + return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254); +} + +static inline u16 FAN16_TO_REG(long rpm) +{ + if (rpm == 0) + return 0xffff; + return clamp_val((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) (clamp_val(((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) { - /* IT8705F Datasheet 0.4.1, 3h == Version G. - IT8712F Datasheet 0.9.1, section 8.3.5 indicates 8h == Version J. - These are the first revisions with 16bit tachometer support. */ - return (data->type == it87 && data->revision >= 0x03) - || (data->type == it8712 && data->revision >= 0x08) - || data->type == it8716 - || data->type == it8718 - || data->type == it8720; + if (has_newer_autopwm(data)) + return val; + else + return val >> 1; +} + +static int pwm_from_reg(const struct it87_data *data, u8 reg) +{ + if (has_newer_autopwm(data)) + 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 int it87_probe(struct platform_device *pdev); -static int __devexit it87_remove(struct platform_device *pdev); +static int it87_remove(struct platform_device *pdev); static int it87_read_value(struct it87_data *data, u8 reg); static void it87_write_value(struct it87_data *data, u8 reg, u8 value); @@ -312,245 +502,273 @@ static struct platform_driver it87_driver = { .name = DRVNAME, }, .probe = it87_probe, - .remove = __devexit_p(it87_remove), + .remove = it87_remove, }; static ssize_t show_in(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(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])); -} - -static ssize_t show_in_min(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->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[nr][index])); } -static ssize_t show_in_max(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t set_in(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(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])); -} - -static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; struct it87_data *data = dev_get_drvdata(dev); - unsigned long val = simple_strtoul(buf, NULL, 10); - - mutex_lock(&data->update_lock); - data->in_min[nr] = IN_TO_REG(val); - it87_write_value(data, IT87_REG_VIN_MIN(nr), - data->in_min[nr]); - mutex_unlock(&data->update_lock); - return count; -} -static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; + unsigned long val; - struct it87_data *data = dev_get_drvdata(dev); - unsigned long val = simple_strtoul(buf, NULL, 10); + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; mutex_lock(&data->update_lock); - data->in_max[nr] = IN_TO_REG(val); - it87_write_value(data, IT87_REG_VIN_MAX(nr), - data->in_max[nr]); + data->in[nr][index] = in_to_reg(data, nr, val); + it87_write_value(data, + index == 1 ? IT87_REG_VIN_MIN(nr) + : IT87_REG_VIN_MAX(nr), + data->in[nr][index]); mutex_unlock(&data->update_lock); return count; } -#define show_in_offset(offset) \ -static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \ - show_in, NULL, offset); - -#define limit_in_offset(offset) \ -static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ - show_in_min, set_in_min, offset); \ -static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ - show_in_max, set_in_max, offset); - -show_in_offset(0); -limit_in_offset(0); -show_in_offset(1); -limit_in_offset(1); -show_in_offset(2); -limit_in_offset(2); -show_in_offset(3); -limit_in_offset(3); -show_in_offset(4); -limit_in_offset(4); -show_in_offset(5); -limit_in_offset(5); -show_in_offset(6); -limit_in_offset(6); -show_in_offset(7); -limit_in_offset(7); -show_in_offset(8); +static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0); +static SENSOR_DEVICE_ATTR_2(in0_min, S_IRUGO | S_IWUSR, show_in, set_in, + 0, 1); +static SENSOR_DEVICE_ATTR_2(in0_max, S_IRUGO | S_IWUSR, show_in, set_in, + 0, 2); + +static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 1, 0); +static SENSOR_DEVICE_ATTR_2(in1_min, S_IRUGO | S_IWUSR, show_in, set_in, + 1, 1); +static SENSOR_DEVICE_ATTR_2(in1_max, S_IRUGO | S_IWUSR, show_in, set_in, + 1, 2); + +static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 2, 0); +static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_in, set_in, + 2, 1); +static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_in, set_in, + 2, 2); + +static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 3, 0); +static SENSOR_DEVICE_ATTR_2(in3_min, S_IRUGO | S_IWUSR, show_in, set_in, + 3, 1); +static SENSOR_DEVICE_ATTR_2(in3_max, S_IRUGO | S_IWUSR, show_in, set_in, + 3, 2); + +static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 4, 0); +static SENSOR_DEVICE_ATTR_2(in4_min, S_IRUGO | S_IWUSR, show_in, set_in, + 4, 1); +static SENSOR_DEVICE_ATTR_2(in4_max, S_IRUGO | S_IWUSR, show_in, set_in, + 4, 2); + +static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, show_in, NULL, 5, 0); +static SENSOR_DEVICE_ATTR_2(in5_min, S_IRUGO | S_IWUSR, show_in, set_in, + 5, 1); +static SENSOR_DEVICE_ATTR_2(in5_max, S_IRUGO | S_IWUSR, show_in, set_in, + 5, 2); + +static SENSOR_DEVICE_ATTR_2(in6_input, S_IRUGO, show_in, NULL, 6, 0); +static SENSOR_DEVICE_ATTR_2(in6_min, S_IRUGO | S_IWUSR, show_in, set_in, + 6, 1); +static SENSOR_DEVICE_ATTR_2(in6_max, S_IRUGO | S_IWUSR, show_in, set_in, + 6, 2); + +static SENSOR_DEVICE_ATTR_2(in7_input, S_IRUGO, show_in, NULL, 7, 0); +static SENSOR_DEVICE_ATTR_2(in7_min, S_IRUGO | S_IWUSR, show_in, set_in, + 7, 1); +static SENSOR_DEVICE_ATTR_2(in7_max, S_IRUGO | S_IWUSR, show_in, set_in, + 7, 2); + +static SENSOR_DEVICE_ATTR_2(in8_input, S_IRUGO, show_in, NULL, 8, 0); +static SENSOR_DEVICE_ATTR_2(in9_input, S_IRUGO, show_in, NULL, 9, 0); /* 3 temperatures */ static ssize_t show_temp(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr])); -} -static ssize_t show_temp_max(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[nr])); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr][index])); } -static ssize_t show_temp_min(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_low[nr])); -} -static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t set_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; struct it87_data *data = dev_get_drvdata(dev); - int val = simple_strtol(buf, NULL, 10); + long val; + u8 reg, regval; + + if (kstrtol(buf, 10, &val) < 0) + return -EINVAL; mutex_lock(&data->update_lock); - data->temp_high[nr] = TEMP_TO_REG(val); - it87_write_value(data, IT87_REG_TEMP_HIGH(nr), data->temp_high[nr]); - mutex_unlock(&data->update_lock); - return count; -} -static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct it87_data *data = dev_get_drvdata(dev); - int val = simple_strtol(buf, NULL, 10); + switch (index) { + default: + case 1: + reg = IT87_REG_TEMP_LOW(nr); + break; + case 2: + reg = IT87_REG_TEMP_HIGH(nr); + break; + case 3: + regval = it87_read_value(data, IT87_REG_BEEP_ENABLE); + if (!(regval & 0x80)) { + regval |= 0x80; + it87_write_value(data, IT87_REG_BEEP_ENABLE, regval); + } + data->valid = 0; + reg = IT87_REG_TEMP_OFFSET[nr]; + break; + } - mutex_lock(&data->update_lock); - data->temp_low[nr] = TEMP_TO_REG(val); - it87_write_value(data, IT87_REG_TEMP_LOW(nr), data->temp_low[nr]); + data->temp[nr][index] = TEMP_TO_REG(val); + it87_write_value(data, reg, data->temp[nr][index]); mutex_unlock(&data->update_lock); return count; } -#define show_temp_offset(offset) \ -static SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO, \ - show_temp, NULL, offset - 1); \ -static SENSOR_DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ - show_temp_max, set_temp_max, offset - 1); \ -static SENSOR_DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ - show_temp_min, set_temp_min, offset - 1); - -show_temp_offset(1); -show_temp_offset(2); -show_temp_offset(3); - -static ssize_t show_sensor(struct device *dev, struct device_attribute *attr, - char *buf) + +static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0); +static SENSOR_DEVICE_ATTR_2(temp1_min, S_IRUGO | S_IWUSR, show_temp, set_temp, + 0, 1); +static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 0, 2); +static SENSOR_DEVICE_ATTR_2(temp1_offset, S_IRUGO | S_IWUSR, show_temp, + set_temp, 0, 3); +static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 1, 0); +static SENSOR_DEVICE_ATTR_2(temp2_min, S_IRUGO | S_IWUSR, show_temp, set_temp, + 1, 1); +static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 1, 2); +static SENSOR_DEVICE_ATTR_2(temp2_offset, S_IRUGO | S_IWUSR, show_temp, + set_temp, 1, 3); +static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 2, 0); +static SENSOR_DEVICE_ATTR_2(temp3_min, S_IRUGO | S_IWUSR, show_temp, set_temp, + 2, 1); +static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 2, 2); +static SENSOR_DEVICE_ATTR_2(temp3_offset, S_IRUGO | S_IWUSR, show_temp, + set_temp, 2, 3); + +static ssize_t show_temp_type(struct device *dev, struct device_attribute *attr, + char *buf) { struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; - struct it87_data *data = it87_update_device(dev); - u8 reg = data->sensor; /* In case the value is updated while we use it */ - + u8 reg = data->sensor; /* In case value is updated while used */ + u8 extra = data->extra; + + if ((has_temp_peci(data, nr) && (reg >> 6 == nr + 1)) + || (has_temp_old_peci(data, nr) && (extra & 0x80))) + return sprintf(buf, "6\n"); /* Intel PECI */ if (reg & (1 << nr)) return sprintf(buf, "3\n"); /* thermal diode */ if (reg & (8 << nr)) return sprintf(buf, "4\n"); /* thermistor */ return sprintf(buf, "0\n"); /* disabled */ } -static ssize_t set_sensor(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + +static ssize_t set_temp_type(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct it87_data *data = dev_get_drvdata(dev); - int val = simple_strtol(buf, NULL, 10); + long val; + u8 reg, extra; - mutex_lock(&data->update_lock); + if (kstrtol(buf, 10, &val) < 0) + return -EINVAL; - data->sensor &= ~(1 << nr); - data->sensor &= ~(8 << nr); + reg = it87_read_value(data, IT87_REG_TEMP_ENABLE); + reg &= ~(1 << nr); + reg &= ~(8 << nr); + if (has_temp_peci(data, nr) && (reg >> 6 == nr + 1 || val == 6)) + reg &= 0x3f; + extra = it87_read_value(data, IT87_REG_TEMP_EXTRA); + if (has_temp_old_peci(data, nr) && ((extra & 0x80) || val == 6)) + extra &= 0x7f; if (val == 2) { /* backwards compatibility */ - dev_warn(dev, "Sensor type 2 is deprecated, please use 4 " - "instead\n"); + dev_warn(dev, + "Sensor type 2 is deprecated, please use 4 instead\n"); val = 4; } - /* 3 = thermal diode; 4 = thermistor; 0 = disabled */ + /* 3 = thermal diode; 4 = thermistor; 6 = Intel PECI; 0 = disabled */ if (val == 3) - data->sensor |= 1 << nr; + reg |= 1 << nr; else if (val == 4) - data->sensor |= 8 << nr; - else if (val != 0) { - mutex_unlock(&data->update_lock); + reg |= 8 << nr; + else if (has_temp_peci(data, nr) && val == 6) + reg |= (nr + 1) << 6; + else if (has_temp_old_peci(data, nr) && val == 6) + extra |= 0x80; + else if (val != 0) return -EINVAL; - } + + mutex_lock(&data->update_lock); + data->sensor = reg; + data->extra = extra; it87_write_value(data, IT87_REG_TEMP_ENABLE, data->sensor); + if (has_temp_old_peci(data, nr)) + it87_write_value(data, IT87_REG_TEMP_EXTRA, data->extra); + data->valid = 0; /* Force cache refresh */ mutex_unlock(&data->update_lock); return count; } -#define show_sensor_offset(offset) \ -static SENSOR_DEVICE_ATTR(temp##offset##_type, S_IRUGO | S_IWUSR, \ - show_sensor, set_sensor, offset - 1); -show_sensor_offset(1); -show_sensor_offset(2); -show_sensor_offset(3); +static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO | S_IWUSR, show_temp_type, + set_temp_type, 0); +static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO | S_IWUSR, show_temp_type, + set_temp_type, 1); +static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO | S_IWUSR, show_temp_type, + set_temp_type, 2); /* 3 Fans */ -static ssize_t show_fan(struct device *dev, struct device_attribute *attr, - char *buf) + +static int pwm_mode(const struct it87_data *data, int nr) { - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; + int ctrl = data->fan_main_ctrl & (1 << nr); - struct it87_data *data = it87_update_device(dev); - return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan[nr], - DIV_FROM_REG(data->fan_div[nr]))); + if (ctrl == 0 && data->type != it8603) /* Full speed */ + return 0; + if (data->pwm_ctrl[nr] & 0x80) /* Automatic mode */ + return 2; + else /* Manual mode */ + return 1; } -static ssize_t show_fan_min(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; +static ssize_t show_fan(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + int speed; struct it87_data *data = it87_update_device(dev); - return sprintf(buf,"%d\n", - FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]))); + + speed = has_16bit_fans(data) ? + FAN16_FROM_REG(data->fan[nr][index]) : + FAN_FROM_REG(data->fan[nr][index], + DIV_FROM_REG(data->fan_div[nr])); + return sprintf(buf, "%d\n", speed); } + static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr, char *buf) { @@ -560,14 +778,14 @@ static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr, struct it87_data *data = it87_update_device(dev); return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr])); } -static ssize_t show_pwm_enable(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t show_pwm_enable(struct device *dev, + struct device_attribute *attr, char *buf) { struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf,"%d\n", (data->fan_main_ctrl & (1 << nr)) ? 1 : 0); + return sprintf(buf, "%d\n", pwm_mode(data, nr)); } static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, char *buf) @@ -576,7 +794,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", data->manual_pwm_ctl[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) @@ -586,29 +805,52 @@ static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%u\n", pwm_freq[index]); } -static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + +static ssize_t set_fan(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; struct it87_data *data = dev_get_drvdata(dev); - int val = simple_strtol(buf, NULL, 10); + long val; u8 reg; + if (kstrtol(buf, 10, &val) < 0) + return -EINVAL; + mutex_lock(&data->update_lock); - reg = it87_read_value(data, IT87_REG_FAN_DIV); - switch (nr) { - case 0: data->fan_div[nr] = reg & 0x07; break; - case 1: data->fan_div[nr] = (reg >> 3) & 0x07; break; - case 2: data->fan_div[nr] = (reg & 0x40) ? 3 : 1; break; + + if (has_16bit_fans(data)) { + data->fan[nr][index] = FAN16_TO_REG(val); + it87_write_value(data, IT87_REG_FAN_MIN[nr], + data->fan[nr][index] & 0xff); + it87_write_value(data, IT87_REG_FANX_MIN[nr], + data->fan[nr][index] >> 8); + } else { + reg = it87_read_value(data, IT87_REG_FAN_DIV); + switch (nr) { + case 0: + data->fan_div[nr] = reg & 0x07; + break; + case 1: + data->fan_div[nr] = (reg >> 3) & 0x07; + break; + case 2: + data->fan_div[nr] = (reg & 0x40) ? 3 : 1; + break; + } + data->fan[nr][index] = + FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); + it87_write_value(data, IT87_REG_FAN_MIN[nr], + data->fan[nr][index]); } - data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); - it87_write_value(data, IT87_REG_FAN_MIN[nr], data->fan_min[nr]); mutex_unlock(&data->update_lock); return count; } + static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -616,15 +858,18 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = dev_get_drvdata(dev); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; int min; u8 old; + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + mutex_lock(&data->update_lock); old = it87_read_value(data, IT87_REG_FAN_DIV); /* Save fan min limit */ - min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])); + min = FAN_FROM_REG(data->fan[nr][1], DIV_FROM_REG(data->fan_div[nr])); switch (nr) { case 0: @@ -645,12 +890,38 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, it87_write_value(data, IT87_REG_FAN_DIV, val); /* Restore fan min limit */ - data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); - it87_write_value(data, IT87_REG_FAN_MIN[nr], data->fan_min[nr]); + data->fan[nr][1] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); + it87_write_value(data, IT87_REG_FAN_MIN[nr], data->fan[nr][1]); mutex_unlock(&data->update_lock); return count; } + +/* Returns 0 if OK, -EINVAL otherwise */ +static int check_trip_points(struct device *dev, int nr) +{ + const struct it87_data *data = dev_get_drvdata(dev); + int i, err = 0; + + if (has_old_autopwm(data)) { + for (i = 0; i < 3; i++) { + if (data->auto_temp[nr][i] > data->auto_temp[nr][i + 1]) + err = -EINVAL; + } + for (i = 0; i < 2; i++) { + if (data->auto_pwm[nr][i] > data->auto_pwm[nr][i + 1]) + err = -EINVAL; + } + } + + if (err) { + dev_err(dev, + "Inconsistent trip points, not switching to automatic mode\n"); + dev_err(dev, "Adjust the trip points and try again\n"); + } + return err; +} + static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -658,7 +929,20 @@ static ssize_t set_pwm_enable(struct device *dev, int nr = sensor_attr->index; struct it87_data *data = dev_get_drvdata(dev); - int val = simple_strtol(buf, NULL, 10); + long val; + + if (kstrtol(buf, 10, &val) < 0 || val < 0 || val > 2) + return -EINVAL; + + /* Check trip points before switching to automatic mode */ + if (val == 2) { + if (check_trip_points(dev, nr) < 0) + return -EINVAL; + } + + /* IT8603E does not have on/off mode */ + if (val == 0 && data->type == it8603) + return -EINVAL; mutex_lock(&data->update_lock); @@ -669,16 +953,23 @@ static ssize_t set_pwm_enable(struct device *dev, it87_write_value(data, IT87_REG_FAN_CTL, tmp | (1 << nr)); /* set on/off mode */ data->fan_main_ctrl &= ~(1 << nr); - it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl); - } else if (val == 1) { - /* set SmartGuardian mode */ - data->fan_main_ctrl |= (1 << nr); - it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl); - /* set saved pwm value, clear FAN_CTLX PWM mode bit */ - it87_write_value(data, IT87_REG_PWM(nr), PWM_TO_REG(data->manual_pwm_ctl[nr])); + it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, + data->fan_main_ctrl); } else { - mutex_unlock(&data->update_lock); - return -EINVAL; + if (val == 1) /* Manual mode */ + data->pwm_ctrl[nr] = has_newer_autopwm(data) ? + data->pwm_temp_map[nr] : + data->pwm_duty[nr]; + else /* Automatic mode */ + data->pwm_ctrl[nr] = 0x80 | data->pwm_temp_map[nr]; + it87_write_value(data, IT87_REG_PWM(nr), data->pwm_ctrl[nr]); + + if (data->type != it8603) { + /* set SmartGuardian mode */ + data->fan_main_ctrl |= (1 << nr); + it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, + data->fan_main_ctrl); + } } mutex_unlock(&data->update_lock); @@ -691,15 +982,36 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = dev_get_drvdata(dev); - int val = simple_strtol(buf, NULL, 10); + long val; - if (val < 0 || val > 255) + if (kstrtol(buf, 10, &val) < 0 || val < 0 || val > 255) return -EINVAL; mutex_lock(&data->update_lock); - data->manual_pwm_ctl[nr] = val; - if (data->fan_main_ctrl & (1 << nr)) - it87_write_value(data, IT87_REG_PWM(nr), PWM_TO_REG(data->manual_pwm_ctl[nr])); + if (has_newer_autopwm(data)) { + /* + * If we are in automatic mode, the PWM duty cycle register + * is read-only so we can't write the value. + */ + if (data->pwm_ctrl[nr] & 0x80) { + mutex_unlock(&data->update_lock); + return -EBUSY; + } + data->pwm_duty[nr] = pwm_to_reg(data, val); + it87_write_value(data, IT87_REG_PWM_DUTY(nr), + data->pwm_duty[nr]); + } else { + 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)) { + data->pwm_ctrl[nr] = data->pwm_duty[nr]; + it87_write_value(data, IT87_REG_PWM(nr), + data->pwm_ctrl[nr]); + } + } mutex_unlock(&data->update_lock); return count; } @@ -707,9 +1019,12 @@ static ssize_t set_pwm_freq(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct it87_data *data = dev_get_drvdata(dev); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; int i; + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + /* Search for the nearest available frequency */ for (i = 0; i < 7; i++) { if (val > (pwm_freq[i] + pwm_freq[i+1]) / 2) @@ -724,88 +1039,242 @@ static ssize_t set_pwm_freq(struct device *dev, return count; } - -#define show_fan_offset(offset) \ -static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ - show_fan, NULL, offset - 1); \ -static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ - show_fan_min, set_fan_min, offset - 1); \ -static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ - show_fan_div, set_fan_div, offset - 1); - -show_fan_offset(1); -show_fan_offset(2); -show_fan_offset(3); - -#define show_pwm_offset(offset) \ -static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \ - show_pwm_enable, set_pwm_enable, offset - 1); \ -static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ - show_pwm, set_pwm, offset - 1); \ -static DEVICE_ATTR(pwm##offset##_freq, \ - (offset == 1 ? S_IRUGO | S_IWUSR : S_IRUGO), \ - show_pwm_freq, (offset == 1 ? set_pwm_freq : NULL)); - -show_pwm_offset(1); -show_pwm_offset(2); -show_pwm_offset(3); - -/* A different set of callbacks for 16-bit fans */ -static ssize_t show_fan16(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t show_pwm_temp_map(struct device *dev, + struct device_attribute *attr, char *buf) { struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; + struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", FAN16_FROM_REG(data->fan[nr])); -} + int map; -static ssize_t show_fan16_min(struct device *dev, struct device_attribute *attr, - char *buf) + if (data->pwm_temp_map[nr] < 3) + map = 1 << data->pwm_temp_map[nr]; + else + map = 0; /* Should never happen */ + return sprintf(buf, "%d\n", map); +} +static ssize_t set_pwm_temp_map(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; + + struct it87_data *data = dev_get_drvdata(dev); + long val; + u8 reg; + + /* + * This check can go away if we ever support automatic fan speed + * control on newer chips. + */ + if (!has_old_autopwm(data)) { + dev_notice(dev, "Mapping change disabled for safety reasons\n"); + return -EINVAL; + } + + if (kstrtol(buf, 10, &val) < 0) + return -EINVAL; + + switch (val) { + case (1 << 0): + reg = 0x00; + break; + case (1 << 1): + reg = 0x01; + break; + case (1 << 2): + reg = 0x02; + break; + default: + return -EINVAL; + } + + mutex_lock(&data->update_lock); + data->pwm_temp_map[nr] = reg; + /* + * If we are in automatic mode, write the temp mapping immediately; + * otherwise, just store it for later use. + */ + if (data->pwm_ctrl[nr] & 0x80) { + data->pwm_ctrl[nr] = 0x80 | data->pwm_temp_map[nr]; + it87_write_value(data, IT87_REG_PWM(nr), data->pwm_ctrl[nr]); + } + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t show_auto_pwm(struct device *dev, + struct device_attribute *attr, char *buf) +{ struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", FAN16_FROM_REG(data->fan_min[nr])); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int point = sensor_attr->index; + + return sprintf(buf, "%d\n", + pwm_from_reg(data, data->auto_pwm[nr][point])); } -static ssize_t set_fan16_min(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t set_auto_pwm(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; struct it87_data *data = dev_get_drvdata(dev); - int val = simple_strtol(buf, NULL, 10); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int point = sensor_attr->index; + long val; + + if (kstrtol(buf, 10, &val) < 0 || val < 0 || val > 255) + return -EINVAL; mutex_lock(&data->update_lock); - data->fan_min[nr] = FAN16_TO_REG(val); - it87_write_value(data, IT87_REG_FAN_MIN[nr], - data->fan_min[nr] & 0xff); - it87_write_value(data, IT87_REG_FANX_MIN[nr], - data->fan_min[nr] >> 8); + 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); return count; } -/* We want to use the same sysfs file names as 8-bit fans, but we need - different variable names, so we have to use SENSOR_ATTR instead of - SENSOR_DEVICE_ATTR. */ -#define show_fan16_offset(offset) \ -static struct sensor_device_attribute sensor_dev_attr_fan##offset##_input16 \ - = SENSOR_ATTR(fan##offset##_input, S_IRUGO, \ - show_fan16, NULL, offset - 1); \ -static struct sensor_device_attribute sensor_dev_attr_fan##offset##_min16 \ - = SENSOR_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ - show_fan16_min, set_fan16_min, offset - 1) - -show_fan16_offset(1); -show_fan16_offset(2); -show_fan16_offset(3); -show_fan16_offset(4); -show_fan16_offset(5); +static ssize_t show_auto_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct it87_data *data = it87_update_device(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int point = sensor_attr->index; + + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->auto_temp[nr][point])); +} + +static ssize_t set_auto_temp(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct it87_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sensor_attr = + to_sensor_dev_attr_2(attr); + int nr = sensor_attr->nr; + int point = sensor_attr->index; + long val; + + if (kstrtol(buf, 10, &val) < 0 || val < -128000 || val > 127000) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->auto_temp[nr][point] = TEMP_TO_REG(val); + it87_write_value(data, IT87_REG_AUTO_TEMP(nr, point), + data->auto_temp[nr][point]); + mutex_unlock(&data->update_lock); + return count; +} + +static SENSOR_DEVICE_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0); +static SENSOR_DEVICE_ATTR_2(fan1_min, S_IRUGO | S_IWUSR, show_fan, set_fan, + 0, 1); +static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, show_fan_div, + set_fan_div, 0); + +static SENSOR_DEVICE_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 1, 0); +static SENSOR_DEVICE_ATTR_2(fan2_min, S_IRUGO | S_IWUSR, show_fan, set_fan, + 1, 1); +static SENSOR_DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR, show_fan_div, + set_fan_div, 1); + +static SENSOR_DEVICE_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 2, 0); +static SENSOR_DEVICE_ATTR_2(fan3_min, S_IRUGO | S_IWUSR, show_fan, set_fan, + 2, 1); +static SENSOR_DEVICE_ATTR(fan3_div, S_IRUGO | S_IWUSR, show_fan_div, + set_fan_div, 2); + +static SENSOR_DEVICE_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 3, 0); +static SENSOR_DEVICE_ATTR_2(fan4_min, S_IRUGO | S_IWUSR, show_fan, set_fan, + 3, 1); + +static SENSOR_DEVICE_ATTR_2(fan5_input, S_IRUGO, show_fan, NULL, 4, 0); +static SENSOR_DEVICE_ATTR_2(fan5_min, S_IRUGO | S_IWUSR, show_fan, set_fan, + 4, 1); + +static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, + show_pwm_enable, set_pwm_enable, 0); +static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 0); +static DEVICE_ATTR(pwm1_freq, S_IRUGO | S_IWUSR, show_pwm_freq, set_pwm_freq); +static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, S_IRUGO | S_IWUSR, + show_pwm_temp_map, set_pwm_temp_map, 0); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO | S_IWUSR, + show_auto_pwm, set_auto_pwm, 0, 0); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO | S_IWUSR, + show_auto_pwm, set_auto_pwm, 0, 1); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point3_pwm, S_IRUGO | S_IWUSR, + show_auto_pwm, set_auto_pwm, 0, 2); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point4_pwm, S_IRUGO, + show_auto_pwm, NULL, 0, 3); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 0, 1); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 0, 0); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point2_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 0, 2); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point3_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 0, 3); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point4_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 0, 4); + +static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR, + show_pwm_enable, set_pwm_enable, 1); +static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 1); +static DEVICE_ATTR(pwm2_freq, S_IRUGO, show_pwm_freq, NULL); +static SENSOR_DEVICE_ATTR(pwm2_auto_channels_temp, S_IRUGO | S_IWUSR, + show_pwm_temp_map, set_pwm_temp_map, 1); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO | S_IWUSR, + show_auto_pwm, set_auto_pwm, 1, 0); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO | S_IWUSR, + show_auto_pwm, set_auto_pwm, 1, 1); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point3_pwm, S_IRUGO | S_IWUSR, + show_auto_pwm, set_auto_pwm, 1, 2); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point4_pwm, S_IRUGO, + show_auto_pwm, NULL, 1, 3); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 1, 1); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 1, 0); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point2_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 1, 2); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point3_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 1, 3); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point4_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 1, 4); + +static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR, + show_pwm_enable, set_pwm_enable, 2); +static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 2); +static DEVICE_ATTR(pwm3_freq, S_IRUGO, show_pwm_freq, NULL); +static SENSOR_DEVICE_ATTR(pwm3_auto_channels_temp, S_IRUGO | S_IWUSR, + show_pwm_temp_map, set_pwm_temp_map, 2); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO | S_IWUSR, + show_auto_pwm, set_auto_pwm, 2, 0); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO | S_IWUSR, + show_auto_pwm, set_auto_pwm, 2, 1); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point3_pwm, S_IRUGO | S_IWUSR, + show_auto_pwm, set_auto_pwm, 2, 2); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point4_pwm, S_IRUGO, + show_auto_pwm, NULL, 2, 3); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 2, 1); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 2, 0); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 2, 2); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point3_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 2, 3); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point4_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 2, 4); /* Alarms */ -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, + char *buf) { struct it87_data *data = it87_update_device(dev); return sprintf(buf, "%u\n", data->alarms); @@ -819,6 +1288,32 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, struct it87_data *data = it87_update_device(dev); return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); } + +static ssize_t clear_intrusion(struct device *dev, struct device_attribute + *attr, const char *buf, size_t count) +{ + struct it87_data *data = dev_get_drvdata(dev); + long val; + int config; + + if (kstrtol(buf, 10, &val) < 0 || val != 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + config = it87_read_value(data, IT87_REG_CONFIG); + if (config < 0) { + count = config; + } else { + config |= 1 << 5; + it87_write_value(data, IT87_REG_CONFIG, config); + /* Invalidate cache to force re-read */ + data->valid = 0; + } + mutex_unlock(&data->update_lock); + + return count; +} + static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 8); static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 9); static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 10); @@ -835,34 +1330,112 @@ static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 6); static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 16); static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 17); static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 18); +static SENSOR_DEVICE_ATTR(intrusion0_alarm, S_IRUGO | S_IWUSR, + show_alarm, clear_intrusion, 4); + +static ssize_t show_beep(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int bitnr = to_sensor_dev_attr(attr)->index; + struct it87_data *data = it87_update_device(dev); + return sprintf(buf, "%u\n", (data->beeps >> bitnr) & 1); +} +static ssize_t set_beep(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int bitnr = to_sensor_dev_attr(attr)->index; + struct it87_data *data = dev_get_drvdata(dev); + long val; -static ssize_t -show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf) + if (kstrtol(buf, 10, &val) < 0 + || (val != 0 && val != 1)) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->beeps = it87_read_value(data, IT87_REG_BEEP_ENABLE); + if (val) + data->beeps |= (1 << bitnr); + else + data->beeps &= ~(1 << bitnr); + it87_write_value(data, IT87_REG_BEEP_ENABLE, data->beeps); + mutex_unlock(&data->update_lock); + return count; +} + +static SENSOR_DEVICE_ATTR(in0_beep, S_IRUGO | S_IWUSR, + show_beep, set_beep, 1); +static SENSOR_DEVICE_ATTR(in1_beep, S_IRUGO, show_beep, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_beep, S_IRUGO, show_beep, NULL, 1); +static SENSOR_DEVICE_ATTR(in3_beep, S_IRUGO, show_beep, NULL, 1); +static SENSOR_DEVICE_ATTR(in4_beep, S_IRUGO, show_beep, NULL, 1); +static SENSOR_DEVICE_ATTR(in5_beep, S_IRUGO, show_beep, NULL, 1); +static SENSOR_DEVICE_ATTR(in6_beep, S_IRUGO, show_beep, NULL, 1); +static SENSOR_DEVICE_ATTR(in7_beep, S_IRUGO, show_beep, NULL, 1); +/* fanX_beep writability is set later */ +static SENSOR_DEVICE_ATTR(fan1_beep, S_IRUGO, show_beep, set_beep, 0); +static SENSOR_DEVICE_ATTR(fan2_beep, S_IRUGO, show_beep, set_beep, 0); +static SENSOR_DEVICE_ATTR(fan3_beep, S_IRUGO, show_beep, set_beep, 0); +static SENSOR_DEVICE_ATTR(fan4_beep, S_IRUGO, show_beep, set_beep, 0); +static SENSOR_DEVICE_ATTR(fan5_beep, S_IRUGO, show_beep, set_beep, 0); +static SENSOR_DEVICE_ATTR(temp1_beep, S_IRUGO | S_IWUSR, + show_beep, set_beep, 2); +static SENSOR_DEVICE_ATTR(temp2_beep, S_IRUGO, show_beep, NULL, 2); +static SENSOR_DEVICE_ATTR(temp3_beep, S_IRUGO, show_beep, NULL, 2); + +static ssize_t show_vrm_reg(struct device *dev, struct device_attribute *attr, + char *buf) { struct it87_data *data = dev_get_drvdata(dev); return sprintf(buf, "%u\n", data->vrm); } -static ssize_t -store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t store_vrm_reg(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct it87_data *data = dev_get_drvdata(dev); - u32 val; + unsigned long val; + + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; - val = simple_strtoul(buf, NULL, 10); data->vrm = val; return count; } static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); -static ssize_t -show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, + char *buf) { struct it87_data *data = it87_update_device(dev); return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); } static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); +static ssize_t show_label(struct device *dev, struct device_attribute *attr, + char *buf) +{ + static const char * const labels[] = { + "+5V", + "5VSB", + "Vbat", + }; + static const char * const 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", has_12mv_adc(data) ? 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); +static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_label, NULL, 2); +/* special AVCC3 IT8603E in9 */ +static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL, 0); + static ssize_t show_name(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -871,58 +1444,115 @@ static ssize_t show_name(struct device *dev, struct device_attribute } static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); -static struct attribute *it87_attributes[] = { +static struct attribute *it87_attributes_in[10][5] = { +{ &sensor_dev_attr_in0_input.dev_attr.attr, - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_in2_input.dev_attr.attr, - &sensor_dev_attr_in3_input.dev_attr.attr, - &sensor_dev_attr_in4_input.dev_attr.attr, - &sensor_dev_attr_in5_input.dev_attr.attr, - &sensor_dev_attr_in6_input.dev_attr.attr, - &sensor_dev_attr_in7_input.dev_attr.attr, - &sensor_dev_attr_in8_input.dev_attr.attr, &sensor_dev_attr_in0_min.dev_attr.attr, - &sensor_dev_attr_in1_min.dev_attr.attr, - &sensor_dev_attr_in2_min.dev_attr.attr, - &sensor_dev_attr_in3_min.dev_attr.attr, - &sensor_dev_attr_in4_min.dev_attr.attr, - &sensor_dev_attr_in5_min.dev_attr.attr, - &sensor_dev_attr_in6_min.dev_attr.attr, - &sensor_dev_attr_in7_min.dev_attr.attr, &sensor_dev_attr_in0_max.dev_attr.attr, - &sensor_dev_attr_in1_max.dev_attr.attr, - &sensor_dev_attr_in2_max.dev_attr.attr, - &sensor_dev_attr_in3_max.dev_attr.attr, - &sensor_dev_attr_in4_max.dev_attr.attr, - &sensor_dev_attr_in5_max.dev_attr.attr, - &sensor_dev_attr_in6_max.dev_attr.attr, - &sensor_dev_attr_in7_max.dev_attr.attr, &sensor_dev_attr_in0_alarm.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, &sensor_dev_attr_in1_alarm.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, &sensor_dev_attr_in2_alarm.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, &sensor_dev_attr_in3_alarm.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, &sensor_dev_attr_in4_alarm.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, &sensor_dev_attr_in5_alarm.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in6_min.dev_attr.attr, + &sensor_dev_attr_in6_max.dev_attr.attr, &sensor_dev_attr_in6_alarm.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in7_min.dev_attr.attr, + &sensor_dev_attr_in7_max.dev_attr.attr, &sensor_dev_attr_in7_alarm.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_in8_input.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_in9_input.dev_attr.attr, + NULL +} }; + +static const struct attribute_group it87_group_in[10] = { + { .attrs = it87_attributes_in[0] }, + { .attrs = it87_attributes_in[1] }, + { .attrs = it87_attributes_in[2] }, + { .attrs = it87_attributes_in[3] }, + { .attrs = it87_attributes_in[4] }, + { .attrs = it87_attributes_in[5] }, + { .attrs = it87_attributes_in[6] }, + { .attrs = it87_attributes_in[7] }, + { .attrs = it87_attributes_in[8] }, + { .attrs = it87_attributes_in[9] }, +}; +static struct attribute *it87_attributes_temp[3][6] = { +{ &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp3_input.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp3_max.dev_attr.attr, &sensor_dev_attr_temp1_min.dev_attr.attr, - &sensor_dev_attr_temp2_min.dev_attr.attr, - &sensor_dev_attr_temp3_min.dev_attr.attr, &sensor_dev_attr_temp1_type.dev_attr.attr, - &sensor_dev_attr_temp2_type.dev_attr.attr, - &sensor_dev_attr_temp3_type.dev_attr.attr, &sensor_dev_attr_temp1_alarm.dev_attr.attr, + NULL +} , { + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_type.dev_attr.attr, &sensor_dev_attr_temp2_alarm.dev_attr.attr, + NULL +} , { + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp3_type.dev_attr.attr, &sensor_dev_attr_temp3_alarm.dev_attr.attr, + NULL +} }; +static const struct attribute_group it87_group_temp[3] = { + { .attrs = it87_attributes_temp[0] }, + { .attrs = it87_attributes_temp[1] }, + { .attrs = it87_attributes_temp[2] }, +}; + +static struct attribute *it87_attributes_temp_offset[] = { + &sensor_dev_attr_temp1_offset.dev_attr.attr, + &sensor_dev_attr_temp2_offset.dev_attr.attr, + &sensor_dev_attr_temp3_offset.dev_attr.attr, +}; + +static struct attribute *it87_attributes[] = { &dev_attr_alarms.attr, + &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, &dev_attr_name.attr, NULL }; @@ -931,62 +1561,176 @@ static const struct attribute_group it87_group = { .attrs = it87_attributes, }; -static struct attribute *it87_attributes_opt[] = { - &sensor_dev_attr_fan1_input16.dev_attr.attr, - &sensor_dev_attr_fan1_min16.dev_attr.attr, - &sensor_dev_attr_fan2_input16.dev_attr.attr, - &sensor_dev_attr_fan2_min16.dev_attr.attr, - &sensor_dev_attr_fan3_input16.dev_attr.attr, - &sensor_dev_attr_fan3_min16.dev_attr.attr, - &sensor_dev_attr_fan4_input16.dev_attr.attr, - &sensor_dev_attr_fan4_min16.dev_attr.attr, - &sensor_dev_attr_fan5_input16.dev_attr.attr, - &sensor_dev_attr_fan5_min16.dev_attr.attr, +static struct attribute *it87_attributes_in_beep[] = { + &sensor_dev_attr_in0_beep.dev_attr.attr, + &sensor_dev_attr_in1_beep.dev_attr.attr, + &sensor_dev_attr_in2_beep.dev_attr.attr, + &sensor_dev_attr_in3_beep.dev_attr.attr, + &sensor_dev_attr_in4_beep.dev_attr.attr, + &sensor_dev_attr_in5_beep.dev_attr.attr, + &sensor_dev_attr_in6_beep.dev_attr.attr, + &sensor_dev_attr_in7_beep.dev_attr.attr, + NULL, + NULL, +}; +static struct attribute *it87_attributes_temp_beep[] = { + &sensor_dev_attr_temp1_beep.dev_attr.attr, + &sensor_dev_attr_temp2_beep.dev_attr.attr, + &sensor_dev_attr_temp3_beep.dev_attr.attr, +}; + +static struct attribute *it87_attributes_fan[5][3+1] = { { &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan1_min.dev_attr.attr, - &sensor_dev_attr_fan1_div.dev_attr.attr, + &sensor_dev_attr_fan1_alarm.dev_attr.attr, + NULL +}, { &sensor_dev_attr_fan2_input.dev_attr.attr, &sensor_dev_attr_fan2_min.dev_attr.attr, - &sensor_dev_attr_fan2_div.dev_attr.attr, + &sensor_dev_attr_fan2_alarm.dev_attr.attr, + NULL +}, { &sensor_dev_attr_fan3_input.dev_attr.attr, &sensor_dev_attr_fan3_min.dev_attr.attr, - &sensor_dev_attr_fan3_div.dev_attr.attr, - - &sensor_dev_attr_fan1_alarm.dev_attr.attr, - &sensor_dev_attr_fan2_alarm.dev_attr.attr, &sensor_dev_attr_fan3_alarm.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan4_min.dev_attr.attr, &sensor_dev_attr_fan4_alarm.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_fan5_input.dev_attr.attr, + &sensor_dev_attr_fan5_min.dev_attr.attr, &sensor_dev_attr_fan5_alarm.dev_attr.attr, + NULL +} }; + +static const struct attribute_group it87_group_fan[5] = { + { .attrs = it87_attributes_fan[0] }, + { .attrs = it87_attributes_fan[1] }, + { .attrs = it87_attributes_fan[2] }, + { .attrs = it87_attributes_fan[3] }, + { .attrs = it87_attributes_fan[4] }, +}; +static const struct attribute *it87_attributes_fan_div[] = { + &sensor_dev_attr_fan1_div.dev_attr.attr, + &sensor_dev_attr_fan2_div.dev_attr.attr, + &sensor_dev_attr_fan3_div.dev_attr.attr, +}; + +static struct attribute *it87_attributes_pwm[3][4+1] = { { &sensor_dev_attr_pwm1_enable.dev_attr.attr, - &sensor_dev_attr_pwm2_enable.dev_attr.attr, - &sensor_dev_attr_pwm3_enable.dev_attr.attr, &sensor_dev_attr_pwm1.dev_attr.attr, - &sensor_dev_attr_pwm2.dev_attr.attr, - &sensor_dev_attr_pwm3.dev_attr.attr, &dev_attr_pwm1_freq.attr, + &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_pwm2.dev_attr.attr, &dev_attr_pwm2_freq.attr, + &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_pwm3_enable.dev_attr.attr, + &sensor_dev_attr_pwm3.dev_attr.attr, &dev_attr_pwm3_freq.attr, + &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr, + NULL +} }; + +static const struct attribute_group it87_group_pwm[3] = { + { .attrs = it87_attributes_pwm[0] }, + { .attrs = it87_attributes_pwm[1] }, + { .attrs = it87_attributes_pwm[2] }, +}; + +static struct attribute *it87_attributes_autopwm[3][9+1] = { { + &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point3_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point4_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point4_temp.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point3_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point4_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point4_temp.dev_attr.attr, + NULL +} }; + +static const struct attribute_group it87_group_autopwm[3] = { + { .attrs = it87_attributes_autopwm[0] }, + { .attrs = it87_attributes_autopwm[1] }, + { .attrs = it87_attributes_autopwm[2] }, +}; + +static struct attribute *it87_attributes_fan_beep[] = { + &sensor_dev_attr_fan1_beep.dev_attr.attr, + &sensor_dev_attr_fan2_beep.dev_attr.attr, + &sensor_dev_attr_fan3_beep.dev_attr.attr, + &sensor_dev_attr_fan4_beep.dev_attr.attr, + &sensor_dev_attr_fan5_beep.dev_attr.attr, +}; +static struct attribute *it87_attributes_vid[] = { &dev_attr_vrm.attr, &dev_attr_cpu0_vid.attr, NULL }; -static const struct attribute_group it87_group_opt = { - .attrs = it87_attributes_opt, +static const struct attribute_group it87_group_vid = { + .attrs = it87_attributes_vid, +}; + +static struct attribute *it87_attributes_label[] = { + &sensor_dev_attr_in3_label.dev_attr.attr, + &sensor_dev_attr_in7_label.dev_attr.attr, + &sensor_dev_attr_in8_label.dev_attr.attr, + &sensor_dev_attr_in9_label.dev_attr.attr, + NULL +}; + +static const struct attribute_group it87_group_label = { + .attrs = it87_attributes_label, }; /* SuperIO detection - will change isa_address if a chip is found */ static int __init it87_find(unsigned short *address, struct it87_sio_data *sio_data) { - int err = -ENODEV; + int err; u16 chip_type; const char *board_vendor, *board_name; - superio_enter(); + err = superio_enter(); + if (err) + return err; + + err = -ENODEV; chip_type = force_id ? force_id : superio_inw(DEVID); switch (chip_type) { @@ -1006,44 +1750,188 @@ 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 IT8728F_DEVID: + sio_data->type = it8728; + break; + case IT8771E_DEVID: + sio_data->type = it8771; + break; + case IT8772E_DEVID: + sio_data->type = it8772; + break; + case IT8782F_DEVID: + sio_data->type = it8782; + break; + case IT8783E_DEVID: + sio_data->type = it8783; + break; + case IT8603E_DEVID: + case IT8623E_DEVID: + sio_data->type = it8603; + break; case 0xffff: /* No device at all */ goto exit; default: - pr_debug(DRVNAME ": Unsupported chip (DEVID=0x%x)\n", - chip_type); + pr_debug("Unsupported chip (DEVID=0x%x)\n", chip_type); goto exit; } superio_select(PME); if (!(superio_inb(IT87_ACT_REG) & 0x01)) { - pr_info("it87: Device not activated, skipping\n"); + pr_info("Device not activated, skipping\n"); goto exit; } *address = superio_inw(IT87_BASE_REG) & ~(IT87_EXTENT - 1); if (*address == 0) { - pr_info("it87: Base address not set, skipping\n"); + pr_info("Base address not set, skipping\n"); goto exit; } err = 0; sio_data->revision = superio_inb(DEVREV) & 0x0f; - pr_info("it87: Found IT%04xF chip at 0x%x, revision %d\n", - chip_type, *address, sio_data->revision); + pr_info("Found IT%04x%c chip at 0x%x, revision %d\n", chip_type, + chip_type == 0x8771 || chip_type == 0x8772 || + chip_type == 0x8603 ? 'E' : 'F', *address, + sio_data->revision); + + /* in8 (Vbat) is always internal */ + sio_data->internal = (1 << 2); + /* Only the IT8603E has in9 */ + if (sio_data->type != it8603) + sio_data->skip_in |= (1 << 9); /* Read GPIO config and VID value from LDN 7 (GPIO) */ if (sio_data->type == it87) { /* The IT8705F doesn't have VID pins at all */ sio_data->skip_vid = 1; + + /* The IT8705F has a different LD number for GPIO */ + superio_select(5); + sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; + } else if (sio_data->type == it8783) { + int reg25, reg27, reg2a, reg2c, regef; + + sio_data->skip_vid = 1; /* No VID */ + + superio_select(GPIO); + + reg25 = superio_inb(IT87_SIO_GPIO1_REG); + reg27 = superio_inb(IT87_SIO_GPIO3_REG); + reg2a = superio_inb(IT87_SIO_PINX1_REG); + reg2c = superio_inb(IT87_SIO_PINX2_REG); + regef = superio_inb(IT87_SIO_SPI_REG); + + /* Check if fan3 is there or not */ + if ((reg27 & (1 << 0)) || !(reg2c & (1 << 2))) + sio_data->skip_fan |= (1 << 2); + if ((reg25 & (1 << 4)) + || (!(reg2a & (1 << 1)) && (regef & (1 << 0)))) + sio_data->skip_pwm |= (1 << 2); + + /* Check if fan2 is there or not */ + if (reg27 & (1 << 7)) + sio_data->skip_fan |= (1 << 1); + if (reg27 & (1 << 3)) + sio_data->skip_pwm |= (1 << 1); + + /* VIN5 */ + if ((reg27 & (1 << 0)) || (reg2c & (1 << 2))) + sio_data->skip_in |= (1 << 5); /* No VIN5 */ + + /* VIN6 */ + if (reg27 & (1 << 1)) + sio_data->skip_in |= (1 << 6); /* No VIN6 */ + + /* + * VIN7 + * Does not depend on bit 2 of Reg2C, contrary to datasheet. + */ + if (reg27 & (1 << 2)) { + /* + * The data sheet is a bit unclear regarding the + * internal voltage divider for VCCH5V. It says + * "This bit enables and switches VIN7 (pin 91) to the + * internal voltage divider for VCCH5V". + * This is different to other chips, where the internal + * voltage divider would connect VIN7 to an internal + * voltage source. Maybe that is the case here as well. + * + * Since we don't know for sure, re-route it if that is + * not the case, and ask the user to report if the + * resulting voltage is sane. + */ + if (!(reg2c & (1 << 1))) { + reg2c |= (1 << 1); + superio_outb(IT87_SIO_PINX2_REG, reg2c); + pr_notice("Routing internal VCCH5V to in7.\n"); + } + pr_notice("in7 routed to internal voltage divider, with external pin disabled.\n"); + pr_notice("Please report if it displays a reasonable voltage.\n"); + } + + if (reg2c & (1 << 0)) + sio_data->internal |= (1 << 0); + if (reg2c & (1 << 1)) + sio_data->internal |= (1 << 1); + + sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; + } else if (sio_data->type == it8603) { + int reg27, reg29; + + sio_data->skip_vid = 1; /* No VID */ + superio_select(GPIO); + + reg27 = superio_inb(IT87_SIO_GPIO3_REG); + + /* Check if fan3 is there or not */ + if (reg27 & (1 << 6)) + sio_data->skip_pwm |= (1 << 2); + if (reg27 & (1 << 7)) + sio_data->skip_fan |= (1 << 2); + + /* Check if fan2 is there or not */ + reg29 = superio_inb(IT87_SIO_GPIO5_REG); + if (reg29 & (1 << 1)) + sio_data->skip_pwm |= (1 << 1); + if (reg29 & (1 << 2)) + sio_data->skip_fan |= (1 << 1); + + sio_data->skip_in |= (1 << 5); /* No VIN5 */ + sio_data->skip_in |= (1 << 6); /* No VIN6 */ + + /* no fan4 */ + sio_data->skip_pwm |= (1 << 3); + sio_data->skip_fan |= (1 << 3); + + sio_data->internal |= (1 << 1); /* in7 is VSB */ + sio_data->internal |= (1 << 3); /* in9 is AVCC */ + + sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; } else { int reg; + bool uart6; 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 || sio_data->type == it8728 || + sio_data->type == it8771 || sio_data->type == it8772 || + sio_data->type == it8782) { + /* + * IT8721F/IT8758E, and IT8782F don't have VID pins + * at all, not sure about the IT8728F and compatibles. + */ sio_data->skip_vid = 1; + } else { + /* We need at least 4 VID pins */ + if (reg & 0x0f) { + pr_info("VID is disabled (pins used for GPIO)\n"); + sio_data->skip_vid = 1; + } } /* Check if fan3 is there or not */ @@ -1064,11 +1952,54 @@ static int __init it87_find(unsigned short *address, sio_data->vid_value = superio_inb(IT87_SIO_VID_REG); reg = superio_inb(IT87_SIO_PINX2_REG); + + uart6 = sio_data->type == it8782 && (reg & (1 << 2)); + + /* + * The IT8720F has no VIN7 pin, so VCCH should always be + * routed internally to VIN7 with an internal divider. + * Curiously, there still is a configuration bit to control + * this, which means it can be set incorrectly. And even + * more curiously, many boards out there are improperly + * configured, even though the IT8720F datasheet claims + * that the internal routing of VCCH to VIN7 is the default + * setting. So we force the internal routing in this case. + * + * On IT8782F, VIN7 is multiplexed with one of the UART6 pins. + * If UART6 is enabled, re-route VIN7 to the internal divider + * if that is not already the case. + */ + if ((sio_data->type == it8720 || uart6) && !(reg & (1 << 1))) { + reg |= (1 << 1); + superio_outb(IT87_SIO_PINX2_REG, reg); + pr_notice("Routing internal VCCH to in7\n"); + } if (reg & (1 << 0)) - pr_info("it87: in3 is VCC (+5V)\n"); - if (reg & (1 << 1)) - pr_info("it87: in7 is VCCH (+5V Stand-By)\n"); + sio_data->internal |= (1 << 0); + if ((reg & (1 << 1)) || sio_data->type == it8721 || + sio_data->type == it8728 || + sio_data->type == it8771 || + sio_data->type == it8772) + sio_data->internal |= (1 << 1); + + /* + * On IT8782F, UART6 pins overlap with VIN5, VIN6, and VIN7. + * While VIN7 can be routed to the internal voltage divider, + * VIN5 and VIN6 are not available if UART6 is enabled. + * + * Also, temp3 is not available if UART6 is enabled and TEMPIN3 + * is the temperature source. Since we can not read the + * temperature source here, skip_temp is preliminary. + */ + if (uart6) { + sio_data->skip_in |= (1 << 5) | (1 << 6); + sio_data->skip_temp |= (1 << 2); + } + + sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; } + if (sio_data->beep_pin) + pr_info("Beeping is supported\n"); /* Disable specific features based on DMI strings */ board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); @@ -1076,14 +2007,15 @@ static int __init it87_find(unsigned short *address, if (board_vendor && board_name) { if (strcmp(board_vendor, "nVIDIA") == 0 && strcmp(board_name, "FN68PT") == 0) { - /* On the Shuttle SN68PT, FAN_CTL2 is apparently not - connected to a fan, but to something else. One user - has reported instant system power-off when changing - the PWM2 duty cycle, so we disable it. - I use the board name string as the trigger in case - the same board is ever used in other systems. */ - pr_info("it87: Disabling pwm2 due to " - "hardware constraints\n"); + /* + * On the Shuttle SN68PT, FAN_CTL2 is apparently not + * connected to a fan, but to something else. One user + * has reported instant system power-off when changing + * the PWM2 duty cycle, so we disable it. + * I use the board name string as the trigger in case + * the same board is ever used in other systems. + */ + pr_info("Disabling pwm2 due to hardware constraints\n"); sio_data->skip_pwm = (1 << 1); } } @@ -1093,47 +2025,111 @@ exit: return err; } -static int __devinit it87_probe(struct platform_device *pdev) +static void it87_remove_files(struct device *dev) +{ + struct it87_data *data = platform_get_drvdata(pdev); + struct it87_sio_data *sio_data = dev_get_platdata(dev); + int i; + + sysfs_remove_group(&dev->kobj, &it87_group); + for (i = 0; i < 10; i++) { + if (sio_data->skip_in & (1 << i)) + continue; + sysfs_remove_group(&dev->kobj, &it87_group_in[i]); + if (it87_attributes_in_beep[i]) + sysfs_remove_file(&dev->kobj, + it87_attributes_in_beep[i]); + } + for (i = 0; i < 3; i++) { + if (!(data->has_temp & (1 << i))) + continue; + sysfs_remove_group(&dev->kobj, &it87_group_temp[i]); + if (has_temp_offset(data)) + sysfs_remove_file(&dev->kobj, + it87_attributes_temp_offset[i]); + if (sio_data->beep_pin) + sysfs_remove_file(&dev->kobj, + it87_attributes_temp_beep[i]); + } + for (i = 0; i < 5; i++) { + if (!(data->has_fan & (1 << i))) + continue; + sysfs_remove_group(&dev->kobj, &it87_group_fan[i]); + if (sio_data->beep_pin) + sysfs_remove_file(&dev->kobj, + it87_attributes_fan_beep[i]); + if (i < 3 && !has_16bit_fans(data)) + sysfs_remove_file(&dev->kobj, + it87_attributes_fan_div[i]); + } + for (i = 0; i < 3; i++) { + if (sio_data->skip_pwm & (1 << 0)) + continue; + sysfs_remove_group(&dev->kobj, &it87_group_pwm[i]); + if (has_old_autopwm(data)) + sysfs_remove_group(&dev->kobj, + &it87_group_autopwm[i]); + } + if (!sio_data->skip_vid) + sysfs_remove_group(&dev->kobj, &it87_group_vid); + sysfs_remove_group(&dev->kobj, &it87_group_label); +} + +static int it87_probe(struct platform_device *pdev) { struct it87_data *data; struct resource *res; struct device *dev = &pdev->dev; - struct it87_sio_data *sio_data = dev->platform_data; - int err = 0; + struct it87_sio_data *sio_data = dev_get_platdata(dev); + int err = 0, i; int enable_pwm_interface; - static const char *names[] = { - "it87", - "it8712", - "it8716", - "it8718", - "it8720", - }; + int fan_beep_need_rw; res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start, IT87_EC_EXTENT, DRVNAME)) { + if (!devm_request_region(&pdev->dev, res->start, IT87_EC_EXTENT, + DRVNAME)) { dev_err(dev, "Failed to request region 0x%lx-0x%lx\n", (unsigned long)res->start, (unsigned long)(res->start + IT87_EC_EXTENT - 1)); - err = -EBUSY; - goto ERROR0; + return -EBUSY; } - if (!(data = kzalloc(sizeof(struct it87_data), GFP_KERNEL))) { - err = -ENOMEM; - goto ERROR1; - } + data = devm_kzalloc(&pdev->dev, sizeof(struct it87_data), GFP_KERNEL); + if (!data) + return -ENOMEM; data->addr = res->start; data->type = sio_data->type; - data->revision = sio_data->revision; - data->name = names[sio_data->type]; + data->features = it87_devices[sio_data->type].features; + data->peci_mask = it87_devices[sio_data->type].peci_mask; + data->old_peci_mask = it87_devices[sio_data->type].old_peci_mask; + data->name = it87_devices[sio_data->type].name; + /* + * IT8705F Datasheet 0.4.1, 3h == Version G. + * IT8712F Datasheet 0.9.1, section 8.3.5 indicates 8h == Version J. + * These are the first revisions with 16-bit tachometer support. + */ + switch (data->type) { + case it87: + if (sio_data->revision >= 0x03) { + data->features &= ~FEAT_OLD_AUTOPWM; + data->features |= FEAT_16BIT_FANS; + } + break; + case it8712: + if (sio_data->revision >= 0x08) { + data->features &= ~FEAT_OLD_AUTOPWM; + data->features |= FEAT_16BIT_FANS; + } + break; + default: + break; + } /* Now, we do the remaining detection. */ if ((it87_read_value(data, IT87_REG_CONFIG) & 0x80) - || it87_read_value(data, IT87_REG_CHIPID) != 0x90) { - err = -ENODEV; - goto ERROR2; - } + || it87_read_value(data, IT87_REG_CHIPID) != 0x90) + return -ENODEV; platform_set_drvdata(pdev, data); @@ -1142,125 +2138,125 @@ 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 (has_12mv_adc(data)) { + 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 */ + if (sio_data->internal & (1 << 3)) + data->in_scaled |= (1 << 9); /* in9 is AVCC */ + } else if (sio_data->type == it8782 || sio_data->type == it8783) { + if (sio_data->internal & (1 << 0)) + data->in_scaled |= (1 << 3); /* in3 is VCC5V */ + if (sio_data->internal & (1 << 1)) + data->in_scaled |= (1 << 7); /* in7 is VCCH5V */ + } + + data->has_temp = 0x07; + if (sio_data->skip_temp & (1 << 2)) { + if (sio_data->type == it8782 + && !(it87_read_value(data, IT87_REG_TEMP_EXTRA) & 0x80)) + data->has_temp &= ~(1 << 2); + } + /* Initialize the IT87 chip */ it87_init_device(pdev); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&dev->kobj, &it87_group))) - goto ERROR2; + err = sysfs_create_group(&dev->kobj, &it87_group); + if (err) + return err; - /* Do not create fan files for disabled fans */ - if (has_16bit_fans(data)) { - /* 16-bit tachometers */ - if (data->has_fan & (1 << 0)) { - if ((err = device_create_file(dev, - &sensor_dev_attr_fan1_input16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan1_min16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan1_alarm.dev_attr))) - goto ERROR4; - } - if (data->has_fan & (1 << 1)) { - if ((err = device_create_file(dev, - &sensor_dev_attr_fan2_input16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan2_min16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan2_alarm.dev_attr))) - goto ERROR4; - } - if (data->has_fan & (1 << 2)) { - if ((err = device_create_file(dev, - &sensor_dev_attr_fan3_input16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan3_min16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan3_alarm.dev_attr))) - goto ERROR4; + for (i = 0; i < 10; i++) { + if (sio_data->skip_in & (1 << i)) + continue; + err = sysfs_create_group(&dev->kobj, &it87_group_in[i]); + if (err) + goto error; + if (sio_data->beep_pin && it87_attributes_in_beep[i]) { + err = sysfs_create_file(&dev->kobj, + it87_attributes_in_beep[i]); + if (err) + goto error; } - if (data->has_fan & (1 << 3)) { - if ((err = device_create_file(dev, - &sensor_dev_attr_fan4_input16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan4_min16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan4_alarm.dev_attr))) - goto ERROR4; + } + + for (i = 0; i < 3; i++) { + if (!(data->has_temp & (1 << i))) + continue; + err = sysfs_create_group(&dev->kobj, &it87_group_temp[i]); + if (err) + goto error; + if (has_temp_offset(data)) { + err = sysfs_create_file(&dev->kobj, + it87_attributes_temp_offset[i]); + if (err) + goto error; } - if (data->has_fan & (1 << 4)) { - if ((err = device_create_file(dev, - &sensor_dev_attr_fan5_input16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan5_min16.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan5_alarm.dev_attr))) - goto ERROR4; + if (sio_data->beep_pin) { + err = sysfs_create_file(&dev->kobj, + it87_attributes_temp_beep[i]); + if (err) + goto error; } - } else { - /* 8-bit tachometers with clock divider */ - if (data->has_fan & (1 << 0)) { - if ((err = device_create_file(dev, - &sensor_dev_attr_fan1_input.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan1_min.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan1_div.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan1_alarm.dev_attr))) - goto ERROR4; - } - if (data->has_fan & (1 << 1)) { - if ((err = device_create_file(dev, - &sensor_dev_attr_fan2_input.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan2_min.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan2_div.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan2_alarm.dev_attr))) - goto ERROR4; + } + + /* Do not create fan files for disabled fans */ + fan_beep_need_rw = 1; + for (i = 0; i < 5; i++) { + if (!(data->has_fan & (1 << i))) + continue; + err = sysfs_create_group(&dev->kobj, &it87_group_fan[i]); + if (err) + goto error; + + if (i < 3 && !has_16bit_fans(data)) { + err = sysfs_create_file(&dev->kobj, + it87_attributes_fan_div[i]); + if (err) + goto error; } - if (data->has_fan & (1 << 2)) { - if ((err = device_create_file(dev, - &sensor_dev_attr_fan3_input.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan3_min.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan3_div.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan3_alarm.dev_attr))) - goto ERROR4; + + if (sio_data->beep_pin) { + err = sysfs_create_file(&dev->kobj, + it87_attributes_fan_beep[i]); + if (err) + goto error; + if (!fan_beep_need_rw) + continue; + + /* + * As we have a single beep enable bit for all fans, + * only the first enabled fan has a writable attribute + * for it. + */ + if (sysfs_chmod_file(&dev->kobj, + it87_attributes_fan_beep[i], + S_IRUGO | S_IWUSR)) + dev_dbg(dev, "chmod +w fan%d_beep failed\n", + i + 1); + fan_beep_need_rw = 0; } } if (enable_pwm_interface) { - if (!(sio_data->skip_pwm & (1 << 0))) { - if ((err = device_create_file(dev, - &sensor_dev_attr_pwm1_enable.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_pwm1.dev_attr)) - || (err = device_create_file(dev, - &dev_attr_pwm1_freq))) - goto ERROR4; - } - if (!(sio_data->skip_pwm & (1 << 1))) { - if ((err = device_create_file(dev, - &sensor_dev_attr_pwm2_enable.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_pwm2.dev_attr)) - || (err = device_create_file(dev, - &dev_attr_pwm2_freq))) - goto ERROR4; - } - if (!(sio_data->skip_pwm & (1 << 2))) { - if ((err = device_create_file(dev, - &sensor_dev_attr_pwm3_enable.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_pwm3.dev_attr)) - || (err = device_create_file(dev, - &dev_attr_pwm3_freq))) - goto ERROR4; + for (i = 0; i < 3; i++) { + if (sio_data->skip_pwm & (1 << i)) + continue; + err = sysfs_create_group(&dev->kobj, + &it87_group_pwm[i]); + if (err) + goto error; + + if (!has_old_autopwm(data)) + continue; + err = sysfs_create_group(&dev->kobj, + &it87_group_autopwm[i]); + if (err) + goto error; } } @@ -1268,60 +2264,60 @@ static int __devinit it87_probe(struct platform_device *pdev) data->vrm = vid_which_vrm(); /* VID reading from Super-I/O config space if available */ data->vid = sio_data->vid_value; - if ((err = device_create_file(dev, - &dev_attr_vrm)) - || (err = device_create_file(dev, - &dev_attr_cpu0_vid))) - goto ERROR4; + err = sysfs_create_group(&dev->kobj, &it87_group_vid); + if (err) + goto error; + } + + /* Export labels for internal sensors */ + for (i = 0; i < 4; i++) { + if (!(sio_data->internal & (1 << i))) + continue; + err = sysfs_create_file(&dev->kobj, + it87_attributes_label[i]); + if (err) + goto error; } data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); - goto ERROR4; + goto error; } return 0; -ERROR4: - sysfs_remove_group(&dev->kobj, &it87_group); - sysfs_remove_group(&dev->kobj, &it87_group_opt); -ERROR2: - platform_set_drvdata(pdev, NULL); - kfree(data); -ERROR1: - release_region(res->start, IT87_EC_EXTENT); -ERROR0: +error: + it87_remove_files(dev); return err; } -static int __devexit it87_remove(struct platform_device *pdev) +static int it87_remove(struct platform_device *pdev) { struct it87_data *data = platform_get_drvdata(pdev); hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&pdev->dev.kobj, &it87_group); - sysfs_remove_group(&pdev->dev.kobj, &it87_group_opt); - - release_region(data->addr, IT87_EC_EXTENT); - platform_set_drvdata(pdev, NULL); - kfree(data); + it87_remove_files(&pdev->dev); return 0; } -/* Must be called with data->update_lock held, except during initialization. - We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, - would slow down the IT87 access and should not be necessary. */ +/* + * Must be called with data->update_lock held, except during initialization. + * We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, + * would slow down the IT87 access and should not be necessary. + */ static int it87_read_value(struct it87_data *data, u8 reg) { outb_p(reg, data->addr + IT87_ADDR_REG_OFFSET); return inb_p(data->addr + IT87_DATA_REG_OFFSET); } -/* Must be called with data->update_lock held, except during initialization. - We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, - would slow down the IT87 access and should not be necessary. */ +/* + * Must be called with data->update_lock held, except during initialization. + * We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, + * would slow down the IT87 access and should not be necessary. + */ static void it87_write_value(struct it87_data *data, u8 reg, u8 value) { outb_p(reg, data->addr + IT87_ADDR_REG_OFFSET); @@ -1329,18 +2325,22 @@ static void it87_write_value(struct it87_data *data, u8 reg, u8 value) } /* Return 1 if and only if the PWM interface is safe to use */ -static int __devinit it87_check_pwm(struct device *dev) +static int it87_check_pwm(struct device *dev) { struct it87_data *data = dev_get_drvdata(dev); - /* Some BIOSes fail to correctly configure the IT87 fans. All fans off + /* + * Some BIOSes fail to correctly configure the IT87 fans. All fans off * and polarity set to active low is sign that this is the case so we - * disable pwm control to protect the user. */ + * disable pwm control to protect the user. + */ int tmp = it87_read_value(data, IT87_REG_FAN_CTL); if ((tmp & 0x87) == 0) { if (fix_pwm_polarity) { - /* The user asks us to attempt a chip reconfiguration. + /* + * The user asks us to attempt a chip reconfiguration. * This means switching to active high polarity and - * inverting all fan speed values. */ + * inverting all fan speed values. + */ int i; u8 pwm[3]; @@ -1348,13 +2348,15 @@ static int __devinit it87_check_pwm(struct device *dev) pwm[i] = it87_read_value(data, IT87_REG_PWM(i)); - /* If any fan is in automatic pwm mode, the polarity + /* + * If any fan is in automatic pwm mode, the polarity * might be correct, as suspicious as it seems, so we * better don't change anything (but still disable the - * PWM interface). */ + * PWM interface). + */ if (!((pwm[0] | pwm[1] | pwm[2]) & 0x80)) { - dev_info(dev, "Reconfiguring PWM to " - "active high polarity\n"); + dev_info(dev, + "Reconfiguring PWM to active high polarity\n"); it87_write_value(data, IT87_REG_FAN_CTL, tmp | 0x87); for (i = 0; i < 3; i++) @@ -1364,45 +2366,55 @@ static int __devinit it87_check_pwm(struct device *dev) return 1; } - dev_info(dev, "PWM configuration is " - "too broken to be fixed\n"); + dev_info(dev, + "PWM configuration is too broken to be fixed\n"); } - dev_info(dev, "Detected broken BIOS " - "defaults, disabling PWM interface\n"); + dev_info(dev, + "Detected broken BIOS defaults, disabling PWM interface\n"); return 0; } else if (fix_pwm_polarity) { - dev_info(dev, "PWM configuration looks " - "sane, won't touch\n"); + dev_info(dev, + "PWM configuration looks sane, won't touch\n"); } return 1; } /* Called when we have found a new IT87. */ -static void __devinit it87_init_device(struct platform_device *pdev) +static void it87_init_device(struct platform_device *pdev) { - struct it87_sio_data *sio_data = pdev->dev.platform_data; + struct it87_sio_data *sio_data = dev_get_platdata(&pdev->dev); struct it87_data *data = platform_get_drvdata(pdev); int tmp, i; u8 mask; - /* initialize to sane defaults: - * - if the chip is in manual pwm mode, this will be overwritten with - * the actual settings on the chip (so in this case, initialization - * is not needed) - * - if in automatic or on/off mode, we could switch to manual mode, - * read the registers and set manual_pwm_ctl accordingly, but currently - * this is not implemented, so we initialize to something sane */ + /* + * For each PWM channel: + * - If it is in automatic mode, setting to manual mode should set + * the fan to full speed by default. + * - If it is in manual mode, we need a mapping to temperature + * channels to use when later setting to automatic mode later. + * Use a 1:1 mapping by default (we are clueless.) + * In both cases, the value can (and should) be changed by the user + * prior to switching to a different mode. + * Note that this is no longer needed for the IT8721F and later, as + * these have separate registers for the temperature mapping and the + * manual duty cycle. + */ for (i = 0; i < 3; i++) { - data->manual_pwm_ctl[i] = 0xff; + data->pwm_temp_map[i] = i; + data->pwm_duty[i] = 0x7f; /* Full speed */ + data->auto_pwm[i][3] = 0x7f; /* Full speed, hard-coded */ } - /* Some chips seem to have default value 0xff for all limit + /* + * Some chips seem to have default value 0xff for all limit * registers. For low voltage limits it makes no sense and triggers * alarms, so change to 0 instead. For high temperature limits, it * means -1 degree C, which surprisingly doesn't trigger an alarm, - * but is still confusing, so change to 127 degrees C. */ + * but is still confusing, so change to 127 degrees C. + */ for (i = 0; i < 8; i++) { tmp = it87_read_value(data, IT87_REG_VIN_MIN(i)); if (tmp == 0xff) @@ -1414,14 +2426,12 @@ static void __devinit it87_init_device(struct platform_device *pdev) it87_write_value(data, IT87_REG_TEMP_HIGH(i), 127); } - /* Check if temperature channels are reset manually or by some reason */ - tmp = it87_read_value(data, IT87_REG_TEMP_ENABLE); - if ((tmp & 0x3f) == 0) { - /* Temp1,Temp3=thermistor; Temp2=thermal diode */ - tmp = (tmp & 0xc0) | 0x2a; - it87_write_value(data, IT87_REG_TEMP_ENABLE, tmp); - } - data->sensor = tmp; + /* + * Temperature channels are not forcibly enabled, as they can be + * set to two different sensor types and we can't guess which one + * is correct for a given system. These channels can be enabled at + * run-time through the temp{1-3}_type sysfs accessors if needed. + */ /* Check if voltage monitors are reset manually or by some reason */ tmp = it87_read_value(data, IT87_REG_VIN_ENABLE); @@ -1436,12 +2446,14 @@ static void __devinit it87_init_device(struct platform_device *pdev) if ((data->fan_main_ctrl & mask) == 0) { /* Enable all fan tachometers */ data->fan_main_ctrl |= mask; - it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl); + it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, + data->fan_main_ctrl); } data->has_fan = (data->fan_main_ctrl >> 4) & 0x07; - /* Set tachometers to 16-bit mode if needed */ - if (has_16bit_fans(data)) { + /* Set tachometers to 16-bit mode if needed, IT8603E (and IT8728F?) + * has it by default */ + if (has_16bit_fans(data) && data->type != it8603) { tmp = it87_read_value(data, IT87_REG_FAN_16BIT); if (~tmp & 0x07 & data->has_fan) { dev_dbg(&pdev->dev, @@ -1449,8 +2461,9 @@ static void __devinit it87_init_device(struct platform_device *pdev) it87_write_value(data, IT87_REG_FAN_16BIT, tmp | 0x07); } - /* IT8705F only supports three fans. */ - if (data->type != it87) { + /* IT8705F, IT8782F, and IT8783E/F only support three fans. */ + if (data->type != it87 && data->type != it8782 && + data->type != it8783) { if (tmp & (1 << 4)) data->has_fan |= (1 << 3); /* fan4 enabled */ if (tmp & (1 << 5)) @@ -1461,30 +2474,38 @@ static void __devinit it87_init_device(struct platform_device *pdev) /* Fan input pins may be used for alternative functions */ data->has_fan &= ~sio_data->skip_fan; - /* Set current fan mode registers and the default settings for the - * other mode registers */ - for (i = 0; i < 3; i++) { - if (data->fan_main_ctrl & (1 << i)) { - /* pwm mode */ - tmp = it87_read_value(data, IT87_REG_PWM(i)); - if (tmp & 0x80) { - /* automatic pwm - not yet implemented, but - * leave the settings made by the BIOS alone - * until a change is requested via the sysfs - * interface */ - } else { - /* manual pwm */ - data->manual_pwm_ctl[i] = PWM_FROM_REG(tmp); - } - } - } - /* Start monitoring */ it87_write_value(data, IT87_REG_CONFIG, - (it87_read_value(data, IT87_REG_CONFIG) & 0x36) + (it87_read_value(data, IT87_REG_CONFIG) & 0x3e) | (update_vbat ? 0x41 : 0x01)); } +static void it87_update_pwm_ctrl(struct it87_data *data, int nr) +{ + data->pwm_ctrl[nr] = it87_read_value(data, IT87_REG_PWM(nr)); + if (has_newer_autopwm(data)) { + data->pwm_temp_map[nr] = data->pwm_ctrl[nr] & 0x03; + data->pwm_duty[nr] = it87_read_value(data, + IT87_REG_PWM_DUTY(nr)); + } else { + if (data->pwm_ctrl[nr] & 0x80) /* Automatic mode */ + data->pwm_temp_map[nr] = data->pwm_ctrl[nr] & 0x03; + else /* Manual mode */ + data->pwm_duty[nr] = data->pwm_ctrl[nr] & 0x7f; + } + + if (has_old_autopwm(data)) { + int i; + + for (i = 0; i < 5 ; i++) + data->auto_temp[nr][i] = it87_read_value(data, + IT87_REG_AUTO_TEMP(nr, i)); + for (i = 0; i < 3 ; i++) + data->auto_pwm[nr][i] = it87_read_value(data, + IT87_REG_AUTO_PWM(nr, i)); + } +} + static struct it87_data *it87_update_device(struct device *dev) { struct it87_data *data = dev_get_drvdata(dev); @@ -1494,49 +2515,57 @@ static struct it87_data *it87_update_device(struct device *dev) if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || !data->valid) { - if (update_vbat) { - /* Cleared after each update, so reenable. Value - returned by this read will be previous value */ + /* + * Cleared after each update, so reenable. Value + * returned by this read will be previous value + */ it87_write_value(data, IT87_REG_CONFIG, - it87_read_value(data, IT87_REG_CONFIG) | 0x40); + it87_read_value(data, IT87_REG_CONFIG) | 0x40); } for (i = 0; i <= 7; i++) { - data->in[i] = - it87_read_value(data, IT87_REG_VIN(i)); - data->in_min[i] = - it87_read_value(data, IT87_REG_VIN_MIN(i)); - data->in_max[i] = - it87_read_value(data, IT87_REG_VIN_MAX(i)); + data->in[i][0] = + it87_read_value(data, IT87_REG_VIN(i)); + data->in[i][1] = + it87_read_value(data, IT87_REG_VIN_MIN(i)); + data->in[i][2] = + it87_read_value(data, IT87_REG_VIN_MAX(i)); } /* in8 (battery) has no limit registers */ - data->in[8] = - it87_read_value(data, IT87_REG_VIN(8)); + data->in[8][0] = it87_read_value(data, IT87_REG_VIN(8)); + if (data->type == it8603) + data->in[9][0] = it87_read_value(data, 0x2f); for (i = 0; i < 5; i++) { /* Skip disabled fans */ if (!(data->has_fan & (1 << i))) continue; - data->fan_min[i] = - it87_read_value(data, IT87_REG_FAN_MIN[i]); - data->fan[i] = it87_read_value(data, + data->fan[i][1] = + it87_read_value(data, IT87_REG_FAN_MIN[i]); + data->fan[i][0] = it87_read_value(data, IT87_REG_FAN[i]); /* Add high byte if in 16-bit mode */ if (has_16bit_fans(data)) { - data->fan[i] |= it87_read_value(data, + data->fan[i][0] |= it87_read_value(data, IT87_REG_FANX[i]) << 8; - data->fan_min[i] |= it87_read_value(data, + data->fan[i][1] |= it87_read_value(data, IT87_REG_FANX_MIN[i]) << 8; } } for (i = 0; i < 3; i++) { - data->temp[i] = - it87_read_value(data, IT87_REG_TEMP(i)); - data->temp_high[i] = - it87_read_value(data, IT87_REG_TEMP_HIGH(i)); - data->temp_low[i] = - it87_read_value(data, IT87_REG_TEMP_LOW(i)); + if (!(data->has_temp & (1 << i))) + continue; + data->temp[i][0] = + it87_read_value(data, IT87_REG_TEMP(i)); + data->temp[i][1] = + it87_read_value(data, IT87_REG_TEMP_LOW(i)); + data->temp[i][2] = + it87_read_value(data, IT87_REG_TEMP_HIGH(i)); + if (has_temp_offset(data)) + data->temp[i][3] = + it87_read_value(data, + IT87_REG_TEMP_OFFSET[i]); } /* Newer chips don't have clock dividers */ @@ -1551,18 +2580,27 @@ static struct it87_data *it87_update_device(struct device *dev) it87_read_value(data, IT87_REG_ALARM1) | (it87_read_value(data, IT87_REG_ALARM2) << 8) | (it87_read_value(data, IT87_REG_ALARM3) << 16); + data->beeps = it87_read_value(data, IT87_REG_BEEP_ENABLE); + data->fan_main_ctrl = it87_read_value(data, IT87_REG_FAN_MAIN_CTRL); data->fan_ctl = it87_read_value(data, IT87_REG_FAN_CTL); + for (i = 0; i < 3; i++) + it87_update_pwm_ctrl(data, i); 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 - same purpose. */ + data->extra = it87_read_value(data, IT87_REG_TEMP_EXTRA); + /* + * The IT8705F does not have VID capability. + * The IT8718F 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); - /* The older IT8712F revisions had only 5 VID pins, - but we assume it is always safe to read 6 bits. */ + /* + * The older IT8712F revisions had only 5 VID pins, + * but we assume it is always safe to read 6 bits. + */ data->vid &= 0x3f; } data->last_updated = jiffies; @@ -1592,28 +2630,26 @@ static int __init it87_device_add(unsigned short address, pdev = platform_device_alloc(DRVNAME, address); if (!pdev) { err = -ENOMEM; - printk(KERN_ERR DRVNAME ": Device allocation failed\n"); + pr_err("Device allocation failed\n"); goto exit; } err = platform_device_add_resources(pdev, &res, 1); if (err) { - printk(KERN_ERR DRVNAME ": Device resource addition failed " - "(%d)\n", err); + pr_err("Device resource addition failed (%d)\n", err); goto exit_device_put; } err = platform_device_add_data(pdev, sio_data, sizeof(struct it87_sio_data)); if (err) { - printk(KERN_ERR DRVNAME ": Platform data allocation failed\n"); + pr_err("Platform data allocation failed\n"); goto exit_device_put; } err = platform_device_add(pdev); if (err) { - printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", - err); + pr_err("Device addition failed (%d)\n", err); goto exit_device_put; } @@ -1628,7 +2664,7 @@ exit: static int __init sm_it87_init(void) { int err; - unsigned short isa_address=0; + unsigned short isa_address = 0; struct it87_sio_data sio_data; memset(&sio_data, 0, sizeof(struct it87_sio_data)); @@ -1640,7 +2676,7 @@ static int __init sm_it87_init(void) return err; err = it87_device_add(isa_address, &sio_data); - if (err){ + if (err) { platform_driver_unregister(&it87_driver); return err; } @@ -1655,13 +2691,13 @@ 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_AUTHOR("Chris Gauthron, Jean Delvare <jdelvare@suse.de>"); +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); -MODULE_PARM_DESC(fix_pwm_polarity, "Force PWM polarity to active high (DANGEROUS)"); +MODULE_PARM_DESC(fix_pwm_polarity, + "Force PWM polarity to active high (DANGEROUS)"); MODULE_LICENSE("GPL"); module_init(sm_it87_init); diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c new file mode 100644 index 00000000000..388f8bcd898 --- /dev/null +++ b/drivers/hwmon/jc42.c @@ -0,0 +1,554 @@ +/* + * jc42.c - driver for Jedec JC42.4 compliant temperature sensors + * + * Copyright (c) 2010 Ericsson AB. + * + * Derived from lm77.c by Andras BALI <drewie@freemail.hu>. + * + * JC42.4 compliant temperature sensors are typically used on memory modules. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, I2C_CLIENT_END }; + +/* JC42 registers. All registers are 16 bit. */ +#define JC42_REG_CAP 0x00 +#define JC42_REG_CONFIG 0x01 +#define JC42_REG_TEMP_UPPER 0x02 +#define JC42_REG_TEMP_LOWER 0x03 +#define JC42_REG_TEMP_CRITICAL 0x04 +#define JC42_REG_TEMP 0x05 +#define JC42_REG_MANID 0x06 +#define JC42_REG_DEVICEID 0x07 + +/* Status bits in temperature register */ +#define JC42_ALARM_CRIT_BIT 15 +#define JC42_ALARM_MAX_BIT 14 +#define JC42_ALARM_MIN_BIT 13 + +/* Configuration register defines */ +#define JC42_CFG_CRIT_ONLY (1 << 2) +#define JC42_CFG_TCRIT_LOCK (1 << 6) +#define JC42_CFG_EVENT_LOCK (1 << 7) +#define JC42_CFG_SHUTDOWN (1 << 8) +#define JC42_CFG_HYST_SHIFT 9 +#define JC42_CFG_HYST_MASK (0x03 << 9) + +/* Capabilities */ +#define JC42_CAP_RANGE (1 << 2) + +/* Manufacturer IDs */ +#define ADT_MANID 0x11d4 /* Analog Devices */ +#define ATMEL_MANID 0x001f /* Atmel */ +#define ATMEL_MANID2 0x1114 /* Atmel */ +#define MAX_MANID 0x004d /* Maxim */ +#define IDT_MANID 0x00b3 /* IDT */ +#define MCP_MANID 0x0054 /* Microchip */ +#define NXP_MANID 0x1131 /* NXP Semiconductors */ +#define ONS_MANID 0x1b09 /* ON Semiconductor */ +#define STM_MANID 0x104a /* ST Microelectronics */ + +/* Supported chips */ + +/* Analog Devices */ +#define ADT7408_DEVID 0x0801 +#define ADT7408_DEVID_MASK 0xffff + +/* Atmel */ +#define AT30TS00_DEVID 0x8201 +#define AT30TS00_DEVID_MASK 0xffff + +#define AT30TSE004_DEVID 0x2200 +#define AT30TSE004_DEVID_MASK 0xffff + +/* IDT */ +#define TS3000B3_DEVID 0x2903 /* Also matches TSE2002B3 */ +#define TS3000B3_DEVID_MASK 0xffff + +#define TS3000GB2_DEVID 0x2912 /* Also matches TSE2002GB2 */ +#define TS3000GB2_DEVID_MASK 0xffff + +/* Maxim */ +#define MAX6604_DEVID 0x3e00 +#define MAX6604_DEVID_MASK 0xffff + +/* Microchip */ +#define MCP9804_DEVID 0x0200 +#define MCP9804_DEVID_MASK 0xfffc + +#define MCP98242_DEVID 0x2000 +#define MCP98242_DEVID_MASK 0xfffc + +#define MCP98243_DEVID 0x2100 +#define MCP98243_DEVID_MASK 0xfffc + +#define MCP98244_DEVID 0x2200 +#define MCP98244_DEVID_MASK 0xfffc + +#define MCP9843_DEVID 0x0000 /* Also matches mcp9805 */ +#define MCP9843_DEVID_MASK 0xfffe + +/* NXP */ +#define SE97_DEVID 0xa200 +#define SE97_DEVID_MASK 0xfffc + +#define SE98_DEVID 0xa100 +#define SE98_DEVID_MASK 0xfffc + +/* ON Semiconductor */ +#define CAT6095_DEVID 0x0800 /* Also matches CAT34TS02 */ +#define CAT6095_DEVID_MASK 0xffe0 + +/* ST Microelectronics */ +#define STTS424_DEVID 0x0101 +#define STTS424_DEVID_MASK 0xffff + +#define STTS424E_DEVID 0x0000 +#define STTS424E_DEVID_MASK 0xfffe + +#define STTS2002_DEVID 0x0300 +#define STTS2002_DEVID_MASK 0xffff + +#define STTS2004_DEVID 0x2201 +#define STTS2004_DEVID_MASK 0xffff + +#define STTS3000_DEVID 0x0200 +#define STTS3000_DEVID_MASK 0xffff + +static u16 jc42_hysteresis[] = { 0, 1500, 3000, 6000 }; + +struct jc42_chips { + u16 manid; + u16 devid; + u16 devid_mask; +}; + +static struct jc42_chips jc42_chips[] = { + { ADT_MANID, ADT7408_DEVID, ADT7408_DEVID_MASK }, + { ATMEL_MANID, AT30TS00_DEVID, AT30TS00_DEVID_MASK }, + { ATMEL_MANID2, AT30TSE004_DEVID, AT30TSE004_DEVID_MASK }, + { IDT_MANID, TS3000B3_DEVID, TS3000B3_DEVID_MASK }, + { IDT_MANID, TS3000GB2_DEVID, TS3000GB2_DEVID_MASK }, + { MAX_MANID, MAX6604_DEVID, MAX6604_DEVID_MASK }, + { MCP_MANID, MCP9804_DEVID, MCP9804_DEVID_MASK }, + { MCP_MANID, MCP98242_DEVID, MCP98242_DEVID_MASK }, + { MCP_MANID, MCP98243_DEVID, MCP98243_DEVID_MASK }, + { MCP_MANID, MCP98244_DEVID, MCP98244_DEVID_MASK }, + { MCP_MANID, MCP9843_DEVID, MCP9843_DEVID_MASK }, + { NXP_MANID, SE97_DEVID, SE97_DEVID_MASK }, + { ONS_MANID, CAT6095_DEVID, CAT6095_DEVID_MASK }, + { NXP_MANID, SE98_DEVID, SE98_DEVID_MASK }, + { STM_MANID, STTS424_DEVID, STTS424_DEVID_MASK }, + { STM_MANID, STTS424E_DEVID, STTS424E_DEVID_MASK }, + { STM_MANID, STTS2002_DEVID, STTS2002_DEVID_MASK }, + { STM_MANID, STTS2004_DEVID, STTS2004_DEVID_MASK }, + { STM_MANID, STTS3000_DEVID, STTS3000_DEVID_MASK }, +}; + +enum temp_index { + t_input = 0, + t_crit, + t_min, + t_max, + t_num_temp +}; + +static const u8 temp_regs[t_num_temp] = { + [t_input] = JC42_REG_TEMP, + [t_crit] = JC42_REG_TEMP_CRITICAL, + [t_min] = JC42_REG_TEMP_LOWER, + [t_max] = JC42_REG_TEMP_UPPER, +}; + +/* Each client has this additional data */ +struct jc42_data { + struct i2c_client *client; + struct mutex update_lock; /* protect register access */ + bool extended; /* true if extended range supported */ + bool valid; + unsigned long last_updated; /* In jiffies */ + u16 orig_config; /* original configuration */ + u16 config; /* current configuration */ + u16 temp[t_num_temp];/* Temperatures */ +}; + +#define JC42_TEMP_MIN_EXTENDED (-40000) +#define JC42_TEMP_MIN 0 +#define JC42_TEMP_MAX 125000 + +static u16 jc42_temp_to_reg(int temp, bool extended) +{ + int ntemp = clamp_val(temp, + extended ? JC42_TEMP_MIN_EXTENDED : + JC42_TEMP_MIN, JC42_TEMP_MAX); + + /* convert from 0.001 to 0.0625 resolution */ + return (ntemp * 2 / 125) & 0x1fff; +} + +static int jc42_temp_from_reg(s16 reg) +{ + reg &= 0x1fff; + + /* sign extend register */ + if (reg & 0x1000) + reg |= 0xf000; + + /* convert from 0.0625 to 0.001 resolution */ + return reg * 125 / 2; +} + +static struct jc42_data *jc42_update_device(struct device *dev) +{ + struct jc42_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + struct jc42_data *ret = data; + int i, val; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + for (i = 0; i < t_num_temp; i++) { + val = i2c_smbus_read_word_swapped(client, temp_regs[i]); + if (val < 0) { + ret = ERR_PTR(val); + goto abort; + } + data->temp[i] = val; + } + data->last_updated = jiffies; + data->valid = true; + } +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +/* sysfs functions */ + +static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct jc42_data *data = jc42_update_device(dev); + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", + jc42_temp_from_reg(data->temp[attr->index])); +} + +static ssize_t show_temp_hyst(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct jc42_data *data = jc42_update_device(dev); + int temp, hyst; + + if (IS_ERR(data)) + return PTR_ERR(data); + + temp = jc42_temp_from_reg(data->temp[attr->index]); + hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK) + >> JC42_CFG_HYST_SHIFT]; + return sprintf(buf, "%d\n", temp - hyst); +} + +static ssize_t set_temp(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct jc42_data *data = dev_get_drvdata(dev); + int err, ret = count; + int nr = attr->index; + long val; + + if (kstrtol(buf, 10, &val) < 0) + return -EINVAL; + mutex_lock(&data->update_lock); + data->temp[nr] = jc42_temp_to_reg(val, data->extended); + err = i2c_smbus_write_word_swapped(data->client, temp_regs[nr], + data->temp[nr]); + if (err < 0) + ret = err; + mutex_unlock(&data->update_lock); + return ret; +} + +/* + * JC42.4 compliant chips only support four hysteresis values. + * Pick best choice and go from there. + */ +static ssize_t set_temp_crit_hyst(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct jc42_data *data = dev_get_drvdata(dev); + unsigned long val; + int diff, hyst; + int err; + int ret = count; + + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + + diff = jc42_temp_from_reg(data->temp[t_crit]) - val; + hyst = 0; + if (diff > 0) { + if (diff < 2250) + hyst = 1; /* 1.5 degrees C */ + else if (diff < 4500) + hyst = 2; /* 3.0 degrees C */ + else + hyst = 3; /* 6.0 degrees C */ + } + + mutex_lock(&data->update_lock); + data->config = (data->config & ~JC42_CFG_HYST_MASK) + | (hyst << JC42_CFG_HYST_SHIFT); + err = i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG, + data->config); + if (err < 0) + ret = err; + mutex_unlock(&data->update_lock); + return ret; +} + +static ssize_t show_alarm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u16 bit = to_sensor_dev_attr(attr)->index; + struct jc42_data *data = jc42_update_device(dev); + u16 val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = data->temp[t_input]; + if (bit != JC42_ALARM_CRIT_BIT && (data->config & JC42_CFG_CRIT_ONLY)) + val = 0; + return sprintf(buf, "%u\n", (val >> bit) & 1); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, t_input); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, set_temp, t_crit); +static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp, set_temp, t_min); +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, set_temp, t_max); + +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_hyst, + set_temp_crit_hyst, t_crit); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp_hyst, NULL, t_max); + +static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, + JC42_ALARM_CRIT_BIT); +static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, + JC42_ALARM_MIN_BIT); +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, + JC42_ALARM_MAX_BIT); + +static struct attribute *jc42_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + NULL +}; + +static umode_t jc42_attribute_mode(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct jc42_data *data = dev_get_drvdata(dev); + unsigned int config = data->config; + bool readonly; + + if (attr == &sensor_dev_attr_temp1_crit.dev_attr.attr) + readonly = config & JC42_CFG_TCRIT_LOCK; + else if (attr == &sensor_dev_attr_temp1_min.dev_attr.attr || + attr == &sensor_dev_attr_temp1_max.dev_attr.attr) + readonly = config & JC42_CFG_EVENT_LOCK; + else if (attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr) + readonly = config & (JC42_CFG_EVENT_LOCK | JC42_CFG_TCRIT_LOCK); + else + readonly = true; + + return S_IRUGO | (readonly ? 0 : S_IWUSR); +} + +static const struct attribute_group jc42_group = { + .attrs = jc42_attributes, + .is_visible = jc42_attribute_mode, +}; +__ATTRIBUTE_GROUPS(jc42); + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + int i, config, cap, manid, devid; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + cap = i2c_smbus_read_word_swapped(client, JC42_REG_CAP); + config = i2c_smbus_read_word_swapped(client, JC42_REG_CONFIG); + manid = i2c_smbus_read_word_swapped(client, JC42_REG_MANID); + devid = i2c_smbus_read_word_swapped(client, JC42_REG_DEVICEID); + + if (cap < 0 || config < 0 || manid < 0 || devid < 0) + return -ENODEV; + + if ((cap & 0xff00) || (config & 0xf800)) + return -ENODEV; + + for (i = 0; i < ARRAY_SIZE(jc42_chips); i++) { + struct jc42_chips *chip = &jc42_chips[i]; + if (manid == chip->manid && + (devid & chip->devid_mask) == chip->devid) { + strlcpy(info->type, "jc42", I2C_NAME_SIZE); + return 0; + } + } + return -ENODEV; +} + +static int jc42_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct jc42_data *data; + int config, cap; + + data = devm_kzalloc(dev, sizeof(struct jc42_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + cap = i2c_smbus_read_word_swapped(client, JC42_REG_CAP); + if (cap < 0) + return cap; + + data->extended = !!(cap & JC42_CAP_RANGE); + + config = i2c_smbus_read_word_swapped(client, JC42_REG_CONFIG); + if (config < 0) + return config; + + data->orig_config = config; + if (config & JC42_CFG_SHUTDOWN) { + config &= ~JC42_CFG_SHUTDOWN; + i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, config); + } + data->config = config; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + jc42_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static int jc42_remove(struct i2c_client *client) +{ + struct jc42_data *data = i2c_get_clientdata(client); + + /* Restore original configuration except hysteresis */ + if ((data->config & ~JC42_CFG_HYST_MASK) != + (data->orig_config & ~JC42_CFG_HYST_MASK)) { + int config; + + config = (data->orig_config & ~JC42_CFG_HYST_MASK) + | (data->config & JC42_CFG_HYST_MASK); + i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, config); + } + return 0; +} + +#ifdef CONFIG_PM + +static int jc42_suspend(struct device *dev) +{ + struct jc42_data *data = dev_get_drvdata(dev); + + data->config |= JC42_CFG_SHUTDOWN; + i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG, + data->config); + return 0; +} + +static int jc42_resume(struct device *dev) +{ + struct jc42_data *data = dev_get_drvdata(dev); + + data->config &= ~JC42_CFG_SHUTDOWN; + i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG, + data->config); + return 0; +} + +static const struct dev_pm_ops jc42_dev_pm_ops = { + .suspend = jc42_suspend, + .resume = jc42_resume, +}; + +#define JC42_DEV_PM_OPS (&jc42_dev_pm_ops) +#else +#define JC42_DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + +static const struct i2c_device_id jc42_id[] = { + { "jc42", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, jc42_id); + +static struct i2c_driver jc42_driver = { + .class = I2C_CLASS_SPD, + .driver = { + .name = "jc42", + .pm = JC42_DEV_PM_OPS, + }, + .probe = jc42_probe, + .remove = jc42_remove, + .id_table = jc42_id, + .detect = jc42_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(jc42_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("JC42 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/jz4740-hwmon.c b/drivers/hwmon/jz4740-hwmon.c new file mode 100644 index 00000000000..7488e36809c --- /dev/null +++ b/drivers/hwmon/jz4740-hwmon.c @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> + * JZ4740 SoC HWMON driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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/err.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/io.h> + +#include <linux/completion.h> +#include <linux/mfd/core.h> + +#include <linux/hwmon.h> + +struct jz4740_hwmon { + void __iomem *base; + + int irq; + + const struct mfd_cell *cell; + struct device *hwmon; + + struct completion read_completion; + + struct mutex lock; +}; + +static ssize_t jz4740_hwmon_show_name(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + return sprintf(buf, "jz4740\n"); +} + +static irqreturn_t jz4740_hwmon_irq(int irq, void *data) +{ + struct jz4740_hwmon *hwmon = data; + + complete(&hwmon->read_completion); + return IRQ_HANDLED; +} + +static ssize_t jz4740_hwmon_read_adcin(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + struct jz4740_hwmon *hwmon = dev_get_drvdata(dev); + struct completion *completion = &hwmon->read_completion; + long t; + unsigned long val; + int ret; + + mutex_lock(&hwmon->lock); + + reinit_completion(completion); + + enable_irq(hwmon->irq); + hwmon->cell->enable(to_platform_device(dev)); + + t = wait_for_completion_interruptible_timeout(completion, HZ); + + if (t > 0) { + val = readw(hwmon->base) & 0xfff; + val = (val * 3300) >> 12; + ret = sprintf(buf, "%lu\n", val); + } else { + ret = t ? t : -ETIMEDOUT; + } + + hwmon->cell->disable(to_platform_device(dev)); + disable_irq(hwmon->irq); + + mutex_unlock(&hwmon->lock); + + return ret; +} + +static DEVICE_ATTR(name, S_IRUGO, jz4740_hwmon_show_name, NULL); +static DEVICE_ATTR(in0_input, S_IRUGO, jz4740_hwmon_read_adcin, NULL); + +static struct attribute *jz4740_hwmon_attributes[] = { + &dev_attr_name.attr, + &dev_attr_in0_input.attr, + NULL +}; + +static const struct attribute_group jz4740_hwmon_attr_group = { + .attrs = jz4740_hwmon_attributes, +}; + +static int jz4740_hwmon_probe(struct platform_device *pdev) +{ + int ret; + struct jz4740_hwmon *hwmon; + struct resource *mem; + + hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL); + if (!hwmon) + return -ENOMEM; + + hwmon->cell = mfd_get_cell(pdev); + + hwmon->irq = platform_get_irq(pdev, 0); + if (hwmon->irq < 0) { + dev_err(&pdev->dev, "Failed to get platform irq: %d\n", + hwmon->irq); + return hwmon->irq; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hwmon->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(hwmon->base)) + return PTR_ERR(hwmon->base); + + init_completion(&hwmon->read_completion); + mutex_init(&hwmon->lock); + + platform_set_drvdata(pdev, hwmon); + + ret = devm_request_irq(&pdev->dev, hwmon->irq, jz4740_hwmon_irq, 0, + pdev->name, hwmon); + if (ret) { + dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); + return ret; + } + disable_irq(hwmon->irq); + + ret = sysfs_create_group(&pdev->dev.kobj, &jz4740_hwmon_attr_group); + if (ret) { + dev_err(&pdev->dev, "Failed to create sysfs group: %d\n", ret); + return ret; + } + + hwmon->hwmon = hwmon_device_register(&pdev->dev); + if (IS_ERR(hwmon->hwmon)) { + ret = PTR_ERR(hwmon->hwmon); + goto err_remove_file; + } + + return 0; + +err_remove_file: + sysfs_remove_group(&pdev->dev.kobj, &jz4740_hwmon_attr_group); + return ret; +} + +static int jz4740_hwmon_remove(struct platform_device *pdev) +{ + struct jz4740_hwmon *hwmon = platform_get_drvdata(pdev); + + hwmon_device_unregister(hwmon->hwmon); + sysfs_remove_group(&pdev->dev.kobj, &jz4740_hwmon_attr_group); + + return 0; +} + +static struct platform_driver jz4740_hwmon_driver = { + .probe = jz4740_hwmon_probe, + .remove = jz4740_hwmon_remove, + .driver = { + .name = "jz4740-hwmon", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(jz4740_hwmon_driver); + +MODULE_DESCRIPTION("JZ4740 SoC HWMON driver"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:jz4740-hwmon"); diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index 099a2138cdf..f7b46f68ef4 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -1,5 +1,5 @@ /* - * k10temp.c - AMD Family 10h/11h processor hardware monitoring + * k10temp.c - AMD Family 10h/11h/12h/14h/15h/16h processor hardware monitoring * * Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de> * @@ -25,7 +25,7 @@ #include <linux/pci.h> #include <asm/processor.h> -MODULE_DESCRIPTION("AMD Family 10h/11h CPU core temperature monitor"); +MODULE_DESCRIPTION("AMD Family 10h+ CPU core temperature monitor"); MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); MODULE_LICENSE("GPL"); @@ -95,7 +95,7 @@ static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_crit, NULL, 1); static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); -static bool __devinit has_erratum_319(struct pci_dev *pdev) +static bool has_erratum_319(struct pci_dev *pdev) { u32 pkg_type, reg_dram_cfg; @@ -112,14 +112,24 @@ static bool __devinit has_erratum_319(struct pci_dev *pdev) if (pkg_type != CPUID_PKGTYPE_AM2R2_AM3) return false; - /* Differentiate between AM2+ (bad) and AM3 (good) */ + /* DDR3 memory implies socket AM3, which is good */ pci_bus_read_config_dword(pdev->bus, PCI_DEVFN(PCI_SLOT(pdev->devfn), 2), REG_DCT0_CONFIG_HIGH, ®_dram_cfg); - return !(reg_dram_cfg & DDR3_MODE); + if (reg_dram_cfg & DDR3_MODE) + return false; + + /* + * Unfortunately it is possible to run a socket AM3 CPU with DDR2 + * memory. We blacklist all the cores which do exist in socket AM2+ + * format. It still isn't perfect, as RB-C2 cores exist in both AM2+ + * and AM3 formats, but that's the best we can do. + */ + return boot_cpu_data.x86_model < 4 || + (boot_cpu_data.x86_model == 4 && boot_cpu_data.x86_mask <= 2); } -static int __devinit k10temp_probe(struct pci_dev *pdev, +static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct device *hwmon_dev; @@ -163,7 +173,7 @@ static int __devinit k10temp_probe(struct pci_dev *pdev, err = PTR_ERR(hwmon_dev); goto exit_remove; } - dev_set_drvdata(&pdev->dev, hwmon_dev); + pci_set_drvdata(pdev, hwmon_dev); if (unreliable && force) dev_warn(&pdev->dev, @@ -182,9 +192,9 @@ exit: return err; } -static void __devexit k10temp_remove(struct pci_dev *pdev) +static void k10temp_remove(struct pci_dev *pdev) { - hwmon_device_unregister(dev_get_drvdata(&pdev->dev)); + hwmon_device_unregister(pci_get_drvdata(pdev)); device_remove_file(&pdev->dev, &dev_attr_name); device_remove_file(&pdev->dev, &dev_attr_temp1_input); device_remove_file(&pdev->dev, &dev_attr_temp1_max); @@ -192,12 +202,17 @@ static void __devexit k10temp_remove(struct pci_dev *pdev) &sensor_dev_attr_temp1_crit.dev_attr); device_remove_file(&pdev->dev, &sensor_dev_attr_temp1_crit_hyst.dev_attr); - dev_set_drvdata(&pdev->dev, NULL); } static const struct pci_device_id k10temp_id_table[] = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) }, {} }; MODULE_DEVICE_TABLE(pci, k10temp_id_table); @@ -206,18 +221,7 @@ static struct pci_driver k10temp_driver = { .name = "k10temp", .id_table = k10temp_id_table, .probe = k10temp_probe, - .remove = __devexit_p(k10temp_remove), + .remove = k10temp_remove, }; -static int __init k10temp_init(void) -{ - return pci_register_driver(&k10temp_driver); -} - -static void __exit k10temp_exit(void) -{ - pci_unregister_driver(&k10temp_driver); -} - -module_init(k10temp_init) -module_exit(k10temp_exit) +module_pci_driver(k10temp_driver); diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c index 0ceb6d6200a..734d55d48cc 100644 --- a/drivers/hwmon/k8temp.c +++ b/drivers/hwmon/k8temp.c @@ -22,7 +22,6 @@ */ #include <linux/module.h> -#include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/jiffies.h> @@ -46,7 +45,7 @@ struct k8temp_data { unsigned long last_updated; /* in jiffies */ /* registers values */ - u8 sensorsp; /* sensor presence bits - SEL_CORE & SEL_PLACE */ + u8 sensorsp; /* sensor presence bits - SEL_CORE, SEL_PLACE */ u32 temp[2][2]; /* core, place */ u8 swap_core_select; /* meaning of SEL_CORE is inverted */ u32 temp_offset; @@ -63,7 +62,7 @@ static struct k8temp_data *k8temp_update_device(struct device *dev) if (!data->valid || time_after(jiffies, data->last_updated + HZ)) { pci_read_config_byte(pdev, REG_TEMP, &tmp); - tmp &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */ + tmp &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */ pci_write_config_byte(pdev, REG_TEMP, tmp); pci_read_config_dword(pdev, REG_TEMP, &data->temp[0][0]); @@ -82,7 +81,7 @@ static struct k8temp_data *k8temp_update_device(struct device *dev) &data->temp[1][0]); if (data->sensorsp & SEL_PLACE) { - tmp |= SEL_PLACE; /* Select sensor 1, core1 */ + tmp |= SEL_PLACE; /* Select sensor 1, core1 */ pci_write_config_byte(pdev, REG_TEMP, tmp); pci_read_config_dword(pdev, REG_TEMP, &data->temp[1][1]); @@ -120,7 +119,7 @@ static ssize_t show_temp(struct device *dev, int temp; struct k8temp_data *data = k8temp_update_device(dev); - if (data->swap_core_select) + if (data->swap_core_select && (data->sensorsp & SEL_CORE)) core = core ? 0 : 1; temp = TEMP_FROM_REG(data->temp[core][place]) + data->temp_offset; @@ -143,7 +142,38 @@ static const struct pci_device_id k8temp_ids[] = { MODULE_DEVICE_TABLE(pci, k8temp_ids); -static int __devinit k8temp_probe(struct pci_dev *pdev, +static int is_rev_g_desktop(u8 model) +{ + u32 brandidx; + + if (model < 0x69) + return 0; + + if (model == 0xc1 || model == 0x6c || model == 0x7c) + return 0; + + /* + * Differentiate between AM2 and ASB1. + * See "Constructing the processor Name String" in "Revision + * Guide for AMD NPT Family 0Fh Processors" (33610). + */ + brandidx = cpuid_ebx(0x80000001); + brandidx = (brandidx >> 9) & 0x1f; + + /* Single core */ + if ((model == 0x6f || model == 0x7f) && + (brandidx == 0x7 || brandidx == 0x9 || brandidx == 0xc)) + return 0; + + /* Dual core */ + if (model == 0x6b && + (brandidx == 0xb || brandidx == 0xc)) + return 0; + + return 1; +} + +static int k8temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int err; @@ -152,55 +182,44 @@ static int __devinit k8temp_probe(struct pci_dev *pdev, u8 model, stepping; struct k8temp_data *data; - if (!(data = kzalloc(sizeof(struct k8temp_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&pdev->dev, sizeof(struct k8temp_data), GFP_KERNEL); + if (!data) + return -ENOMEM; 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 ((model >= 0x69) && - !(model == 0xc1 || model == 0x6c || model == 0x7c)) { - /* - * RevG desktop CPUs (i.e. no socket S1G1 parts) - * need additional offset, otherwise reported - * temperature is below ambient temperature - */ - data->temp_offset = 21000; - } - - break; + /* feature available since SH-C0, exclude older revisions */ + if ((model == 4 && stepping == 0) || + (model == 5 && stepping <= 1)) + return -ENODEV; + + /* + * 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 */ + scfg &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */ pci_write_config_byte(pdev, REG_TEMP, scfg); pci_read_config_byte(pdev, REG_TEMP, &scfg); if (scfg & (SEL_PLACE | SEL_CORE)) { dev_err(&pdev->dev, "Configuration bit(s) stuck at 1!\n"); - err = -ENODEV; - goto exit_free; + return -ENODEV; } scfg |= (SEL_PLACE | SEL_CORE); @@ -214,7 +233,7 @@ static int __devinit k8temp_probe(struct pci_dev *pdev, pci_write_config_byte(pdev, REG_TEMP, scfg); pci_read_config_dword(pdev, REG_TEMP, &temp); scfg |= SEL_CORE; /* prepare for next selection */ - if (!((temp >> 16) & 0xff)) /* if temp is 0 -49C is not likely */ + if (!((temp >> 16) & 0xff)) /* if temp is 0 -49C is unlikely */ data->sensorsp &= ~SEL_PLACE; } @@ -222,13 +241,13 @@ static int __devinit k8temp_probe(struct pci_dev *pdev, scfg &= ~SEL_PLACE; /* Select sensor 0, core1 */ pci_write_config_byte(pdev, REG_TEMP, scfg); pci_read_config_dword(pdev, REG_TEMP, &temp); - if (!((temp >> 16) & 0xff)) /* if temp is 0 -49C is not likely */ + if (!((temp >> 16) & 0xff)) /* if temp is 0 -49C is unlikely */ data->sensorsp &= ~SEL_CORE; } data->name = "k8temp"; mutex_init(&data->update_lock); - dev_set_drvdata(&pdev->dev, data); + pci_set_drvdata(pdev, data); /* Register sysfs hooks */ err = device_create_file(&pdev->dev, @@ -250,12 +269,13 @@ static int __devinit k8temp_probe(struct pci_dev *pdev, &sensor_dev_attr_temp3_input.dev_attr); if (err) goto exit_remove; - if (data->sensorsp & SEL_PLACE) + if (data->sensorsp & SEL_PLACE) { err = device_create_file(&pdev->dev, &sensor_dev_attr_temp4_input. dev_attr); if (err) goto exit_remove; + } } err = device_create_file(&pdev->dev, &dev_attr_name); @@ -281,16 +301,12 @@ exit_remove: device_remove_file(&pdev->dev, &sensor_dev_attr_temp4_input.dev_attr); device_remove_file(&pdev->dev, &dev_attr_name); -exit_free: - dev_set_drvdata(&pdev->dev, NULL); - kfree(data); -exit: return err; } -static void __devexit k8temp_remove(struct pci_dev *pdev) +static void k8temp_remove(struct pci_dev *pdev) { - struct k8temp_data *data = dev_get_drvdata(&pdev->dev); + struct k8temp_data *data = pci_get_drvdata(pdev); hwmon_device_unregister(data->hwmon_dev); device_remove_file(&pdev->dev, @@ -302,30 +318,17 @@ static void __devexit k8temp_remove(struct pci_dev *pdev) device_remove_file(&pdev->dev, &sensor_dev_attr_temp4_input.dev_attr); device_remove_file(&pdev->dev, &dev_attr_name); - dev_set_drvdata(&pdev->dev, NULL); - kfree(data); } static struct pci_driver k8temp_driver = { .name = "k8temp", .id_table = k8temp_ids, .probe = k8temp_probe, - .remove = __devexit_p(k8temp_remove), + .remove = k8temp_remove, }; -static int __init k8temp_init(void) -{ - return pci_register_driver(&k8temp_driver); -} - -static void __exit k8temp_exit(void) -{ - pci_unregister_driver(&k8temp_driver); -} +module_pci_driver(k8temp_driver); MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>"); MODULE_DESCRIPTION("AMD K8 core temperature monitor"); MODULE_LICENSE("GPL"); - -module_init(k8temp_init) -module_exit(k8temp_exit) diff --git a/drivers/hwmon/lineage-pem.c b/drivers/hwmon/lineage-pem.c new file mode 100644 index 00000000000..ebbb9f4f27a --- /dev/null +++ b/drivers/hwmon/lineage-pem.c @@ -0,0 +1,575 @@ +/* + * Driver for Lineage Compact Power Line series of power entry modules. + * + * Copyright (C) 2010, 2011 Ericsson AB. + * + * Documentation: + * http://www.lineagepower.com/oem/pdf/CPLI2C.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> +#include <linux/jiffies.h> + +/* + * This driver supports various Lineage Compact Power Line DC/DC and AC/DC + * converters such as CP1800, CP2000AC, CP2000DC, CP2100DC, and others. + * + * The devices are nominally PMBus compliant. However, most standard PMBus + * commands are not supported. Specifically, all hardware monitoring and + * status reporting commands are non-standard. For this reason, a standard + * PMBus driver can not be used. + * + * All Lineage CPL devices have a built-in I2C bus master selector (PCA9541). + * To ensure device access, this driver should only be used as client driver + * to the pca9541 I2C master selector driver. + */ + +/* Command codes */ +#define PEM_OPERATION 0x01 +#define PEM_CLEAR_INFO_FLAGS 0x03 +#define PEM_VOUT_COMMAND 0x21 +#define PEM_VOUT_OV_FAULT_LIMIT 0x40 +#define PEM_READ_DATA_STRING 0xd0 +#define PEM_READ_INPUT_STRING 0xdc +#define PEM_READ_FIRMWARE_REV 0xdd +#define PEM_READ_RUN_TIMER 0xde +#define PEM_FAN_HI_SPEED 0xdf +#define PEM_FAN_NORMAL_SPEED 0xe0 +#define PEM_READ_FAN_SPEED 0xe1 + +/* offsets in data string */ +#define PEM_DATA_STATUS_2 0 +#define PEM_DATA_STATUS_1 1 +#define PEM_DATA_ALARM_2 2 +#define PEM_DATA_ALARM_1 3 +#define PEM_DATA_VOUT_LSB 4 +#define PEM_DATA_VOUT_MSB 5 +#define PEM_DATA_CURRENT 6 +#define PEM_DATA_TEMP 7 + +/* Virtual entries, to report constants */ +#define PEM_DATA_TEMP_MAX 10 +#define PEM_DATA_TEMP_CRIT 11 + +/* offsets in input string */ +#define PEM_INPUT_VOLTAGE 0 +#define PEM_INPUT_POWER_LSB 1 +#define PEM_INPUT_POWER_MSB 2 + +/* offsets in fan data */ +#define PEM_FAN_ADJUSTMENT 0 +#define PEM_FAN_FAN1 1 +#define PEM_FAN_FAN2 2 +#define PEM_FAN_FAN3 3 + +/* Status register bits */ +#define STS1_OUTPUT_ON (1 << 0) +#define STS1_LEDS_FLASHING (1 << 1) +#define STS1_EXT_FAULT (1 << 2) +#define STS1_SERVICE_LED_ON (1 << 3) +#define STS1_SHUTDOWN_OCCURRED (1 << 4) +#define STS1_INT_FAULT (1 << 5) +#define STS1_ISOLATION_TEST_OK (1 << 6) + +#define STS2_ENABLE_PIN_HI (1 << 0) +#define STS2_DATA_OUT_RANGE (1 << 1) +#define STS2_RESTARTED_OK (1 << 1) +#define STS2_ISOLATION_TEST_FAIL (1 << 3) +#define STS2_HIGH_POWER_CAP (1 << 4) +#define STS2_INVALID_INSTR (1 << 5) +#define STS2_WILL_RESTART (1 << 6) +#define STS2_PEC_ERR (1 << 7) + +/* Alarm register bits */ +#define ALRM1_VIN_OUT_LIMIT (1 << 0) +#define ALRM1_VOUT_OUT_LIMIT (1 << 1) +#define ALRM1_OV_VOLT_SHUTDOWN (1 << 2) +#define ALRM1_VIN_OVERCURRENT (1 << 3) +#define ALRM1_TEMP_WARNING (1 << 4) +#define ALRM1_TEMP_SHUTDOWN (1 << 5) +#define ALRM1_PRIMARY_FAULT (1 << 6) +#define ALRM1_POWER_LIMIT (1 << 7) + +#define ALRM2_5V_OUT_LIMIT (1 << 1) +#define ALRM2_TEMP_FAULT (1 << 2) +#define ALRM2_OV_LOW (1 << 3) +#define ALRM2_DCDC_TEMP_HIGH (1 << 4) +#define ALRM2_PRI_TEMP_HIGH (1 << 5) +#define ALRM2_NO_PRIMARY (1 << 6) +#define ALRM2_FAN_FAULT (1 << 7) + +#define FIRMWARE_REV_LEN 4 +#define DATA_STRING_LEN 9 +#define INPUT_STRING_LEN 5 /* 4 for most devices */ +#define FAN_SPEED_LEN 5 + +struct pem_data { + struct device *hwmon_dev; + + struct mutex update_lock; + bool valid; + bool fans_supported; + int input_length; + unsigned long last_updated; /* in jiffies */ + + u8 firmware_rev[FIRMWARE_REV_LEN]; + u8 data_string[DATA_STRING_LEN]; + u8 input_string[INPUT_STRING_LEN]; + u8 fan_speed[FAN_SPEED_LEN]; +}; + +static int pem_read_block(struct i2c_client *client, u8 command, u8 *data, + int data_len) +{ + u8 block_buffer[I2C_SMBUS_BLOCK_MAX]; + int result; + + result = i2c_smbus_read_block_data(client, command, block_buffer); + if (unlikely(result < 0)) + goto abort; + if (unlikely(result == 0xff || result != data_len)) { + result = -EIO; + goto abort; + } + memcpy(data, block_buffer, data_len); + result = 0; +abort: + return result; +} + +static struct pem_data *pem_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct pem_data *data = i2c_get_clientdata(client); + struct pem_data *ret = data; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + int result; + + /* Read data string */ + result = pem_read_block(client, PEM_READ_DATA_STRING, + data->data_string, + sizeof(data->data_string)); + if (unlikely(result < 0)) { + ret = ERR_PTR(result); + goto abort; + } + + /* Read input string */ + if (data->input_length) { + result = pem_read_block(client, PEM_READ_INPUT_STRING, + data->input_string, + data->input_length); + if (unlikely(result < 0)) { + ret = ERR_PTR(result); + goto abort; + } + } + + /* Read fan speeds */ + if (data->fans_supported) { + result = pem_read_block(client, PEM_READ_FAN_SPEED, + data->fan_speed, + sizeof(data->fan_speed)); + if (unlikely(result < 0)) { + ret = ERR_PTR(result); + goto abort; + } + } + + i2c_smbus_write_byte(client, PEM_CLEAR_INFO_FLAGS); + + data->last_updated = jiffies; + data->valid = 1; + } +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +static long pem_get_data(u8 *data, int len, int index) +{ + long val; + + switch (index) { + case PEM_DATA_VOUT_LSB: + val = (data[index] + (data[index+1] << 8)) * 5 / 2; + break; + case PEM_DATA_CURRENT: + val = data[index] * 200; + break; + case PEM_DATA_TEMP: + val = data[index] * 1000; + break; + case PEM_DATA_TEMP_MAX: + val = 97 * 1000; /* 97 degrees C per datasheet */ + break; + case PEM_DATA_TEMP_CRIT: + val = 107 * 1000; /* 107 degrees C per datasheet */ + break; + default: + WARN_ON_ONCE(1); + val = 0; + } + return val; +} + +static long pem_get_input(u8 *data, int len, int index) +{ + long val; + + switch (index) { + case PEM_INPUT_VOLTAGE: + if (len == INPUT_STRING_LEN) + val = (data[index] + (data[index+1] << 8) - 75) * 1000; + else + val = (data[index] - 75) * 1000; + break; + case PEM_INPUT_POWER_LSB: + if (len == INPUT_STRING_LEN) + index++; + val = (data[index] + (data[index+1] << 8)) * 1000000L; + break; + default: + WARN_ON_ONCE(1); + val = 0; + } + return val; +} + +static long pem_get_fan(u8 *data, int len, int index) +{ + long val; + + switch (index) { + case PEM_FAN_FAN1: + case PEM_FAN_FAN2: + case PEM_FAN_FAN3: + val = data[index] * 100; + break; + default: + WARN_ON_ONCE(1); + val = 0; + } + return val; +} + +/* + * Show boolean, either a fault or an alarm. + * .nr points to the register, .index is the bit mask to check + */ +static ssize_t pem_show_bool(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da); + struct pem_data *data = pem_update_device(dev); + u8 status; + + if (IS_ERR(data)) + return PTR_ERR(data); + + status = data->data_string[attr->nr] & attr->index; + return snprintf(buf, PAGE_SIZE, "%d\n", !!status); +} + +static ssize_t pem_show_data(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct pem_data *data = pem_update_device(dev); + long value; + + if (IS_ERR(data)) + return PTR_ERR(data); + + value = pem_get_data(data->data_string, sizeof(data->data_string), + attr->index); + + return snprintf(buf, PAGE_SIZE, "%ld\n", value); +} + +static ssize_t pem_show_input(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct pem_data *data = pem_update_device(dev); + long value; + + if (IS_ERR(data)) + return PTR_ERR(data); + + value = pem_get_input(data->input_string, sizeof(data->input_string), + attr->index); + + return snprintf(buf, PAGE_SIZE, "%ld\n", value); +} + +static ssize_t pem_show_fan(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct pem_data *data = pem_update_device(dev); + long value; + + if (IS_ERR(data)) + return PTR_ERR(data); + + value = pem_get_fan(data->fan_speed, sizeof(data->fan_speed), + attr->index); + + return snprintf(buf, PAGE_SIZE, "%ld\n", value); +} + +/* Voltages */ +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, pem_show_data, NULL, + PEM_DATA_VOUT_LSB); +static SENSOR_DEVICE_ATTR_2(in1_alarm, S_IRUGO, pem_show_bool, NULL, + PEM_DATA_ALARM_1, ALRM1_VOUT_OUT_LIMIT); +static SENSOR_DEVICE_ATTR_2(in1_crit_alarm, S_IRUGO, pem_show_bool, NULL, + PEM_DATA_ALARM_1, ALRM1_OV_VOLT_SHUTDOWN); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, pem_show_input, NULL, + PEM_INPUT_VOLTAGE); +static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, pem_show_bool, NULL, + PEM_DATA_ALARM_1, + ALRM1_VIN_OUT_LIMIT | ALRM1_PRIMARY_FAULT); + +/* Currents */ +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, pem_show_data, NULL, + PEM_DATA_CURRENT); +static SENSOR_DEVICE_ATTR_2(curr1_alarm, S_IRUGO, pem_show_bool, NULL, + PEM_DATA_ALARM_1, ALRM1_VIN_OVERCURRENT); + +/* Power */ +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, pem_show_input, NULL, + PEM_INPUT_POWER_LSB); +static SENSOR_DEVICE_ATTR_2(power1_alarm, S_IRUGO, pem_show_bool, NULL, + PEM_DATA_ALARM_1, ALRM1_POWER_LIMIT); + +/* Fans */ +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, pem_show_fan, NULL, + PEM_FAN_FAN1); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, pem_show_fan, NULL, + PEM_FAN_FAN2); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, pem_show_fan, NULL, + PEM_FAN_FAN3); +static SENSOR_DEVICE_ATTR_2(fan1_alarm, S_IRUGO, pem_show_bool, NULL, + PEM_DATA_ALARM_2, ALRM2_FAN_FAULT); + +/* Temperatures */ +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, pem_show_data, NULL, + PEM_DATA_TEMP); +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, pem_show_data, NULL, + PEM_DATA_TEMP_MAX); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, pem_show_data, NULL, + PEM_DATA_TEMP_CRIT); +static SENSOR_DEVICE_ATTR_2(temp1_alarm, S_IRUGO, pem_show_bool, NULL, + PEM_DATA_ALARM_1, ALRM1_TEMP_WARNING); +static SENSOR_DEVICE_ATTR_2(temp1_crit_alarm, S_IRUGO, pem_show_bool, NULL, + PEM_DATA_ALARM_1, ALRM1_TEMP_SHUTDOWN); +static SENSOR_DEVICE_ATTR_2(temp1_fault, S_IRUGO, pem_show_bool, NULL, + PEM_DATA_ALARM_2, ALRM2_TEMP_FAULT); + +static struct attribute *pem_attributes[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_alarm.dev_attr.attr, + &sensor_dev_attr_in1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + + &sensor_dev_attr_curr1_alarm.dev_attr.attr, + + &sensor_dev_attr_power1_alarm.dev_attr.attr, + + &sensor_dev_attr_fan1_alarm.dev_attr.attr, + + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_fault.dev_attr.attr, + + NULL, +}; + +static const struct attribute_group pem_group = { + .attrs = pem_attributes, +}; + +static struct attribute *pem_input_attributes[] = { + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_power1_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group pem_input_group = { + .attrs = pem_input_attributes, +}; + +static struct attribute *pem_fan_attributes[] = { + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group pem_fan_group = { + .attrs = pem_fan_attributes, +}; + +static int pem_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct pem_data *data; + int ret; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BLOCK_DATA + | I2C_FUNC_SMBUS_WRITE_BYTE)) + return -ENODEV; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* + * We use the next two commands to determine if the device is really + * there. + */ + ret = pem_read_block(client, PEM_READ_FIRMWARE_REV, + data->firmware_rev, sizeof(data->firmware_rev)); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte(client, PEM_CLEAR_INFO_FLAGS); + if (ret < 0) + return ret; + + dev_info(&client->dev, "Firmware revision %d.%d.%d\n", + data->firmware_rev[0], data->firmware_rev[1], + data->firmware_rev[2]); + + /* Register sysfs hooks */ + ret = sysfs_create_group(&client->dev.kobj, &pem_group); + if (ret) + return ret; + + /* + * Check if input readings are supported. + * This is the case if we can read input data, + * and if the returned data is not all zeros. + * Note that input alarms are always supported. + */ + ret = pem_read_block(client, PEM_READ_INPUT_STRING, + data->input_string, + sizeof(data->input_string) - 1); + if (!ret && (data->input_string[0] || data->input_string[1] || + data->input_string[2])) + data->input_length = sizeof(data->input_string) - 1; + else if (ret < 0) { + /* Input string is one byte longer for some devices */ + ret = pem_read_block(client, PEM_READ_INPUT_STRING, + data->input_string, + sizeof(data->input_string)); + if (!ret && (data->input_string[0] || data->input_string[1] || + data->input_string[2] || data->input_string[3])) + data->input_length = sizeof(data->input_string); + } + ret = 0; + if (data->input_length) { + ret = sysfs_create_group(&client->dev.kobj, &pem_input_group); + if (ret) + goto out_remove_groups; + } + + /* + * Check if fan speed readings are supported. + * This is the case if we can read fan speed data, + * and if the returned data is not all zeros. + * Note that the fan alarm is always supported. + */ + ret = pem_read_block(client, PEM_READ_FAN_SPEED, + data->fan_speed, + sizeof(data->fan_speed)); + if (!ret && (data->fan_speed[0] || data->fan_speed[1] || + data->fan_speed[2] || data->fan_speed[3])) { + data->fans_supported = true; + ret = sysfs_create_group(&client->dev.kobj, &pem_fan_group); + if (ret) + goto out_remove_groups; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + goto out_remove_groups; + } + + return 0; + +out_remove_groups: + sysfs_remove_group(&client->dev.kobj, &pem_input_group); + sysfs_remove_group(&client->dev.kobj, &pem_fan_group); + sysfs_remove_group(&client->dev.kobj, &pem_group); + return ret; +} + +static int pem_remove(struct i2c_client *client) +{ + struct pem_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + + sysfs_remove_group(&client->dev.kobj, &pem_input_group); + sysfs_remove_group(&client->dev.kobj, &pem_fan_group); + sysfs_remove_group(&client->dev.kobj, &pem_group); + + return 0; +} + +static const struct i2c_device_id pem_id[] = { + {"lineage_pem", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, pem_id); + +static struct i2c_driver pem_driver = { + .driver = { + .name = "lineage_pem", + }, + .probe = pem_probe, + .remove = pem_remove, + .id_table = pem_id, +}; + +module_i2c_driver(pem_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("Lineage CPL PEM hardware monitoring driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c deleted file mode 100644 index b2f2277cad3..00000000000 --- a/drivers/hwmon/lis3lv02d.c +++ /dev/null @@ -1,610 +0,0 @@ -/* - * lis3lv02d.c - ST LIS3LV02DL accelerometer driver - * - * Copyright (C) 2007-2008 Yan Burman - * Copyright (C) 2008 Eric Piel - * Copyright (C) 2008-2009 Pavel Machek - * - * 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/kernel.h> -#include <linux/init.h> -#include <linux/dmi.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/input-polldev.h> -#include <linux/delay.h> -#include <linux/wait.h> -#include <linux/poll.h> -#include <linux/freezer.h> -#include <linux/uaccess.h> -#include <linux/miscdevice.h> -#include <asm/atomic.h> -#include "lis3lv02d.h" - -#define DRIVER_NAME "lis3lv02d" - -/* joystick device poll interval in milliseconds */ -#define MDPS_POLL_INTERVAL 50 -/* - * 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 - * to keep the interrupt for the free-fall event. The values are updated at - * 40Hz (at the lowest frequency), but as it can be pretty time consuming on - * some low processor, we poll the sensor only at 20Hz... enough for the - * joystick. - */ - -#define LIS3_PWRON_DELAY_WAI_12B (5000) -#define LIS3_PWRON_DELAY_WAI_8B (3000) - -/* - * LIS3LV02D spec says 1024 LSBs corresponds 1 G -> 1LSB is 1000/1024 mG - * LIS302D spec says: 18 mG / digit - * LIS3_ACCURACY is used to increase accuracy of the intermediate - * calculation results. - */ -#define LIS3_ACCURACY 1024 -/* Sensitivity values for -2G +2G scale */ -#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 - -struct lis3lv02d lis3_dev = { - .misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER(lis3_dev.misc_wait), -}; - -EXPORT_SYMBOL_GPL(lis3_dev); - -static s16 lis3lv02d_read_8(struct lis3lv02d *lis3, int reg) -{ - s8 lo; - if (lis3->read(lis3, reg, &lo) < 0) - return 0; - - return lo; -} - -static s16 lis3lv02d_read_12(struct lis3lv02d *lis3, int reg) -{ - u8 lo, hi; - - lis3->read(lis3, reg - 1, &lo); - lis3->read(lis3, reg, &hi); - /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */ - return (s16)((hi << 8) | lo); -} - -/** - * lis3lv02d_get_axis - For the given axis, give the value converted - * @axis: 1,2,3 - can also be negative - * @hw_values: raw values returned by the hardware - * - * Returns the converted value. - */ -static inline int lis3lv02d_get_axis(s8 axis, int hw_values[3]) -{ - if (axis > 0) - return hw_values[axis - 1]; - else - return -hw_values[-axis - 1]; -} - -/** - * lis3lv02d_get_xyz - Get X, Y and Z axis values from the accelerometer - * @lis3: pointer to the device struct - * @x: where to store the X axis value - * @y: where to store the Y axis value - * @z: where to store the Z axis value - * - * Note that 40Hz input device can eat up about 10% CPU at 800MHZ - */ -static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z) -{ - int position[3]; - int i; - - mutex_lock(&lis3->mutex); - position[0] = lis3->read_data(lis3, OUTX); - position[1] = lis3->read_data(lis3, OUTY); - position[2] = lis3->read_data(lis3, OUTZ); - mutex_unlock(&lis3->mutex); - - for (i = 0; i < 3; i++) - position[i] = (position[i] * lis3->scale) / LIS3_ACCURACY; - - *x = lis3lv02d_get_axis(lis3->ac.x, position); - *y = lis3lv02d_get_axis(lis3->ac.y, position); - *z = lis3lv02d_get_axis(lis3->ac.z, position); -} - -/* 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}; - -/* ODR is Output Data Rate */ -static int lis3lv02d_get_odr(void) -{ - u8 ctrl; - int shift; - - lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl); - ctrl &= lis3_dev.odr_mask; - shift = ffs(lis3_dev.odr_mask) - 1; - return lis3_dev.odrs[(ctrl >> shift)]; -} - -static int lis3lv02d_set_odr(int rate) -{ - u8 ctrl; - int i, len, shift; - - lis3_dev.read(&lis3_dev, CTRL_REG1, &ctrl); - ctrl &= ~lis3_dev.odr_mask; - len = 1 << hweight_long(lis3_dev.odr_mask); /* # of possible values */ - shift = ffs(lis3_dev.odr_mask) - 1; - - for (i = 0; i < len; i++) - if (lis3_dev.odrs[i] == rate) { - lis3_dev.write(&lis3_dev, CTRL_REG1, - ctrl | (i << shift)); - return 0; - } - return -EINVAL; -} - -static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3]) -{ - u8 reg; - s16 x, y, z; - u8 selftest; - int ret; - - 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)); - msleep(lis3->pwron_delay / lis3lv02d_get_odr()); - - /* Read directly to avoid axis remap */ - x = lis3->read_data(lis3, OUTX); - y = lis3->read_data(lis3, OUTY); - z = lis3->read_data(lis3, OUTZ); - - /* back to normal settings */ - lis3->write(lis3, CTRL_REG1, reg); - msleep(lis3->pwron_delay / lis3lv02d_get_odr()); - - results[0] = x - lis3->read_data(lis3, OUTX); - results[1] = y - lis3->read_data(lis3, OUTY); - results[2] = z - lis3->read_data(lis3, OUTZ); - - ret = 0; - 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; - goto fail; - } - } - } - - /* test passed */ -fail: - mutex_unlock(&lis3->mutex); - return ret; -} - -void lis3lv02d_poweroff(struct lis3lv02d *lis3) -{ - /* disable X,Y,Z axis and power down */ - lis3->write(lis3, CTRL_REG1, 0x00); -} -EXPORT_SYMBOL_GPL(lis3lv02d_poweroff); - -void lis3lv02d_poweron(struct lis3lv02d *lis3) -{ - u8 reg; - - 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. - */ - if (lis3->whoami == WAI_12B) { - lis3->read(lis3, CTRL_REG2, ®); - reg |= CTRL2_BDU; - lis3->write(lis3, CTRL_REG2, reg); - } -} -EXPORT_SYMBOL_GPL(lis3lv02d_poweron); - - -static irqreturn_t lis302dl_interrupt(int irq, void *dummy) -{ - /* - * Be careful: on some HP laptops the bios force DD when on battery and - * the lid is closed. This leads to interrupts as soon as a little move - * is done. - */ - atomic_inc(&lis3_dev.count); - - wake_up_interruptible(&lis3_dev.misc_wait); - kill_fasync(&lis3_dev.async_queue, SIGIO, POLL_IN); - return IRQ_HANDLED; -} - -static int lis3lv02d_misc_open(struct inode *inode, struct file *file) -{ - int ret; - - if (test_and_set_bit(0, &lis3_dev.misc_opened)) - return -EBUSY; /* already open */ - - atomic_set(&lis3_dev.count, 0); - - /* - * The sensor can generate interrupts for free-fall and direction - * detection (distinguishable with FF_WU_SRC and DD_SRC) but to keep - * the things simple and _fast_ we activate it only for free-fall, so - * no need to read register (very slow with ACPI). For the same reason, - * we forbid shared interrupts. - * - * IRQF_TRIGGER_RISING seems pointless on HP laptops because the - * io-apic is not configurable (and generates a warning) but I keep it - * in case of support for other hardware. - */ - ret = request_irq(lis3_dev.irq, lis302dl_interrupt, IRQF_TRIGGER_RISING, - DRIVER_NAME, &lis3_dev); - - if (ret) { - clear_bit(0, &lis3_dev.misc_opened); - printk(KERN_ERR DRIVER_NAME ": IRQ%d allocation failed\n", lis3_dev.irq); - return -EBUSY; - } - return 0; -} - -static int lis3lv02d_misc_release(struct inode *inode, struct file *file) -{ - fasync_helper(-1, file, 0, &lis3_dev.async_queue); - free_irq(lis3_dev.irq, &lis3_dev); - clear_bit(0, &lis3_dev.misc_opened); /* release the device */ - return 0; -} - -static ssize_t lis3lv02d_misc_read(struct file *file, char __user *buf, - size_t count, loff_t *pos) -{ - DECLARE_WAITQUEUE(wait, current); - u32 data; - unsigned char byte_data; - ssize_t retval = 1; - - if (count < 1) - return -EINVAL; - - add_wait_queue(&lis3_dev.misc_wait, &wait); - while (true) { - set_current_state(TASK_INTERRUPTIBLE); - data = atomic_xchg(&lis3_dev.count, 0); - if (data) - break; - - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - goto out; - } - - if (signal_pending(current)) { - retval = -ERESTARTSYS; - goto out; - } - - schedule(); - } - - if (data < 255) - byte_data = data; - else - byte_data = 255; - - /* make sure we are not going into copy_to_user() with - * TASK_INTERRUPTIBLE state */ - set_current_state(TASK_RUNNING); - if (copy_to_user(buf, &byte_data, sizeof(byte_data))) - retval = -EFAULT; - -out: - __set_current_state(TASK_RUNNING); - remove_wait_queue(&lis3_dev.misc_wait, &wait); - - return retval; -} - -static unsigned int lis3lv02d_misc_poll(struct file *file, poll_table *wait) -{ - poll_wait(file, &lis3_dev.misc_wait, wait); - if (atomic_read(&lis3_dev.count)) - return POLLIN | POLLRDNORM; - return 0; -} - -static int lis3lv02d_misc_fasync(int fd, struct file *file, int on) -{ - return fasync_helper(fd, file, on, &lis3_dev.async_queue); -} - -static const struct file_operations lis3lv02d_misc_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = lis3lv02d_misc_read, - .open = lis3lv02d_misc_open, - .release = lis3lv02d_misc_release, - .poll = lis3lv02d_misc_poll, - .fasync = lis3lv02d_misc_fasync, -}; - -static struct miscdevice lis3lv02d_misc_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = "freefall", - .fops = &lis3lv02d_misc_fops, -}; - -static void lis3lv02d_joystick_poll(struct input_polled_dev *pidev) -{ - int x, y, z; - - lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z); - input_report_abs(pidev->input, ABS_X, x); - input_report_abs(pidev->input, ABS_Y, y); - input_report_abs(pidev->input, ABS_Z, z); - input_sync(pidev->input); -} - -int lis3lv02d_joystick_enable(void) -{ - struct input_dev *input_dev; - int err; - int max_val, fuzz, flat; - - if (lis3_dev.idev) - return -EINVAL; - - lis3_dev.idev = input_allocate_polled_device(); - if (!lis3_dev.idev) - return -ENOMEM; - - lis3_dev.idev->poll = lis3lv02d_joystick_poll; - lis3_dev.idev->poll_interval = MDPS_POLL_INTERVAL; - input_dev = lis3_dev.idev->input; - - input_dev->name = "ST LIS3LV02DL Accelerometer"; - input_dev->phys = DRIVER_NAME "/input0"; - input_dev->id.bustype = BUS_HOST; - input_dev->id.vendor = 0; - input_dev->dev.parent = &lis3_dev.pdev->dev; - - 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; - 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); - - err = input_register_polled_device(lis3_dev.idev); - if (err) { - input_free_polled_device(lis3_dev.idev); - lis3_dev.idev = NULL; - } - - return err; -} -EXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable); - -void lis3lv02d_joystick_disable(void) -{ - if (!lis3_dev.idev) - return; - - if (lis3_dev.irq) - misc_deregister(&lis3lv02d_misc_device); - input_unregister_polled_device(lis3_dev.idev); - input_free_polled_device(lis3_dev.idev); - lis3_dev.idev = NULL; -} -EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable); - -/* Sysfs stuff */ -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", - values[0], values[1], values[2]); -} - -static ssize_t lis3lv02d_position_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int x, y, z; - - lis3lv02d_get_xyz(&lis3_dev, &x, &y, &z); - return sprintf(buf, "(%d,%d,%d)\n", x, y, z); -} - -static ssize_t lis3lv02d_rate_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%d\n", lis3lv02d_get_odr()); -} - -static ssize_t lis3lv02d_rate_set(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t count) -{ - unsigned long rate; - - if (strict_strtoul(buf, 0, &rate)) - return -EINVAL; - - if (lis3lv02d_set_odr(rate)) - return -EINVAL; - - return count; -} - -static DEVICE_ATTR(selftest, S_IRUSR, lis3lv02d_selftest_show, NULL); -static DEVICE_ATTR(position, S_IRUGO, lis3lv02d_position_show, NULL); -static DEVICE_ATTR(rate, S_IRUGO | S_IWUSR, lis3lv02d_rate_show, - lis3lv02d_rate_set); - -static struct attribute *lis3lv02d_attributes[] = { - &dev_attr_selftest.attr, - &dev_attr_position.attr, - &dev_attr_rate.attr, - NULL -}; - -static struct attribute_group lis3lv02d_attribute_group = { - .attrs = lis3lv02d_attributes -}; - - -static int lis3lv02d_add_fs(struct lis3lv02d *lis3) -{ - lis3->pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); - if (IS_ERR(lis3->pdev)) - return PTR_ERR(lis3->pdev); - - return sysfs_create_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group); -} - -int lis3lv02d_remove_fs(struct lis3lv02d *lis3) -{ - sysfs_remove_group(&lis3->pdev->dev.kobj, &lis3lv02d_attribute_group); - platform_device_unregister(lis3->pdev); - return 0; -} -EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); - -/* - * Initialise the accelerometer and the various subsystems. - * Should be rather independent of the bus system. - */ -int lis3lv02d_init_device(struct lis3lv02d *dev) -{ - dev->whoami = lis3lv02d_read_8(dev, WHO_AM_I); - - switch (dev->whoami) { - case WAI_12B: - printk(KERN_INFO DRIVER_NAME ": 12 bits sensor found\n"); - dev->read_data = lis3lv02d_read_12; - dev->mdps_max_val = 2048; - dev->pwron_delay = LIS3_PWRON_DELAY_WAI_12B; - dev->odrs = lis3_12_rates; - dev->odr_mask = CTRL1_DF0 | CTRL1_DF1; - dev->scale = LIS3_SENSITIVITY_12B; - break; - case WAI_8B: - printk(KERN_INFO DRIVER_NAME ": 8 bits 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_8_rates; - dev->odr_mask = CTRL1_DR; - dev->scale = LIS3_SENSITIVITY_8B; - break; - default: - printk(KERN_ERR DRIVER_NAME - ": unknown sensor type 0x%X\n", dev->whoami); - return -EINVAL; - } - - mutex_init(&dev->mutex); - - lis3lv02d_add_fs(dev); - lis3lv02d_poweron(dev); - - if (lis3lv02d_joystick_enable()) - printk(KERN_ERR DRIVER_NAME ": joystick initialization failed\n"); - - /* passing in platform specific data is purely optional and only - * used by the SPI transport layer at the moment */ - if (dev->pdata) { - struct lis3lv02d_platform_data *p = dev->pdata; - - if (p->click_flags && (dev->whoami == WAI_8B)) { - dev->write(dev, CLICK_CFG, p->click_flags); - dev->write(dev, CLICK_TIMELIMIT, p->click_time_limit); - dev->write(dev, CLICK_LATENCY, p->click_latency); - dev->write(dev, CLICK_WINDOW, p->click_window); - dev->write(dev, CLICK_THSZ, p->click_thresh_z & 0xf); - dev->write(dev, CLICK_THSY_X, - (p->click_thresh_x & 0xf) | - (p->click_thresh_y << 4)); - } - - if (p->wakeup_flags && (dev->whoami == WAI_8B)) { - 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); - /* enable high pass filter for both free-fall units */ - dev->write(dev, CTRL_REG2, HP_FF_WU1 | HP_FF_WU2); - } - - if (p->irq_cfg) - dev->write(dev, CTRL_REG3, p->irq_cfg); - } - - /* bail if we did not get an IRQ from the bus layer */ - if (!dev->irq) { - printk(KERN_ERR DRIVER_NAME - ": No IRQ. Disabling /dev/freefall\n"); - goto out; - } - - if (misc_register(&lis3lv02d_misc_device)) - printk(KERN_ERR DRIVER_NAME ": misc_register failed\n"); -out: - return 0; -} -EXPORT_SYMBOL_GPL(lis3lv02d_init_device); - -MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver"); -MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); -MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h deleted file mode 100644 index e6a01f44709..00000000000 --- a/drivers/hwmon/lis3lv02d.h +++ /dev/null @@ -1,243 +0,0 @@ -/* - * lis3lv02d.h - ST LIS3LV02DL accelerometer driver - * - * Copyright (C) 2007-2008 Yan Burman - * Copyright (C) 2008-2009 Eric Piel - * - * 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/platform_device.h> -#include <linux/input-polldev.h> - -/* - * This driver tries to support the "digital" accelerometer chips from - * STMicroelectronics such as LIS3LV02DL, LIS302DL, LIS3L02DQ, LIS331DL, - * LIS35DE, or LIS202DL. They are very similar in terms of programming, with - * almost the same registers. In addition to differing on physical properties, - * they differ on the number of axes (2/3), precision (8/12 bits), and special - * features (freefall detection, click...). Unfortunately, not all the - * differences can be probed via a register. - * They can be connected either via I²C or SPI. - */ - -#include <linux/lis3lv02d.h> - -enum lis3_reg { - WHO_AM_I = 0x0F, - OFFSET_X = 0x16, - OFFSET_Y = 0x17, - OFFSET_Z = 0x18, - GAIN_X = 0x19, - GAIN_Y = 0x1A, - GAIN_Z = 0x1B, - CTRL_REG1 = 0x20, - CTRL_REG2 = 0x21, - CTRL_REG3 = 0x22, - HP_FILTER_RESET = 0x23, - STATUS_REG = 0x27, - OUTX_L = 0x28, - OUTX_H = 0x29, - OUTX = 0x29, - OUTY_L = 0x2A, - OUTY_H = 0x2B, - OUTY = 0x2B, - OUTZ_L = 0x2C, - OUTZ_H = 0x2D, - OUTZ = 0x2D, -}; - -enum lis302d_reg { - FF_WU_CFG_1 = 0x30, - FF_WU_SRC_1 = 0x31, - FF_WU_THS_1 = 0x32, - FF_WU_DURATION_1 = 0x33, - FF_WU_CFG_2 = 0x34, - FF_WU_SRC_2 = 0x35, - FF_WU_THS_2 = 0x36, - FF_WU_DURATION_2 = 0x37, - CLICK_CFG = 0x38, - CLICK_SRC = 0x39, - CLICK_THSY_X = 0x3B, - CLICK_THSZ = 0x3C, - CLICK_TIMELIMIT = 0x3D, - CLICK_LATENCY = 0x3E, - CLICK_WINDOW = 0x3F, -}; - -enum lis3lv02d_reg { - FF_WU_CFG = 0x30, - FF_WU_SRC = 0x31, - FF_WU_ACK = 0x32, - FF_WU_THS_L = 0x34, - FF_WU_THS_H = 0x35, - FF_WU_DURATION = 0x36, - DD_CFG = 0x38, - DD_SRC = 0x39, - DD_ACK = 0x3A, - DD_THSI_L = 0x3C, - DD_THSI_H = 0x3D, - DD_THSE_L = 0x3E, - DD_THSE_H = 0x3F, -}; - -enum lis3_who_am_i { - WAI_12B = 0x3A, /* 12 bits: LIS3LV02D[LQ]... */ - WAI_8B = 0x3B, /* 8 bits: LIS[23]02D[LQ]... */ - WAI_6B = 0x52, /* 6 bits: LIS331DLF - not supported */ -}; - -enum lis3lv02d_ctrl1_12b { - CTRL1_Xen = 0x01, - CTRL1_Yen = 0x02, - CTRL1_Zen = 0x04, - CTRL1_ST = 0x08, - CTRL1_DF0 = 0x10, - CTRL1_DF1 = 0x20, - CTRL1_PD0 = 0x40, - CTRL1_PD1 = 0x80, -}; - -/* Delta to ctrl1_12b version */ -enum lis3lv02d_ctrl1_8b { - CTRL1_STM = 0x08, - CTRL1_STP = 0x10, - CTRL1_FS = 0x20, - CTRL1_PD = 0x40, - CTRL1_DR = 0x80, -}; - -enum lis3lv02d_ctrl2 { - CTRL2_DAS = 0x01, - CTRL2_SIM = 0x02, - CTRL2_DRDY = 0x04, - CTRL2_IEN = 0x08, - CTRL2_BOOT = 0x10, - CTRL2_BLE = 0x20, - CTRL2_BDU = 0x40, /* Block Data Update */ - CTRL2_FS = 0x80, /* Full Scale selection */ -}; - -enum lis302d_ctrl2 { - HP_FF_WU2 = 0x08, - HP_FF_WU1 = 0x04, -}; - -enum lis3lv02d_ctrl3 { - CTRL3_CFS0 = 0x01, - CTRL3_CFS1 = 0x02, - CTRL3_FDS = 0x10, - CTRL3_HPFF = 0x20, - CTRL3_HPDD = 0x40, - CTRL3_ECK = 0x80, -}; - -enum lis3lv02d_status_reg { - STATUS_XDA = 0x01, - STATUS_YDA = 0x02, - STATUS_ZDA = 0x04, - STATUS_XYZDA = 0x08, - STATUS_XOR = 0x10, - STATUS_YOR = 0x20, - STATUS_ZOR = 0x40, - STATUS_XYZOR = 0x80, -}; - -enum lis3lv02d_ff_wu_cfg { - FF_WU_CFG_XLIE = 0x01, - FF_WU_CFG_XHIE = 0x02, - FF_WU_CFG_YLIE = 0x04, - FF_WU_CFG_YHIE = 0x08, - FF_WU_CFG_ZLIE = 0x10, - FF_WU_CFG_ZHIE = 0x20, - FF_WU_CFG_LIR = 0x40, - FF_WU_CFG_AOI = 0x80, -}; - -enum lis3lv02d_ff_wu_src { - FF_WU_SRC_XL = 0x01, - FF_WU_SRC_XH = 0x02, - FF_WU_SRC_YL = 0x04, - FF_WU_SRC_YH = 0x08, - FF_WU_SRC_ZL = 0x10, - FF_WU_SRC_ZH = 0x20, - FF_WU_SRC_IA = 0x40, -}; - -enum lis3lv02d_dd_cfg { - DD_CFG_XLIE = 0x01, - DD_CFG_XHIE = 0x02, - DD_CFG_YLIE = 0x04, - DD_CFG_YHIE = 0x08, - DD_CFG_ZLIE = 0x10, - DD_CFG_ZHIE = 0x20, - DD_CFG_LIR = 0x40, - DD_CFG_IEND = 0x80, -}; - -enum lis3lv02d_dd_src { - DD_SRC_XL = 0x01, - DD_SRC_XH = 0x02, - DD_SRC_YL = 0x04, - DD_SRC_YH = 0x08, - DD_SRC_ZL = 0x10, - DD_SRC_ZH = 0x20, - DD_SRC_IA = 0x40, -}; - -struct axis_conversion { - s8 x; - s8 y; - s8 z; -}; - -struct lis3lv02d { - void *bus_priv; /* used by the bus layer only */ - int (*init) (struct lis3lv02d *lis3); - int (*write) (struct lis3lv02d *lis3, int reg, u8 val); - int (*read) (struct lis3lv02d *lis3, int reg, u8 *ret); - - int *odrs; /* Supported output data rates */ - u8 odr_mask; /* ODR bit mask */ - u8 whoami; /* indicates measurement precision */ - s16 (*read_data) (struct lis3lv02d *lis3, int reg); - int mdps_max_val; - int pwron_delay; - int scale; /* - * relationship between 1 LBS and mG - * (1/1000th of earth gravity) - */ - - struct input_polled_dev *idev; /* input device */ - struct platform_device *pdev; /* platform device */ - atomic_t count; /* interrupt count after last read */ - struct axis_conversion ac; /* hw -> logical axis */ - - 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 */ - - struct lis3lv02d_platform_data *pdata; /* for passing board config */ - struct mutex mutex; /* Serialize poll and selftest */ -}; - -int lis3lv02d_init_device(struct lis3lv02d *lis3); -int lis3lv02d_joystick_enable(void); -void lis3lv02d_joystick_disable(void); -void lis3lv02d_poweroff(struct lis3lv02d *lis3); -void lis3lv02d_poweron(struct lis3lv02d *lis3); -int lis3lv02d_remove_fs(struct lis3lv02d *lis3); - -extern struct lis3lv02d lis3_dev; diff --git a/drivers/hwmon/lis3lv02d_i2c.c b/drivers/hwmon/lis3lv02d_i2c.c deleted file mode 100644 index dc1f5402c1d..00000000000 --- a/drivers/hwmon/lis3lv02d_i2c.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * drivers/hwmon/lis3lv02d_i2c.c - * - * Implements I2C interface for lis3lv02d (STMicroelectronics) accelerometer. - * Driver is based on corresponding SPI driver written by Daniel Mack - * (lis3lv02d_spi.c (C) 2009 Daniel Mack <daniel@caiaq.de> ). - * - * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). - * - * Contact: Samu Onkalo <samu.p.onkalo@nokia.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/err.h> -#include <linux/i2c.h> -#include "lis3lv02d.h" - -#define DRV_NAME "lis3lv02d_i2c" - -static inline s32 lis3_i2c_write(struct lis3lv02d *lis3, int reg, u8 value) -{ - struct i2c_client *c = lis3->bus_priv; - return i2c_smbus_write_byte_data(c, reg, value); -} - -static inline s32 lis3_i2c_read(struct lis3lv02d *lis3, int reg, u8 *v) -{ - struct i2c_client *c = lis3->bus_priv; - *v = i2c_smbus_read_byte_data(c, reg); - return 0; -} - -static int lis3_i2c_init(struct lis3lv02d *lis3) -{ - u8 reg; - int ret; - - /* power up the device */ - ret = lis3->read(lis3, CTRL_REG1, ®); - if (ret < 0) - return ret; - - reg |= CTRL1_PD0; - 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 int __devinit lis3lv02d_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int ret = 0; - struct lis3lv02d_platform_data *pdata = client->dev.platform_data; - - if (pdata) { - if (pdata->axis_x) - lis3lv02d_axis_map.x = pdata->axis_x; - - if (pdata->axis_y) - lis3lv02d_axis_map.y = pdata->axis_y; - - if (pdata->axis_z) - lis3lv02d_axis_map.z = pdata->axis_z; - - if (pdata->setup_resources) - ret = pdata->setup_resources(); - - if (ret) - goto fail; - } - - lis3_dev.pdata = pdata; - lis3_dev.bus_priv = client; - lis3_dev.init = lis3_i2c_init; - lis3_dev.read = lis3_i2c_read; - lis3_dev.write = lis3_i2c_write; - lis3_dev.irq = client->irq; - lis3_dev.ac = lis3lv02d_axis_map; - - i2c_set_clientdata(client, &lis3_dev); - ret = lis3lv02d_init_device(&lis3_dev); -fail: - return ret; -} - -static int __devexit lis3lv02d_i2c_remove(struct i2c_client *client) -{ - struct lis3lv02d *lis3 = i2c_get_clientdata(client); - struct lis3lv02d_platform_data *pdata = client->dev.platform_data; - - if (pdata && pdata->release_resources) - pdata->release_resources(); - - lis3lv02d_joystick_disable(); - lis3lv02d_poweroff(lis3); - - return lis3lv02d_remove_fs(&lis3_dev); -} - -#ifdef CONFIG_PM -static int lis3lv02d_i2c_suspend(struct i2c_client *client, pm_message_t mesg) -{ - struct lis3lv02d *lis3 = i2c_get_clientdata(client); - - if (!lis3->pdata->wakeup_flags) - lis3lv02d_poweroff(lis3); - return 0; -} - -static int lis3lv02d_i2c_resume(struct i2c_client *client) -{ - struct lis3lv02d *lis3 = i2c_get_clientdata(client); - - if (!lis3->pdata->wakeup_flags) - lis3lv02d_poweron(lis3); - return 0; -} - -static void lis3lv02d_i2c_shutdown(struct i2c_client *client) -{ - lis3lv02d_i2c_suspend(client, PMSG_SUSPEND); -} -#else -#define lis3lv02d_i2c_suspend NULL -#define lis3lv02d_i2c_resume NULL -#define lis3lv02d_i2c_shutdown NULL -#endif - -static const struct i2c_device_id lis3lv02d_id[] = { - {"lis3lv02d", 0 }, - {} -}; - -MODULE_DEVICE_TABLE(i2c, lis3lv02d_id); - -static struct i2c_driver lis3lv02d_i2c_driver = { - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - }, - .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, -}; - -static int __init lis3lv02d_init(void) -{ - return i2c_add_driver(&lis3lv02d_i2c_driver); -} - -static void __exit lis3lv02d_exit(void) -{ - i2c_del_driver(&lis3lv02d_i2c_driver); -} - -MODULE_AUTHOR("Nokia Corporation"); -MODULE_DESCRIPTION("lis3lv02d I2C interface"); -MODULE_LICENSE("GPL"); - -module_init(lis3lv02d_init); -module_exit(lis3lv02d_exit); diff --git a/drivers/hwmon/lis3lv02d_spi.c b/drivers/hwmon/lis3lv02d_spi.c deleted file mode 100644 index 82b16808a27..00000000000 --- a/drivers/hwmon/lis3lv02d_spi.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * lis3lv02d_spi - SPI glue layer for lis3lv02d - * - * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * publishhed by the Free Software Foundation. - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/err.h> -#include <linux/input.h> -#include <linux/interrupt.h> -#include <linux/workqueue.h> -#include <linux/spi/spi.h> - -#include "lis3lv02d.h" - -#define DRV_NAME "lis3lv02d_spi" -#define LIS3_SPI_READ 0x80 - -static int lis3_spi_read(struct lis3lv02d *lis3, int reg, u8 *v) -{ - struct spi_device *spi = lis3->bus_priv; - int ret = spi_w8r8(spi, reg | LIS3_SPI_READ); - if (ret < 0) - return -EINVAL; - - *v = (u8) ret; - return 0; -} - -static int lis3_spi_write(struct lis3lv02d *lis3, int reg, u8 val) -{ - u8 tmp[2] = { reg, val }; - struct spi_device *spi = lis3->bus_priv; - return spi_write(spi, tmp, sizeof(tmp)); -} - -static int lis3_spi_init(struct lis3lv02d *lis3) -{ - u8 reg; - int ret; - - /* power up the device */ - ret = lis3->read(lis3, CTRL_REG1, ®); - if (ret < 0) - return ret; - - reg |= CTRL1_PD0; - return lis3->write(lis3, CTRL_REG1, reg); -} - -static struct axis_conversion lis3lv02d_axis_normal = { 1, 2, 3 }; - -static int __devinit lis302dl_spi_probe(struct spi_device *spi) -{ - int ret; - - spi->bits_per_word = 8; - spi->mode = SPI_MODE_0; - ret = spi_setup(spi); - if (ret < 0) - return ret; - - lis3_dev.bus_priv = spi; - lis3_dev.init = lis3_spi_init; - lis3_dev.read = lis3_spi_read; - lis3_dev.write = lis3_spi_write; - lis3_dev.irq = spi->irq; - lis3_dev.ac = lis3lv02d_axis_normal; - lis3_dev.pdata = spi->dev.platform_data; - spi_set_drvdata(spi, &lis3_dev); - - return lis3lv02d_init_device(&lis3_dev); -} - -static int __devexit lis302dl_spi_remove(struct spi_device *spi) -{ - struct lis3lv02d *lis3 = spi_get_drvdata(spi); - lis3lv02d_joystick_disable(); - lis3lv02d_poweroff(lis3); - - return lis3lv02d_remove_fs(&lis3_dev); -} - -#ifdef CONFIG_PM -static int lis3lv02d_spi_suspend(struct spi_device *spi, pm_message_t mesg) -{ - struct lis3lv02d *lis3 = spi_get_drvdata(spi); - - if (!lis3->pdata->wakeup_flags) - lis3lv02d_poweroff(&lis3_dev); - - return 0; -} - -static int lis3lv02d_spi_resume(struct spi_device *spi) -{ - struct lis3lv02d *lis3 = spi_get_drvdata(spi); - - if (!lis3->pdata->wakeup_flags) - lis3lv02d_poweron(lis3); - - return 0; -} - -#else -#define lis3lv02d_spi_suspend NULL -#define lis3lv02d_spi_resume NULL -#endif - -static struct spi_driver lis302dl_spi_driver = { - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - }, - .probe = lis302dl_spi_probe, - .remove = __devexit_p(lis302dl_spi_remove), - .suspend = lis3lv02d_spi_suspend, - .resume = lis3lv02d_spi_resume, -}; - -static int __init lis302dl_init(void) -{ - return spi_register_driver(&lis302dl_spi_driver); -} - -static void __exit lis302dl_exit(void) -{ - spi_unregister_driver(&lis302dl_spi_driver); -} - -module_init(lis302dl_init); -module_exit(lis302dl_exit); - -MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); -MODULE_DESCRIPTION("lis3lv02d SPI glue layer"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("spi:" DRV_NAME); diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index bf81aff7051..848b9611151 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -1,7 +1,7 @@ /* * lm63.c - driver for the National Semiconductor LM63 temperature sensor * with integrated fan control - * Copyright (C) 2004-2008 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2004-2008 Jean Delvare <jdelvare@suse.de> * Based on the lm90 driver. * * The LM63 is a sensor chip made by National Semiconductor. It measures @@ -47,19 +47,24 @@ #include <linux/err.h> #include <linux/mutex.h> #include <linux/sysfs.h> +#include <linux/types.h> /* * Addresses to scan - * Address is fully defined internally and cannot be changed. + * Address is fully defined internally and cannot be changed except for + * LM64 which has one pin dedicated to address selection. + * LM63 and LM96163 have address 0x4c. + * LM64 can have address 0x18 or 0x4e. */ -static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END }; +static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END }; /* * The LM63 registers */ #define LM63_REG_CONFIG1 0x03 +#define LM63_REG_CONVRATE 0x04 #define LM63_REG_CONFIG2 0xBF #define LM63_REG_CONFIG_FAN 0x4A @@ -70,6 +75,9 @@ static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END }; #define LM63_REG_PWM_VALUE 0x4C #define LM63_REG_PWM_FREQ 0x4D +#define LM63_REG_LUT_TEMP_HYST 0x4F +#define LM63_REG_LUT_TEMP(nr) (0x50 + 2 * (nr)) +#define LM63_REG_LUT_PWM(nr) (0x51 + 2 * (nr)) #define LM63_REG_LOCAL_TEMP 0x00 #define LM63_REG_LOCAL_HIGH 0x05 @@ -91,6 +99,16 @@ static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END }; #define LM63_REG_MAN_ID 0xFE #define LM63_REG_CHIP_ID 0xFF +#define LM96163_REG_TRUTHERM 0x30 +#define LM96163_REG_REMOTE_TEMP_U_MSB 0x31 +#define LM96163_REG_REMOTE_TEMP_U_LSB 0x32 +#define LM96163_REG_CONFIG_ENHANCED 0x45 + +#define LM63_MAX_CONVRATE 9 + +#define LM63_MAX_CONVRATE_HZ 32 +#define LM96163_MAX_CONVRATE_HZ 26 + /* * Conversions and various macros * For tachometer counts, the LM63 uses 16-bit values. @@ -98,6 +116,9 @@ static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END }; * value, it uses signed 8-bit values with LSB = 1 degree Celsius. * For remote temperature, low and high limits, it uses signed 11-bit values * with LSB = 0.125 degree Celsius, left-justified in 16-bit registers. + * For LM64 the actual remote diode temperature is 16 degree Celsius higher + * than the register reading. Remote temperature setpoints have to be + * adapted accordingly. */ #define FAN_FROM_REG(reg) ((reg) == 0xFFFC || (reg) == 0 ? 0 : \ @@ -109,76 +130,219 @@ static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END }; (val) >= 127000 ? 127 : \ (val) < 0 ? ((val) - 500) / 1000 : \ ((val) + 500) / 1000) +#define TEMP8U_TO_REG(val) ((val) <= 0 ? 0 : \ + (val) >= 255000 ? 255 : \ + ((val) + 500) / 1000) #define TEMP11_FROM_REG(reg) ((reg) / 32 * 125) #define TEMP11_TO_REG(val) ((val) <= -128000 ? 0x8000 : \ (val) >= 127875 ? 0x7FE0 : \ (val) < 0 ? ((val) - 62) / 125 * 32 : \ ((val) + 62) / 125 * 32) +#define TEMP11U_TO_REG(val) ((val) <= 0 ? 0 : \ + (val) >= 255875 ? 0xFFE0 : \ + ((val) + 62) / 125 * 32) #define HYST_TO_REG(val) ((val) <= 0 ? 0 : \ (val) >= 127000 ? 127 : \ ((val) + 500) / 1000) -/* - * Functions declaration - */ - -static int lm63_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int lm63_remove(struct i2c_client *client); - -static struct lm63_data *lm63_update_device(struct device *dev); - -static int lm63_detect(struct i2c_client *client, struct i2c_board_info *info); -static void lm63_init_client(struct i2c_client *client); +#define UPDATE_INTERVAL(max, rate) \ + ((1000 << (LM63_MAX_CONVRATE - (rate))) / (max)) -/* - * Driver data (common to all clients) - */ - -static const struct i2c_device_id lm63_id[] = { - { "lm63", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, lm63_id); - -static struct i2c_driver lm63_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "lm63", - }, - .probe = lm63_probe, - .remove = lm63_remove, - .id_table = lm63_id, - .detect = lm63_detect, - .address_list = normal_i2c, -}; +enum chips { lm63, lm64, lm96163 }; /* * Client data (each client gets its own) */ struct lm63_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; + const struct attribute_group *groups[5]; char valid; /* zero until following fields are valid */ + char lut_valid; /* zero until lut fields are valid */ unsigned long last_updated; /* in jiffies */ + unsigned long lut_last_updated; /* in jiffies */ + enum chips kind; + int temp2_offset; + + int update_interval; /* in milliseconds */ + int max_convrate_hz; + int lut_size; /* 8 or 12 */ /* registers values */ u8 config, config_fan; u16 fan[2]; /* 0: input 1: low limit */ u8 pwm1_freq; - u8 pwm1_value; - s8 temp8[3]; /* 0: local input + u8 pwm1[13]; /* 0: current output + 1-12: lookup table */ + s8 temp8[15]; /* 0: local input 1: local high limit - 2: remote critical limit */ - s16 temp11[3]; /* 0: remote input + 2: remote critical limit + 3-14: lookup table */ + s16 temp11[4]; /* 0: remote input 1: remote low limit - 2: remote high limit */ + 2: remote high limit + 3: remote offset */ + u16 temp11u; /* remote input (unsigned) */ u8 temp2_crit_hyst; + u8 lut_temp_hyst; u8 alarms; + bool pwm_highres; + bool lut_temp_highres; + bool remote_unsigned; /* true if unsigned remote upper limits */ + bool trutherm; }; +static inline int temp8_from_reg(struct lm63_data *data, int nr) +{ + if (data->remote_unsigned) + return TEMP8_FROM_REG((u8)data->temp8[nr]); + return TEMP8_FROM_REG(data->temp8[nr]); +} + +static inline int lut_temp_from_reg(struct lm63_data *data, int nr) +{ + return data->temp8[nr] * (data->lut_temp_highres ? 500 : 1000); +} + +static inline int lut_temp_to_reg(struct lm63_data *data, long val) +{ + val -= data->temp2_offset; + if (data->lut_temp_highres) + return DIV_ROUND_CLOSEST(clamp_val(val, 0, 127500), 500); + else + return DIV_ROUND_CLOSEST(clamp_val(val, 0, 127000), 1000); +} + +/* + * Update the lookup table register cache. + * client->update_lock must be held when calling this function. + */ +static void lm63_update_lut(struct lm63_data *data) +{ + struct i2c_client *client = data->client; + int i; + + if (time_after(jiffies, data->lut_last_updated + 5 * HZ) || + !data->lut_valid) { + for (i = 0; i < data->lut_size; i++) { + data->pwm1[1 + i] = i2c_smbus_read_byte_data(client, + LM63_REG_LUT_PWM(i)); + data->temp8[3 + i] = i2c_smbus_read_byte_data(client, + LM63_REG_LUT_TEMP(i)); + } + data->lut_temp_hyst = i2c_smbus_read_byte_data(client, + LM63_REG_LUT_TEMP_HYST); + + data->lut_last_updated = jiffies; + data->lut_valid = 1; + } +} + +static struct lm63_data *lm63_update_device(struct device *dev) +{ + struct lm63_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long next_update; + + mutex_lock(&data->update_lock); + + next_update = data->last_updated + + msecs_to_jiffies(data->update_interval); + if (time_after(jiffies, next_update) || !data->valid) { + if (data->config & 0x04) { /* tachometer enabled */ + /* order matters for fan1_input */ + data->fan[0] = i2c_smbus_read_byte_data(client, + LM63_REG_TACH_COUNT_LSB) & 0xFC; + data->fan[0] |= i2c_smbus_read_byte_data(client, + LM63_REG_TACH_COUNT_MSB) << 8; + data->fan[1] = (i2c_smbus_read_byte_data(client, + LM63_REG_TACH_LIMIT_LSB) & 0xFC) + | (i2c_smbus_read_byte_data(client, + LM63_REG_TACH_LIMIT_MSB) << 8); + } + + data->pwm1_freq = i2c_smbus_read_byte_data(client, + LM63_REG_PWM_FREQ); + if (data->pwm1_freq == 0) + data->pwm1_freq = 1; + data->pwm1[0] = i2c_smbus_read_byte_data(client, + LM63_REG_PWM_VALUE); + + data->temp8[0] = i2c_smbus_read_byte_data(client, + LM63_REG_LOCAL_TEMP); + data->temp8[1] = i2c_smbus_read_byte_data(client, + LM63_REG_LOCAL_HIGH); + + /* order matters for temp2_input */ + data->temp11[0] = i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TEMP_MSB) << 8; + data->temp11[0] |= i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TEMP_LSB); + data->temp11[1] = (i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_LOW_MSB) << 8) + | i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_LOW_LSB); + data->temp11[2] = (i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_HIGH_MSB) << 8) + | i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_HIGH_LSB); + data->temp11[3] = (i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_OFFSET_MSB) << 8) + | i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_OFFSET_LSB); + + if (data->kind == lm96163) + data->temp11u = (i2c_smbus_read_byte_data(client, + LM96163_REG_REMOTE_TEMP_U_MSB) << 8) + | i2c_smbus_read_byte_data(client, + LM96163_REG_REMOTE_TEMP_U_LSB); + + data->temp8[2] = i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TCRIT); + data->temp2_crit_hyst = i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TCRIT_HYST); + + data->alarms = i2c_smbus_read_byte_data(client, + LM63_REG_ALERT_STATUS) & 0x7F; + + data->last_updated = jiffies; + data->valid = 1; + } + + lm63_update_lut(data); + + mutex_unlock(&data->update_lock); + + return data; +} + +/* + * Trip points in the lookup table should be in ascending order for both + * temperatures and PWM output values. + */ +static int lm63_lut_looks_bad(struct device *dev, struct lm63_data *data) +{ + int i; + + mutex_lock(&data->update_lock); + lm63_update_lut(data); + + for (i = 1; i < data->lut_size; i++) { + if (data->pwm1[1 + i - 1] > data->pwm1[1 + i] + || data->temp8[3 + i - 1] > data->temp8[3 + i]) { + dev_warn(dev, + "Lookup table doesn't look sane (check entries %d and %d)\n", + i, i + 1); + break; + } + } + mutex_unlock(&data->update_lock); + + return i == data->lut_size ? 0 : 1; +} + /* * Sysfs callback functions and files */ @@ -194,9 +358,14 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, static ssize_t set_fan(struct device *dev, struct device_attribute *dummy, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm63_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); + struct lm63_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->fan[1] = FAN_TO_REG(val); @@ -208,60 +377,165 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *dummy, return count; } -static ssize_t show_pwm1(struct device *dev, struct device_attribute *dummy, +static ssize_t show_pwm1(struct device *dev, struct device_attribute *devattr, char *buf) { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm63_data *data = lm63_update_device(dev); - return sprintf(buf, "%d\n", data->pwm1_value >= 2 * data->pwm1_freq ? - 255 : (data->pwm1_value * 255 + data->pwm1_freq) / - (2 * data->pwm1_freq)); + int nr = attr->index; + int pwm; + + if (data->pwm_highres) + pwm = data->pwm1[nr]; + else + pwm = data->pwm1[nr] >= 2 * data->pwm1_freq ? + 255 : (data->pwm1[nr] * 255 + data->pwm1_freq) / + (2 * data->pwm1_freq); + + return sprintf(buf, "%d\n", pwm); } -static ssize_t set_pwm1(struct device *dev, struct device_attribute *dummy, +static ssize_t set_pwm1(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm63_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm63_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int nr = attr->index; unsigned long val; - + int err; + u8 reg; + if (!(data->config_fan & 0x20)) /* register is read-only */ return -EPERM; - val = simple_strtoul(buf, NULL, 10); + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + reg = nr ? LM63_REG_LUT_PWM(nr - 1) : LM63_REG_PWM_VALUE; + val = clamp_val(val, 0, 255); + mutex_lock(&data->update_lock); - data->pwm1_value = val <= 0 ? 0 : - val >= 255 ? 2 * data->pwm1_freq : - (val * data->pwm1_freq * 2 + 127) / 255; - i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1_value); + data->pwm1[nr] = data->pwm_highres ? val : + (val * data->pwm1_freq * 2 + 127) / 255; + i2c_smbus_write_byte_data(client, reg, data->pwm1[nr]); mutex_unlock(&data->update_lock); return count; } -static ssize_t show_pwm1_enable(struct device *dev, struct device_attribute *dummy, - char *buf) +static ssize_t show_pwm1_enable(struct device *dev, + struct device_attribute *dummy, char *buf) { struct lm63_data *data = lm63_update_device(dev); return sprintf(buf, "%d\n", data->config_fan & 0x20 ? 1 : 2); } -static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, - char *buf) +static ssize_t set_pwm1_enable(struct device *dev, + struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct lm63_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + if (val < 1 || val > 2) + return -EINVAL; + + /* + * Only let the user switch to automatic mode if the lookup table + * looks sane. + */ + if (val == 2 && lm63_lut_looks_bad(dev, data)) + return -EPERM; + + mutex_lock(&data->update_lock); + data->config_fan = i2c_smbus_read_byte_data(client, + LM63_REG_CONFIG_FAN); + if (val == 1) + data->config_fan |= 0x20; + else + data->config_fan &= ~0x20; + i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN, + data->config_fan); + mutex_unlock(&data->update_lock); + return count; +} + +/* + * There are 8bit registers for both local(temp1) and remote(temp2) sensor. + * For remote sensor registers temp2_offset has to be considered, + * for local sensor it must not. + * So we need separate 8bit accessors for local and remote sensor. + */ +static ssize_t show_local_temp8(struct device *dev, + struct device_attribute *devattr, + char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm63_data *data = lm63_update_device(dev); return sprintf(buf, "%d\n", TEMP8_FROM_REG(data->temp8[attr->index])); } -static ssize_t set_temp8(struct device *dev, struct device_attribute *dummy, +static ssize_t show_remote_temp8(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm63_data *data = lm63_update_device(dev); + return sprintf(buf, "%d\n", temp8_from_reg(data, attr->index) + + data->temp2_offset); +} + +static ssize_t show_lut_temp(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm63_data *data = lm63_update_device(dev); + return sprintf(buf, "%d\n", lut_temp_from_reg(data, attr->index) + + data->temp2_offset); +} + +static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm63_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm63_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int nr = attr->index; + long val; + int err; + int temp; + u8 reg; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - data->temp8[1] = TEMP8_TO_REG(val); - i2c_smbus_write_byte_data(client, LM63_REG_LOCAL_HIGH, data->temp8[1]); + switch (nr) { + case 2: + reg = LM63_REG_REMOTE_TCRIT; + if (data->remote_unsigned) + temp = TEMP8U_TO_REG(val - data->temp2_offset); + else + temp = TEMP8_TO_REG(val - data->temp2_offset); + break; + case 1: + reg = LM63_REG_LOCAL_HIGH; + temp = TEMP8_TO_REG(val); + break; + default: /* lookup table */ + reg = LM63_REG_LUT_TEMP(nr - 3); + temp = lut_temp_to_reg(data, val); + } + data->temp8[nr] = temp; + i2c_smbus_write_byte_data(client, reg, temp); mutex_unlock(&data->update_lock); return count; } @@ -271,27 +545,56 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm63_data *data = lm63_update_device(dev); - return sprintf(buf, "%d\n", TEMP11_FROM_REG(data->temp11[attr->index])); + int nr = attr->index; + int temp; + + if (!nr) { + /* + * Use unsigned temperature unless its value is zero. + * If it is zero, use signed temperature. + */ + if (data->temp11u) + temp = TEMP11_FROM_REG(data->temp11u); + else + temp = TEMP11_FROM_REG(data->temp11[nr]); + } else { + if (data->remote_unsigned && nr == 2) + temp = TEMP11_FROM_REG((u16)data->temp11[nr]); + else + temp = TEMP11_FROM_REG(data->temp11[nr]); + } + return sprintf(buf, "%d\n", temp + data->temp2_offset); } static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - static const u8 reg[4] = { + static const u8 reg[6] = { LM63_REG_REMOTE_LOW_MSB, LM63_REG_REMOTE_LOW_LSB, LM63_REG_REMOTE_HIGH_MSB, LM63_REG_REMOTE_HIGH_LSB, + LM63_REG_REMOTE_OFFSET_MSB, + LM63_REG_REMOTE_OFFSET_LSB, }; struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct lm63_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + struct lm63_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + long val; + int err; int nr = attr->index; + err = kstrtol(buf, 10, &val); + if (err) + return err; + mutex_lock(&data->update_lock); - data->temp11[nr] = TEMP11_TO_REG(val); + if (data->remote_unsigned && nr == 2) + data->temp11[nr] = TEMP11U_TO_REG(val - data->temp2_offset); + else + data->temp11[nr] = TEMP11_TO_REG(val - data->temp2_offset); + i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], data->temp11[nr] >> 8); i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1], @@ -300,34 +603,141 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, return count; } -/* Hysteresis register holds a relative value, while we want to present - an absolute to user-space */ -static ssize_t show_temp2_crit_hyst(struct device *dev, struct device_attribute *dummy, - char *buf) +/* + * Hysteresis register holds a relative value, while we want to present + * an absolute to user-space + */ +static ssize_t show_temp2_crit_hyst(struct device *dev, + struct device_attribute *dummy, char *buf) { struct lm63_data *data = lm63_update_device(dev); - return sprintf(buf, "%d\n", TEMP8_FROM_REG(data->temp8[2]) + return sprintf(buf, "%d\n", temp8_from_reg(data, 2) + + data->temp2_offset - TEMP8_FROM_REG(data->temp2_crit_hyst)); } -/* And now the other way around, user-space provides an absolute - hysteresis value and we have to store a relative one */ -static ssize_t set_temp2_crit_hyst(struct device *dev, struct device_attribute *dummy, +static ssize_t show_lut_temp_hyst(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm63_data *data = lm63_update_device(dev); + + return sprintf(buf, "%d\n", lut_temp_from_reg(data, attr->index) + + data->temp2_offset + - TEMP8_FROM_REG(data->lut_temp_hyst)); +} + +/* + * And now the other way around, user-space provides an absolute + * hysteresis value and we have to store a relative one + */ +static ssize_t set_temp2_crit_hyst(struct device *dev, + struct device_attribute *dummy, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm63_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + struct lm63_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + long val; + int err; long hyst; + err = kstrtol(buf, 10, &val); + if (err) + return err; + mutex_lock(&data->update_lock); - hyst = TEMP8_FROM_REG(data->temp8[2]) - val; + hyst = temp8_from_reg(data, 2) + data->temp2_offset - val; i2c_smbus_write_byte_data(client, LM63_REG_REMOTE_TCRIT_HYST, HYST_TO_REG(hyst)); mutex_unlock(&data->update_lock); return count; } +/* + * Set conversion rate. + * client->update_lock must be held when calling this function. + */ +static void lm63_set_convrate(struct lm63_data *data, unsigned int interval) +{ + struct i2c_client *client = data->client; + unsigned int update_interval; + int i; + + /* Shift calculations to avoid rounding errors */ + interval <<= 6; + + /* find the nearest update rate */ + update_interval = (1 << (LM63_MAX_CONVRATE + 6)) * 1000 + / data->max_convrate_hz; + for (i = 0; i < LM63_MAX_CONVRATE; i++, update_interval >>= 1) + if (interval >= update_interval * 3 / 4) + break; + + i2c_smbus_write_byte_data(client, LM63_REG_CONVRATE, i); + data->update_interval = UPDATE_INTERVAL(data->max_convrate_hz, i); +} + +static ssize_t show_update_interval(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lm63_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 lm63_data *data = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + lm63_set_convrate(data, clamp_val(val, 0, 100000)); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_type(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm63_data *data = dev_get_drvdata(dev); + + return sprintf(buf, data->trutherm ? "1\n" : "2\n"); +} + +static ssize_t set_type(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lm63_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + int ret; + u8 reg; + + ret = kstrtoul(buf, 10, &val); + if (ret < 0) + return ret; + if (val != 1 && val != 2) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->trutherm = val == 1; + reg = i2c_smbus_read_byte_data(client, LM96163_REG_TRUTHERM) & ~0x02; + i2c_smbus_write_byte_data(client, LM96163_REG_TRUTHERM, + reg | (data->trutherm ? 0x02 : 0x00)); + data->valid = 0; + mutex_unlock(&data->update_lock); + + return count; +} + static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy, char *buf) { @@ -349,11 +759,84 @@ static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan, set_fan, 1); -static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1); -static DEVICE_ATTR(pwm1_enable, S_IRUGO, show_pwm1_enable, NULL); - -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp8, NULL, 0); -static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8, +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0); +static DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + show_pwm1_enable, set_pwm1_enable); +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 1); +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 3); +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 3); +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 2); +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 4); +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 4); +static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 3); +static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 5); +static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 5); +static SENSOR_DEVICE_ATTR(pwm1_auto_point4_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 4); +static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 6); +static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 6); +static SENSOR_DEVICE_ATTR(pwm1_auto_point5_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 5); +static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 7); +static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 7); +static SENSOR_DEVICE_ATTR(pwm1_auto_point6_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 6); +static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 8); +static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 8); +static SENSOR_DEVICE_ATTR(pwm1_auto_point7_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 7); +static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 9); +static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 9); +static SENSOR_DEVICE_ATTR(pwm1_auto_point8_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 8); +static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 10); +static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 10); +static SENSOR_DEVICE_ATTR(pwm1_auto_point9_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 9); +static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 11); +static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 11); +static SENSOR_DEVICE_ATTR(pwm1_auto_point10_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 10); +static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 12); +static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 12); +static SENSOR_DEVICE_ATTR(pwm1_auto_point11_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 11); +static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 13); +static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 13); +static SENSOR_DEVICE_ATTR(pwm1_auto_point12_pwm, S_IWUSR | S_IRUGO, + show_pwm1, set_pwm1, 12); +static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp, S_IWUSR | S_IRUGO, + show_lut_temp, set_temp8, 14); +static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 14); + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_local_temp8, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_local_temp8, set_temp8, 1); static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0); @@ -361,10 +844,15 @@ static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11, set_temp11, 1); static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11, set_temp11, 2); -static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp8, NULL, 2); +static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 3); +static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_remote_temp8, + set_temp8, 2); static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp2_crit_hyst, set_temp2_crit_hyst); +static DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, set_type); + /* Individual alarm files */ static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, show_alarm, NULL, 0); static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1); @@ -375,14 +863,43 @@ 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 *lm63_attributes[] = { - &dev_attr_pwm1.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, &dev_attr_pwm1_enable.attr, + &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point5_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point6_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point6_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point6_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point7_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point7_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point7_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point8_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point8_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point8_temp_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp2_min.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_offset.dev_attr.attr, &sensor_dev_attr_temp2_crit.dev_attr.attr, &dev_attr_temp2_crit_hyst.attr, @@ -392,10 +909,62 @@ static struct attribute *lm63_attributes[] = { &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, &dev_attr_alarms.attr, + &dev_attr_update_interval.attr, + NULL +}; + +static struct attribute *lm63_attributes_temp2_type[] = { + &dev_attr_temp2_type.attr, + NULL +}; + +static const struct attribute_group lm63_group_temp2_type = { + .attrs = lm63_attributes_temp2_type, +}; + +static struct attribute *lm63_attributes_extra_lut[] = { + &sensor_dev_attr_pwm1_auto_point9_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point9_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point9_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point10_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point10_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point10_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point11_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point11_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point11_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point12_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point12_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point12_temp_hyst.dev_attr.attr, NULL }; +static const struct attribute_group lm63_group_extra_lut = { + .attrs = lm63_attributes_extra_lut, +}; + +/* + * On LM63, temp2_crit can be set only once, which should be job + * of the bootloader. + * On LM64, temp2_crit can always be set. + * On LM96163, temp2_crit can be set if bit 1 of the configuration + * register is true. + */ +static umode_t lm63_attribute_mode(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct lm63_data *data = dev_get_drvdata(dev); + + if (attr == &sensor_dev_attr_temp2_crit.dev_attr.attr + && (data->kind == lm64 || + (data->kind == lm96163 && (data->config & 0x02)))) + return attr->mode | S_IWUSR; + + return attr->mode; +} + static const struct attribute_group lm63_group = { + .is_visible = lm63_attribute_mode, .attrs = lm63_attributes, }; @@ -416,30 +985,27 @@ static const struct attribute_group lm63_group_fan1 = { */ /* Return 0 if detection is successful, -ENODEV otherwise */ -static int lm63_detect(struct i2c_client *new_client, +static int lm63_detect(struct i2c_client *client, struct i2c_board_info *info) { - struct i2c_adapter *adapter = new_client->adapter; + struct i2c_adapter *adapter = client->adapter; u8 man_id, chip_id, reg_config1, reg_config2; u8 reg_alert_status, reg_alert_mask; + int address = client->addr; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - man_id = i2c_smbus_read_byte_data(new_client, LM63_REG_MAN_ID); - chip_id = i2c_smbus_read_byte_data(new_client, LM63_REG_CHIP_ID); + man_id = i2c_smbus_read_byte_data(client, LM63_REG_MAN_ID); + chip_id = i2c_smbus_read_byte_data(client, LM63_REG_CHIP_ID); - reg_config1 = i2c_smbus_read_byte_data(new_client, - LM63_REG_CONFIG1); - reg_config2 = i2c_smbus_read_byte_data(new_client, - LM63_REG_CONFIG2); - reg_alert_status = i2c_smbus_read_byte_data(new_client, + reg_config1 = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1); + reg_config2 = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG2); + reg_alert_status = i2c_smbus_read_byte_data(client, LM63_REG_ALERT_STATUS); - reg_alert_mask = i2c_smbus_read_byte_data(new_client, - LM63_REG_ALERT_MASK); + reg_alert_mask = i2c_smbus_read_byte_data(client, LM63_REG_ALERT_MASK); if (man_id != 0x01 /* National Semiconductor */ - || chip_id != 0x41 /* LM63 */ || (reg_config1 & 0x18) != 0x00 || (reg_config2 & 0xF8) != 0x00 || (reg_alert_status & 0x20) != 0x00 @@ -450,62 +1016,27 @@ static int lm63_detect(struct i2c_client *new_client, return -ENODEV; } - strlcpy(info->type, "lm63", I2C_NAME_SIZE); - - return 0; -} - -static int lm63_probe(struct i2c_client *new_client, - const struct i2c_device_id *id) -{ - struct lm63_data *data; - int err; - - data = kzalloc(sizeof(struct lm63_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } - - i2c_set_clientdata(new_client, data); - data->valid = 0; - mutex_init(&data->update_lock); - - /* Initialize the LM63 chip */ - lm63_init_client(new_client); - - /* Register sysfs hooks */ - if ((err = sysfs_create_group(&new_client->dev.kobj, - &lm63_group))) - goto exit_free; - if (data->config & 0x04) { /* tachometer enabled */ - if ((err = sysfs_create_group(&new_client->dev.kobj, - &lm63_group_fan1))) - goto exit_remove_files; - } - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } + if (chip_id == 0x41 && address == 0x4c) + strlcpy(info->type, "lm63", I2C_NAME_SIZE); + else if (chip_id == 0x51 && (address == 0x18 || address == 0x4e)) + strlcpy(info->type, "lm64", I2C_NAME_SIZE); + else if (chip_id == 0x49 && address == 0x4c) + strlcpy(info->type, "lm96163", I2C_NAME_SIZE); + else + return -ENODEV; return 0; - -exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &lm63_group); - sysfs_remove_group(&new_client->dev.kobj, &lm63_group_fan1); -exit_free: - kfree(data); -exit: - return err; } -/* Idealy we shouldn't have to initialize anything, since the BIOS - should have taken care of everything */ -static void lm63_init_client(struct i2c_client *client) +/* + * Ideally we shouldn't have to initialize anything, since the BIOS + * should have taken care of everything + */ +static void lm63_init_client(struct lm63_data *data) { - struct lm63_data *data = i2c_get_clientdata(client); + struct i2c_client *client = data->client; + struct device *dev = &client->dev; + u8 convrate; data->config = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1); data->config_fan = i2c_smbus_read_byte_data(client, @@ -513,116 +1044,133 @@ static void lm63_init_client(struct i2c_client *client) /* Start converting if needed */ if (data->config & 0x40) { /* standby */ - dev_dbg(&client->dev, "Switching to operational mode\n"); + dev_dbg(dev, "Switching to operational mode\n"); data->config &= 0xA7; i2c_smbus_write_byte_data(client, LM63_REG_CONFIG1, data->config); } + /* Tachometer is always enabled on LM64 */ + if (data->kind == lm64) + data->config |= 0x04; /* We may need pwm1_freq before ever updating the client data */ data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ); if (data->pwm1_freq == 0) data->pwm1_freq = 1; + switch (data->kind) { + case lm63: + case lm64: + data->max_convrate_hz = LM63_MAX_CONVRATE_HZ; + data->lut_size = 8; + break; + case lm96163: + data->max_convrate_hz = LM96163_MAX_CONVRATE_HZ; + data->lut_size = 12; + data->trutherm + = i2c_smbus_read_byte_data(client, + LM96163_REG_TRUTHERM) & 0x02; + break; + } + convrate = i2c_smbus_read_byte_data(client, LM63_REG_CONVRATE); + if (unlikely(convrate > LM63_MAX_CONVRATE)) + convrate = LM63_MAX_CONVRATE; + data->update_interval = UPDATE_INTERVAL(data->max_convrate_hz, + convrate); + + /* + * For LM96163, check if high resolution PWM + * and unsigned temperature format is enabled. + */ + if (data->kind == lm96163) { + u8 config_enhanced + = i2c_smbus_read_byte_data(client, + LM96163_REG_CONFIG_ENHANCED); + if (config_enhanced & 0x20) + data->lut_temp_highres = true; + if ((config_enhanced & 0x10) + && !(data->config_fan & 0x08) && data->pwm1_freq == 8) + data->pwm_highres = true; + if (config_enhanced & 0x08) + data->remote_unsigned = true; + } + /* Show some debug info about the LM63 configuration */ - dev_dbg(&client->dev, "Alert/tach pin configured for %s\n", - (data->config & 0x04) ? "tachometer input" : - "alert output"); - dev_dbg(&client->dev, "PWM clock %s kHz, output frequency %u Hz\n", + if (data->kind == lm63) + dev_dbg(dev, "Alert/tach pin configured for %s\n", + (data->config & 0x04) ? "tachometer input" : + "alert output"); + dev_dbg(dev, "PWM clock %s kHz, output frequency %u Hz\n", (data->config_fan & 0x08) ? "1.4" : "360", ((data->config_fan & 0x08) ? 700 : 180000) / data->pwm1_freq); - dev_dbg(&client->dev, "PWM output active %s, %s mode\n", + dev_dbg(dev, "PWM output active %s, %s mode\n", (data->config_fan & 0x10) ? "low" : "high", (data->config_fan & 0x20) ? "manual" : "auto"); } -static int lm63_remove(struct i2c_client *client) -{ - struct lm63_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &lm63_group); - sysfs_remove_group(&client->dev.kobj, &lm63_group_fan1); - - kfree(data); - return 0; -} - -static struct lm63_data *lm63_update_device(struct device *dev) +static int lm63_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - struct i2c_client *client = to_i2c_client(dev); - struct lm63_data *data = i2c_get_clientdata(client); + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct lm63_data *data; + int groups = 0; - mutex_lock(&data->update_lock); + data = devm_kzalloc(dev, sizeof(struct lm63_data), GFP_KERNEL); + if (!data) + return -ENOMEM; - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - if (data->config & 0x04) { /* tachometer enabled */ - /* order matters for fan1_input */ - data->fan[0] = i2c_smbus_read_byte_data(client, - LM63_REG_TACH_COUNT_LSB) & 0xFC; - data->fan[0] |= i2c_smbus_read_byte_data(client, - LM63_REG_TACH_COUNT_MSB) << 8; - data->fan[1] = (i2c_smbus_read_byte_data(client, - LM63_REG_TACH_LIMIT_LSB) & 0xFC) - | (i2c_smbus_read_byte_data(client, - LM63_REG_TACH_LIMIT_MSB) << 8); - } + data->client = client; + mutex_init(&data->update_lock); - data->pwm1_freq = i2c_smbus_read_byte_data(client, - LM63_REG_PWM_FREQ); - if (data->pwm1_freq == 0) - data->pwm1_freq = 1; - data->pwm1_value = i2c_smbus_read_byte_data(client, - LM63_REG_PWM_VALUE); + /* Set the device type */ + data->kind = id->driver_data; + if (data->kind == lm64) + data->temp2_offset = 16000; - data->temp8[0] = i2c_smbus_read_byte_data(client, - LM63_REG_LOCAL_TEMP); - data->temp8[1] = i2c_smbus_read_byte_data(client, - LM63_REG_LOCAL_HIGH); + /* Initialize chip */ + lm63_init_client(data); - /* order matters for temp2_input */ - data->temp11[0] = i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TEMP_MSB) << 8; - data->temp11[0] |= i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TEMP_LSB); - data->temp11[1] = (i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_LOW_MSB) << 8) - | i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_LOW_LSB); - data->temp11[2] = (i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_HIGH_MSB) << 8) - | i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_HIGH_LSB); - data->temp8[2] = i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TCRIT); - data->temp2_crit_hyst = i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TCRIT_HYST); - - data->alarms = i2c_smbus_read_byte_data(client, - LM63_REG_ALERT_STATUS) & 0x7F; + /* Register sysfs hooks */ + data->groups[groups++] = &lm63_group; + if (data->config & 0x04) /* tachometer enabled */ + data->groups[groups++] = &lm63_group_fan1; - data->last_updated = jiffies; - data->valid = 1; + if (data->kind == lm96163) { + data->groups[groups++] = &lm63_group_temp2_type; + data->groups[groups++] = &lm63_group_extra_lut; } - mutex_unlock(&data->update_lock); - - return data; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, data->groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } -static int __init sensors_lm63_init(void) -{ - return i2c_add_driver(&lm63_driver); -} +/* + * Driver data (common to all clients) + */ -static void __exit sensors_lm63_exit(void) -{ - i2c_del_driver(&lm63_driver); -} +static const struct i2c_device_id lm63_id[] = { + { "lm63", lm63 }, + { "lm64", lm64 }, + { "lm96163", lm96163 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm63_id); -MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); +static struct i2c_driver lm63_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "lm63", + }, + .probe = lm63_probe, + .id_table = lm63_id, + .detect = lm63_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(lm63_driver); + +MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); MODULE_DESCRIPTION("LM63 driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_lm63_init); -module_exit(sensors_lm63_exit); diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c index ab8a5d3c769..97204dce162 100644 --- a/drivers/hwmon/lm70.c +++ b/drivers/hwmon/lm70.c @@ -24,6 +24,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> @@ -34,15 +36,18 @@ #include <linux/mutex.h> #include <linux/mod_devicetable.h> #include <linux/spi/spi.h> +#include <linux/slab.h> #define DRVNAME "lm70" #define LM70_CHIP_LM70 0 /* original NS LM70 */ #define LM70_CHIP_TMP121 1 /* TI TMP121/TMP123 */ +#define LM70_CHIP_LM71 2 /* NS LM71 */ +#define LM70_CHIP_LM74 3 /* NS LM74 */ struct lm70 { - struct device *hwmon_dev; + struct spi_device *spi; struct mutex lock; unsigned int chip; }; @@ -51,11 +56,11 @@ struct lm70 { static ssize_t lm70_sense_temp(struct device *dev, struct device_attribute *attr, char *buf) { - struct spi_device *spi = to_spi_device(dev); + struct lm70 *p_lm70 = dev_get_drvdata(dev); + struct spi_device *spi = p_lm70->spi; int status, val = 0; u8 rxbuf[2]; - s16 raw=0; - struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev); + s16 raw = 0; if (mutex_lock_interruptible(&p_lm70->lock)) return -ERESTARTSYS; @@ -66,8 +71,7 @@ static ssize_t lm70_sense_temp(struct device *dev, */ status = spi_write_then_read(spi, NULL, 0, &rxbuf[0], 2); if (status < 0) { - printk(KERN_WARNING - "spi_write_then_read failed with status %d\n", status); + pr_warn("spi_write_then_read failed with status %d\n", status); goto out; } raw = (rxbuf[0] << 8) + rxbuf[1]; @@ -86,9 +90,13 @@ static ssize_t lm70_sense_temp(struct device *dev, * Celsius. * So it's equivalent to multiplying by 0.25 * 1000 = 250. * - * TMP121/TMP123: + * LM74 and TMP121/TMP123: * 13 bits of 2's complement data, discard LSB 3 bits, * resolution 0.0625 degrees celsius. + * + * LM71: + * 14 bits of 2's complement data, discard LSB 2 bits, + * resolution 0.0312 degrees celsius. */ switch (p_lm70->chip) { case LM70_CHIP_LM70: @@ -96,8 +104,13 @@ static ssize_t lm70_sense_temp(struct device *dev, break; case LM70_CHIP_TMP121: + case LM70_CHIP_LM74: val = ((int)raw / 8) * 625 / 10; break; + + case LM70_CHIP_LM71: + val = ((int)raw / 4) * 3125 / 100; + break; } status = sprintf(buf, "%d\n", val); /* millidegrees Celsius */ @@ -108,95 +121,46 @@ out: static DEVICE_ATTR(temp1_input, S_IRUGO, lm70_sense_temp, NULL); -static ssize_t lm70_show_name(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct lm70 *p_lm70 = dev_get_drvdata(dev); - int ret; - - switch (p_lm70->chip) { - case LM70_CHIP_LM70: - ret = sprintf(buf, "lm70\n"); - break; - case LM70_CHIP_TMP121: - ret = sprintf(buf, "tmp121\n"); - break; - default: - ret = -EINVAL; - } - return ret; -} +static struct attribute *lm70_attrs[] = { + &dev_attr_temp1_input.attr, + NULL +}; -static DEVICE_ATTR(name, S_IRUGO, lm70_show_name, NULL); +ATTRIBUTE_GROUPS(lm70); /*----------------------------------------------------------------------*/ -static int __devinit lm70_probe(struct spi_device *spi) +static int lm70_probe(struct spi_device *spi) { int chip = spi_get_device_id(spi)->driver_data; + struct device *hwmon_dev; struct lm70 *p_lm70; - int status; - /* signaling is SPI_MODE_0 for both LM70 and TMP121 */ + /* signaling is SPI_MODE_0 */ if (spi->mode & (SPI_CPOL | SPI_CPHA)) return -EINVAL; - /* 3-wire link (shared SI/SO) for LM70 */ - if (chip == LM70_CHIP_LM70 && !(spi->mode & SPI_3WIRE)) - return -EINVAL; - /* NOTE: we assume 8-bit words, and convert to 16 bits manually */ - p_lm70 = kzalloc(sizeof *p_lm70, GFP_KERNEL); + p_lm70 = devm_kzalloc(&spi->dev, sizeof(*p_lm70), GFP_KERNEL); if (!p_lm70) return -ENOMEM; mutex_init(&p_lm70->lock); p_lm70->chip = chip; + p_lm70->spi = spi; - /* sysfs hook */ - p_lm70->hwmon_dev = hwmon_device_register(&spi->dev); - if (IS_ERR(p_lm70->hwmon_dev)) { - dev_dbg(&spi->dev, "hwmon_device_register failed.\n"); - status = PTR_ERR(p_lm70->hwmon_dev); - goto out_dev_reg_failed; - } - dev_set_drvdata(&spi->dev, p_lm70); - - if ((status = device_create_file(&spi->dev, &dev_attr_temp1_input)) - || (status = device_create_file(&spi->dev, &dev_attr_name))) { - dev_dbg(&spi->dev, "device_create_file failure.\n"); - goto out_dev_create_file_failed; - } - - return 0; - -out_dev_create_file_failed: - device_remove_file(&spi->dev, &dev_attr_temp1_input); - hwmon_device_unregister(p_lm70->hwmon_dev); -out_dev_reg_failed: - dev_set_drvdata(&spi->dev, NULL); - kfree(p_lm70); - return status; + hwmon_dev = devm_hwmon_device_register_with_groups(&spi->dev, + spi->modalias, + p_lm70, lm70_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } -static int __devexit lm70_remove(struct spi_device *spi) -{ - struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev); - - device_remove_file(&spi->dev, &dev_attr_temp1_input); - device_remove_file(&spi->dev, &dev_attr_name); - hwmon_device_unregister(p_lm70->hwmon_dev); - dev_set_drvdata(&spi->dev, NULL); - kfree(p_lm70); - - return 0; -} - - static const struct spi_device_id lm70_ids[] = { { "lm70", LM70_CHIP_LM70 }, { "tmp121", LM70_CHIP_TMP121 }, + { "lm71", LM70_CHIP_LM71 }, + { "lm74", LM70_CHIP_LM74 }, { }, }; MODULE_DEVICE_TABLE(spi, lm70_ids); @@ -208,22 +172,10 @@ static struct spi_driver lm70_driver = { }, .id_table = lm70_ids, .probe = lm70_probe, - .remove = __devexit_p(lm70_remove), }; -static int __init init_lm70(void) -{ - return spi_register_driver(&lm70_driver); -} - -static void __exit cleanup_lm70(void) -{ - spi_unregister_driver(&lm70_driver); -} - -module_init(init_lm70); -module_exit(cleanup_lm70); +module_spi_driver(lm70_driver); MODULE_AUTHOR("Kaiwan N Billimoria"); -MODULE_DESCRIPTION("NS LM70 / TI TMP121/TMP123 Linux driver"); +MODULE_DESCRIPTION("NS LM70 and compatibles Linux driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c index c5f39ba103c..9653bb870a4 100644 --- a/drivers/hwmon/lm73.c +++ b/drivers/hwmon/lm73.c @@ -8,6 +8,7 @@ * Guillaume Ligneul <guillaume.ligneul@gmail.com> * Adrien Demarez <adrien.demarez@bolloretelecom.eu> * Jeremy Laine <jeremy.laine@bolloretelecom.eu> + * Chris Verges <kg4ysn@gmail.com> * * This software program is licensed subject to the GNU General Public License * (GPL).Version 2,June 1991, available at @@ -16,7 +17,6 @@ #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> @@ -35,45 +35,134 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4c, #define LM73_REG_CTRL 0x04 #define LM73_REG_ID 0x07 -#define LM73_ID 0x9001 /* or 0x190 after a swab16() */ +#define LM73_ID 0x9001 /* 0x0190, byte-swapped */ #define DRVNAME "lm73" -#define LM73_TEMP_MIN (-40) -#define LM73_TEMP_MAX 150 +#define LM73_TEMP_MIN (-256000 / 250) +#define LM73_TEMP_MAX (255750 / 250) -/*-----------------------------------------------------------------------*/ +#define LM73_CTRL_RES_SHIFT 5 +#define LM73_CTRL_RES_MASK (BIT(5) | BIT(6)) +#define LM73_CTRL_TO_MASK BIT(7) + +#define LM73_CTRL_HI_SHIFT 2 +#define LM73_CTRL_LO_SHIFT 1 + +static const unsigned short lm73_convrates[] = { + 14, /* 11-bits (0.25000 C/LSB): RES1 Bit = 0, RES0 Bit = 0 */ + 28, /* 12-bits (0.12500 C/LSB): RES1 Bit = 0, RES0 Bit = 1 */ + 56, /* 13-bits (0.06250 C/LSB): RES1 Bit = 1, RES0 Bit = 0 */ + 112, /* 14-bits (0.03125 C/LSB): RES1 Bit = 1, RES0 Bit = 1 */ +}; +struct lm73_data { + struct i2c_client *client; + struct mutex lock; + u8 ctrl; /* control register value */ +}; + +/*-----------------------------------------------------------------------*/ static ssize_t set_temp(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct i2c_client *client = to_i2c_client(dev); + struct lm73_data *data = dev_get_drvdata(dev); long temp; short value; + s32 err; - int status = strict_strtol(buf, 10, &temp); + int status = kstrtol(buf, 10, &temp); if (status < 0) return status; /* Write value */ - value = (short) SENSORS_LIMIT(temp/250, (LM73_TEMP_MIN*4), - (LM73_TEMP_MAX*4)) << 5; - i2c_smbus_write_word_data(client, attr->index, swab16(value)); - return count; + value = clamp_val(temp / 250, LM73_TEMP_MIN, LM73_TEMP_MAX) << 5; + err = i2c_smbus_write_word_swapped(data->client, attr->index, value); + return (err < 0) ? err : count; } static ssize_t show_temp(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 lm73_data *data = dev_get_drvdata(dev); + int temp; + + s32 err = i2c_smbus_read_word_swapped(data->client, attr->index); + if (err < 0) + return err; + /* use integer division instead of equivalent right shift to guarantee arithmetic shift and preserve the sign */ - int temp = ((s16) (swab16(i2c_smbus_read_word_data(client, - attr->index)))*250) / 32; - return sprintf(buf, "%d\n", temp); + temp = (((s16) err) * 250) / 32; + return scnprintf(buf, PAGE_SIZE, "%d\n", temp); } +static ssize_t set_convrate(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct lm73_data *data = dev_get_drvdata(dev); + unsigned long convrate; + s32 err; + int res = 0; + + err = kstrtoul(buf, 10, &convrate); + if (err < 0) + return err; + + /* + * Convert the desired conversion rate into register bits. + * res is already initialized, and everything past the second-to-last + * value in the array is treated as belonging to the last value + * in the array. + */ + while (res < (ARRAY_SIZE(lm73_convrates) - 1) && + convrate > lm73_convrates[res]) + res++; + + mutex_lock(&data->lock); + data->ctrl &= LM73_CTRL_TO_MASK; + data->ctrl |= res << LM73_CTRL_RES_SHIFT; + err = i2c_smbus_write_byte_data(data->client, LM73_REG_CTRL, + data->ctrl); + mutex_unlock(&data->lock); + + if (err < 0) + return err; + + return count; +} + +static ssize_t show_convrate(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct lm73_data *data = dev_get_drvdata(dev); + int res; + + res = (data->ctrl & LM73_CTRL_RES_MASK) >> LM73_CTRL_RES_SHIFT; + return scnprintf(buf, PAGE_SIZE, "%hu\n", lm73_convrates[res]); +} + +static ssize_t show_maxmin_alarm(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct lm73_data *data = dev_get_drvdata(dev); + s32 ctrl; + + mutex_lock(&data->lock); + ctrl = i2c_smbus_read_byte_data(data->client, LM73_REG_CTRL); + if (ctrl < 0) + goto abort; + data->ctrl = ctrl; + mutex_unlock(&data->lock); + + return scnprintf(buf, PAGE_SIZE, "%d\n", (ctrl >> attr->index) & 1); + +abort: + mutex_unlock(&data->lock); + return ctrl; +} /*-----------------------------------------------------------------------*/ @@ -85,19 +174,23 @@ static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp, set_temp, LM73_REG_MIN); static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, LM73_REG_INPUT); - - -static struct attribute *lm73_attributes[] = { +static SENSOR_DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, + show_convrate, set_convrate, 0); +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, + show_maxmin_alarm, NULL, LM73_CTRL_HI_SHIFT); +static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, + show_maxmin_alarm, NULL, LM73_CTRL_LO_SHIFT); + +static struct attribute *lm73_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp1_min.dev_attr.attr, - + &sensor_dev_attr_update_interval.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, NULL }; - -static const struct attribute_group lm73_group = { - .attrs = lm73_attributes, -}; +ATTRIBUTE_GROUPS(lm73); /*-----------------------------------------------------------------------*/ @@ -106,38 +199,30 @@ static const struct attribute_group lm73_group = { static int lm73_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; struct device *hwmon_dev; - int status; + struct lm73_data *data; + int ctrl; - /* Register sysfs hooks */ - status = sysfs_create_group(&client->dev.kobj, &lm73_group); - if (status) - return status; + data = devm_kzalloc(dev, sizeof(struct lm73_data), GFP_KERNEL); + if (!data) + return -ENOMEM; - hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(hwmon_dev)) { - status = PTR_ERR(hwmon_dev); - goto exit_remove; - } - i2c_set_clientdata(client, hwmon_dev); + data->client = client; + mutex_init(&data->lock); - dev_info(&client->dev, "%s: sensor '%s'\n", - dev_name(hwmon_dev), client->name); + ctrl = i2c_smbus_read_byte_data(client, LM73_REG_CTRL); + if (ctrl < 0) + return ctrl; + data->ctrl = ctrl; - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, lm73_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); -exit_remove: - sysfs_remove_group(&client->dev.kobj, &lm73_group); - return status; -} - -static int lm73_remove(struct i2c_client *client) -{ - struct device *hwmon_dev = i2c_get_clientdata(client); + dev_info(dev, "sensor '%s'\n", client->name); - hwmon_device_unregister(hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &lm73_group); - i2c_set_clientdata(client, NULL); return 0; } @@ -152,17 +237,31 @@ static int lm73_detect(struct i2c_client *new_client, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; - u16 id; - u8 ctrl; + int id, ctrl, conf; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; + /* + * Do as much detection as possible with byte reads first, as word + * reads can confuse other devices. + */ + ctrl = i2c_smbus_read_byte_data(new_client, LM73_REG_CTRL); + if (ctrl < 0 || (ctrl & 0x10)) + return -ENODEV; + + conf = i2c_smbus_read_byte_data(new_client, LM73_REG_CONF); + if (conf < 0 || (conf & 0x0c)) + return -ENODEV; + + id = i2c_smbus_read_byte_data(new_client, LM73_REG_ID); + if (id < 0 || id != (LM73_ID & 0xff)) + return -ENODEV; + /* Check device ID */ id = i2c_smbus_read_word_data(new_client, LM73_REG_ID); - ctrl = i2c_smbus_read_byte_data(new_client, LM73_REG_CTRL); - if ((id != LM73_ID) || (ctrl & 0x10)) + if (id < 0 || id != LM73_ID) return -ENODEV; strlcpy(info->type, "lm73", I2C_NAME_SIZE); @@ -176,27 +275,13 @@ static struct i2c_driver lm73_driver = { .name = "lm73", }, .probe = lm73_probe, - .remove = lm73_remove, .id_table = lm73_ids, .detect = lm73_detect, .address_list = normal_i2c, }; -/* module glue */ - -static int __init sensors_lm73_init(void) -{ - return i2c_add_driver(&lm73_driver); -} - -static void __exit sensors_lm73_exit(void) -{ - i2c_del_driver(&lm73_driver); -} +module_i2c_driver(lm73_driver); MODULE_AUTHOR("Guillaume Ligneul <guillaume.ligneul@gmail.com>"); MODULE_DESCRIPTION("LM73 driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_lm73_init); -module_exit(sensors_lm73_exit); diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index 8ae2cfe2d82..479ffbeed3f 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> @@ -27,6 +27,8 @@ #include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/of.h> +#include <linux/thermal.h> #include "lm75.h" @@ -35,8 +37,11 @@ */ enum lm75_type { /* keep sorted in alphabetical order */ + adt75, ds1775, ds75, + ds7505, + g751, lm75, lm75a, max6625, @@ -46,6 +51,7 @@ enum lm75_type { /* keep sorted in alphabetical order */ tcn75, tmp100, tmp101, + tmp105, tmp175, tmp275, tmp75, @@ -66,12 +72,17 @@ static const u8 LM75_REG_TEMP[3] = { /* Each client has this additional data */ struct lm75_data { + struct i2c_client *client; struct device *hwmon_dev; + struct thermal_zone_device *tz; struct mutex update_lock; u8 orig_conf; + u8 resolution; /* In bits, between 9 and 12 */ + u8 resolution_limits; char valid; /* !=0 if registers are valid */ unsigned long last_updated; /* In jiffies */ - u16 temp[3]; /* Register values, + unsigned long sample_time; /* In jiffies */ + s16 temp[3]; /* Register values, 0 = input 1 = max 2 = hyst */ @@ -84,28 +95,66 @@ static struct lm75_data *lm75_update_device(struct device *dev); /*-----------------------------------------------------------------------*/ +static inline long lm75_reg_to_mc(s16 temp, u8 resolution) +{ + return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8); +} + /* sysfs attributes for hwmon */ +static int lm75_read_temp(void *dev, long *temp) +{ + struct lm75_data *data = lm75_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + *temp = lm75_reg_to_mc(data->temp[0], data->resolution); + + return 0; +} + static ssize_t show_temp(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct lm75_data *data = lm75_update_device(dev); - return sprintf(buf, "%d\n", - LM75_TEMP_FROM_REG(data->temp[attr->index])); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%ld\n", lm75_reg_to_mc(data->temp[attr->index], + data->resolution)); } static ssize_t set_temp(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct i2c_client *client = to_i2c_client(dev); - struct lm75_data *data = i2c_get_clientdata(client); + struct lm75_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; int nr = attr->index; - long temp = simple_strtol(buf, NULL, 10); + long temp; + int error; + u8 resolution; + + error = kstrtol(buf, 10, &temp); + if (error) + return error; + + /* + * Resolution of limit registers is assumed to be the same as the + * temperature input register resolution unless given explicitly. + */ + if (attr->index && data->resolution_limits) + resolution = data->resolution_limits; + else + resolution = data->resolution; mutex_lock(&data->update_lock); - data->temp[nr] = LM75_TEMP_TO_REG(temp); + temp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX); + data->temp[nr] = DIV_ROUND_CLOSEST(temp << (resolution - 8), + 1000) << (16 - resolution); lm75_write_value(client, LM75_REG_TEMP[nr], data->temp[nr]); mutex_unlock(&data->update_lock); return count; @@ -117,17 +166,14 @@ static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp, set_temp, 2); static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); -static struct attribute *lm75_attributes[] = { +static struct attribute *lm75_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, NULL }; - -static const struct attribute_group lm75_group = { - .attrs = lm75_attributes, -}; +ATTRIBUTE_GROUPS(lm75); /*-----------------------------------------------------------------------*/ @@ -136,19 +182,22 @@ static const struct attribute_group lm75_group = { static int lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; struct lm75_data *data; int status; u8 set_mask, clr_mask; int new; + enum lm75_type kind = id->driver_data; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return -EIO; - data = kzalloc(sizeof(struct lm75_data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct lm75_data), GFP_KERNEL); if (!data) return -ENOMEM; + data->client = client; i2c_set_clientdata(client, data); mutex_init(&data->update_lock); @@ -156,61 +205,114 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) * Then tweak to be more precise when appropriate. */ set_mask = 0; - clr_mask = (1 << 0) /* continuous conversions */ - | (1 << 6) | (1 << 5); /* 9-bit mode */ + clr_mask = LM75_SHUTDOWN; /* continuous conversions */ + + switch (kind) { + case adt75: + clr_mask |= 1 << 5; /* not one-shot mode */ + data->resolution = 12; + data->sample_time = HZ / 8; + break; + case ds1775: + case ds75: + case stds75: + clr_mask |= 3 << 5; + set_mask |= 2 << 5; /* 11-bit mode */ + data->resolution = 11; + data->sample_time = HZ; + break; + case ds7505: + set_mask |= 3 << 5; /* 12-bit mode */ + data->resolution = 12; + data->sample_time = HZ / 4; + break; + case g751: + case lm75: + case lm75a: + data->resolution = 9; + data->sample_time = HZ / 2; + break; + case max6625: + data->resolution = 9; + data->sample_time = HZ / 4; + break; + case max6626: + data->resolution = 12; + data->resolution_limits = 9; + data->sample_time = HZ / 4; + break; + case tcn75: + data->resolution = 9; + data->sample_time = HZ / 8; + break; + case mcp980x: + data->resolution_limits = 9; + /* fall through */ + case tmp100: + case tmp101: + set_mask |= 3 << 5; /* 12-bit mode */ + data->resolution = 12; + data->sample_time = HZ; + clr_mask |= 1 << 7; /* not one-shot mode */ + break; + case tmp105: + case tmp175: + case tmp275: + case tmp75: + set_mask |= 3 << 5; /* 12-bit mode */ + clr_mask |= 1 << 7; /* not one-shot mode */ + data->resolution = 12; + data->sample_time = HZ / 2; + break; + } /* configure as specified */ status = lm75_read_value(client, LM75_REG_CONF); if (status < 0) { - dev_dbg(&client->dev, "Can't read config? %d\n", status); - goto exit_free; + dev_dbg(dev, "Can't read config? %d\n", status); + return status; } data->orig_conf = status; new = status & ~clr_mask; new |= set_mask; if (status != new) lm75_write_value(client, LM75_REG_CONF, new); - dev_dbg(&client->dev, "Config %02x\n", new); + dev_dbg(dev, "Config %02x\n", new); - /* Register sysfs hooks */ - status = sysfs_create_group(&client->dev.kobj, &lm75_group); - if (status) - goto exit_free; + data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name, + data, lm75_groups); + if (IS_ERR(data->hwmon_dev)) + return PTR_ERR(data->hwmon_dev); - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - status = PTR_ERR(data->hwmon_dev); - goto exit_remove; - } + data->tz = thermal_zone_of_sensor_register(data->hwmon_dev, + 0, + data->hwmon_dev, + lm75_read_temp, NULL); + if (IS_ERR(data->tz)) + data->tz = NULL; - dev_info(&client->dev, "%s: sensor '%s'\n", + dev_info(dev, "%s: sensor '%s'\n", dev_name(data->hwmon_dev), client->name); return 0; - -exit_remove: - sysfs_remove_group(&client->dev.kobj, &lm75_group); -exit_free: - i2c_set_clientdata(client, NULL); - kfree(data); - return status; } static int lm75_remove(struct i2c_client *client) { struct lm75_data *data = i2c_get_clientdata(client); + thermal_zone_of_sensor_unregister(data->hwmon_dev, data->tz); hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &lm75_group); lm75_write_value(client, LM75_REG_CONF, data->orig_conf); - i2c_set_clientdata(client, NULL); - kfree(data); return 0; } static const struct i2c_device_id lm75_ids[] = { + { "adt75", adt75, }, { "ds1775", ds1775, }, { "ds75", ds75, }, + { "ds7505", ds7505, }, + { "g751", g751, }, { "lm75", lm75, }, { "lm75a", lm75a, }, { "max6625", max6625, }, @@ -220,6 +322,7 @@ static const struct i2c_device_id lm75_ids[] = { { "tcn75", tcn75, }, { "tmp100", tmp100, }, { "tmp101", tmp101, }, + { "tmp105", tmp105, }, { "tmp175", tmp175, }, { "tmp275", tmp275, }, { "tmp75", tmp75, }, @@ -227,63 +330,137 @@ static const struct i2c_device_id lm75_ids[] = { }; MODULE_DEVICE_TABLE(i2c, lm75_ids); +#define LM75A_ID 0xA1 + /* Return 0 if detection is successful, -ENODEV otherwise */ static int lm75_detect(struct i2c_client *new_client, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; int i; - int cur, conf, hyst, os; + int conf, hyst, os; + bool is_lm75a = 0; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; - /* Now, we do the remaining detection. There is no identification- - dedicated register so we have to rely on several tricks: - unused bits, registers cycling over 8-address boundaries, - addresses 0x04-0x07 returning the last read value. - The cycling+unused addresses combination is not tested, - since it would significantly slow the detection down and would - hardly add any value. */ - - /* Unused addresses */ - cur = i2c_smbus_read_word_data(new_client, 0); - conf = i2c_smbus_read_byte_data(new_client, 1); - hyst = i2c_smbus_read_word_data(new_client, 2); - if (i2c_smbus_read_word_data(new_client, 4) != hyst - || i2c_smbus_read_word_data(new_client, 5) != hyst - || i2c_smbus_read_word_data(new_client, 6) != hyst - || i2c_smbus_read_word_data(new_client, 7) != hyst) - return -ENODEV; - os = i2c_smbus_read_word_data(new_client, 3); - if (i2c_smbus_read_word_data(new_client, 4) != os - || i2c_smbus_read_word_data(new_client, 5) != os - || i2c_smbus_read_word_data(new_client, 6) != os - || i2c_smbus_read_word_data(new_client, 7) != os) - return -ENODEV; + /* + * Now, we do the remaining detection. There is no identification- + * dedicated register so we have to rely on several tricks: + * unused bits, registers cycling over 8-address boundaries, + * addresses 0x04-0x07 returning the last read value. + * The cycling+unused addresses combination is not tested, + * since it would significantly slow the detection down and would + * hardly add any value. + * + * The National Semiconductor LM75A is different than earlier + * LM75s. It has an ID byte of 0xaX (where X is the chip + * revision, with 1 being the only revision in existence) in + * register 7, and unused registers return 0xff rather than the + * last read value. + * + * Note that this function only detects the original National + * Semiconductor LM75 and the LM75A. Clones from other vendors + * aren't detected, on purpose, because they are typically never + * found on PC hardware. They are found on embedded designs where + * they can be instantiated explicitly so detection is not needed. + * The absence of identification registers on all these clones + * would make their exhaustive detection very difficult and weak, + * and odds are that the driver would bind to unsupported devices. + */ /* Unused bits */ + conf = i2c_smbus_read_byte_data(new_client, 1); if (conf & 0xe0) return -ENODEV; + /* First check for LM75A */ + if (i2c_smbus_read_byte_data(new_client, 7) == LM75A_ID) { + /* LM75A returns 0xff on unused registers so + just to be sure we check for that too. */ + if (i2c_smbus_read_byte_data(new_client, 4) != 0xff + || i2c_smbus_read_byte_data(new_client, 5) != 0xff + || i2c_smbus_read_byte_data(new_client, 6) != 0xff) + return -ENODEV; + is_lm75a = 1; + hyst = i2c_smbus_read_byte_data(new_client, 2); + os = i2c_smbus_read_byte_data(new_client, 3); + } else { /* Traditional style LM75 detection */ + /* Unused addresses */ + hyst = i2c_smbus_read_byte_data(new_client, 2); + if (i2c_smbus_read_byte_data(new_client, 4) != hyst + || i2c_smbus_read_byte_data(new_client, 5) != hyst + || i2c_smbus_read_byte_data(new_client, 6) != hyst + || i2c_smbus_read_byte_data(new_client, 7) != hyst) + return -ENODEV; + os = i2c_smbus_read_byte_data(new_client, 3); + if (i2c_smbus_read_byte_data(new_client, 4) != os + || i2c_smbus_read_byte_data(new_client, 5) != os + || i2c_smbus_read_byte_data(new_client, 6) != os + || i2c_smbus_read_byte_data(new_client, 7) != os) + return -ENODEV; + } + /* Addresses cycling */ - for (i = 8; i < 0xff; i += 8) { + for (i = 8; i <= 248; i += 40) { if (i2c_smbus_read_byte_data(new_client, i + 1) != conf - || i2c_smbus_read_word_data(new_client, i + 2) != hyst - || i2c_smbus_read_word_data(new_client, i + 3) != os) + || i2c_smbus_read_byte_data(new_client, i + 2) != hyst + || i2c_smbus_read_byte_data(new_client, i + 3) != os) + return -ENODEV; + if (is_lm75a && i2c_smbus_read_byte_data(new_client, i + 7) + != LM75A_ID) return -ENODEV; } - strlcpy(info->type, "lm75", I2C_NAME_SIZE); + strlcpy(info->type, is_lm75a ? "lm75a" : "lm75", I2C_NAME_SIZE); + + return 0; +} + +#ifdef CONFIG_PM +static int lm75_suspend(struct device *dev) +{ + int status; + struct i2c_client *client = to_i2c_client(dev); + status = lm75_read_value(client, LM75_REG_CONF); + if (status < 0) { + dev_dbg(&client->dev, "Can't read config? %d\n", status); + return status; + } + status = status | LM75_SHUTDOWN; + lm75_write_value(client, LM75_REG_CONF, status); + return 0; +} +static int lm75_resume(struct device *dev) +{ + int status; + struct i2c_client *client = to_i2c_client(dev); + status = lm75_read_value(client, LM75_REG_CONF); + if (status < 0) { + dev_dbg(&client->dev, "Can't read config? %d\n", status); + return status; + } + status = status & ~LM75_SHUTDOWN; + lm75_write_value(client, LM75_REG_CONF, status); return 0; } +static const struct dev_pm_ops lm75_dev_pm_ops = { + .suspend = lm75_suspend, + .resume = lm75_resume, +}; +#define LM75_DEV_PM_OPS (&lm75_dev_pm_ops) +#else +#define LM75_DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + static struct i2c_driver lm75_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "lm75", + .pm = LM75_DEV_PM_OPS, }, .probe = lm75_probe, .remove = lm75_remove, @@ -296,18 +473,17 @@ 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; - if (reg == LM75_REG_CONF) return i2c_smbus_read_byte_data(client, reg); - - value = i2c_smbus_read_word_data(client, reg); - return (value < 0) ? value : swab16(value); + else + return i2c_smbus_read_word_swapped(client, reg); } static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value) @@ -315,17 +491,18 @@ static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value) if (reg == LM75_REG_CONF) return i2c_smbus_write_byte_data(client, reg, value); else - return i2c_smbus_write_word_data(client, reg, swab16(value)); + return i2c_smbus_write_word_swapped(client, reg, value); } static struct lm75_data *lm75_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct lm75_data *data = i2c_get_clientdata(client); + struct lm75_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + struct lm75_data *ret = data; mutex_lock(&data->update_lock); - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + if (time_after(jiffies, data->last_updated + data->sample_time) || !data->valid) { int i; dev_dbg(&client->dev, "Starting lm75 update\n"); @@ -334,38 +511,27 @@ static struct lm75_data *lm75_update_device(struct device *dev) int status; status = lm75_read_value(client, LM75_REG_TEMP[i]); - if (status < 0) - dev_dbg(&client->dev, "reg %d, err %d\n", - LM75_REG_TEMP[i], status); - else - data->temp[i] = status; + if (unlikely(status < 0)) { + dev_dbg(dev, + "LM75: Failed to read value: reg %d, error %d\n", + LM75_REG_TEMP[i], status); + ret = ERR_PTR(status); + data->valid = 0; + goto abort; + } + data->temp[i] = status; } data->last_updated = jiffies; data->valid = 1; } +abort: mutex_unlock(&data->update_lock); - - return data; -} - -/*-----------------------------------------------------------------------*/ - -/* module glue */ - -static int __init sensors_lm75_init(void) -{ - return i2c_add_driver(&lm75_driver); + return ret; } -static void __exit sensors_lm75_exit(void) -{ - i2c_del_driver(&lm75_driver); -} +module_i2c_driver(lm75_driver); MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"); MODULE_DESCRIPTION("LM75 driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_lm75_init); -module_exit(sensors_lm75_exit); diff --git a/drivers/hwmon/lm75.h b/drivers/hwmon/lm75.h index 7c93454bb4e..5cde94e56f1 100644 --- a/drivers/hwmon/lm75.h +++ b/drivers/hwmon/lm75.h @@ -1,6 +1,6 @@ /* lm75.h - Part of lm_sensors, Linux kernel modules for hardware - monitoring + monitoring Copyright (c) 2003 Mark M. Hoffman <mhoffman@lightlink.com> This program is free software; you can redistribute it and/or modify @@ -25,18 +25,19 @@ which contains this code, we don't worry about the wasted space. */ -#include <linux/hwmon.h> +#include <linux/kernel.h> /* straight from the datasheet */ #define LM75_TEMP_MIN (-55000) #define LM75_TEMP_MAX 125000 +#define LM75_SHUTDOWN 0x01 /* TEMP: 0.001C/bit (-55C to +125C) REG: (0.5C/bit, two's complement) << 7 */ static inline u16 LM75_TEMP_TO_REG(long temp) { - int ntemp = SENSORS_LIMIT(temp, LM75_TEMP_MIN, LM75_TEMP_MAX); - ntemp += (ntemp<0 ? -250 : 250); + int ntemp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX); + ntemp += (ntemp < 0 ? -250 : 250); return (u16)((ntemp / 500) << 7); } @@ -46,4 +47,3 @@ static inline int LM75_TEMP_FROM_REG(u16 reg) guarantee arithmetic shift and preserve the sign */ return ((s16)reg / 128) * 500; } - diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c index b28a297be50..5ceb443b938 100644 --- a/drivers/hwmon/lm77.c +++ b/drivers/hwmon/lm77.c @@ -1,29 +1,25 @@ /* - lm77.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - - Copyright (c) 2004 Andras BALI <drewie@freemail.hu> - - Heavily based on lm75.c by Frodo Looijaard <frodol@dds.nl>. The LM77 - is a temperature sensor and thermal window comparator with 0.5 deg - resolution made by National Semiconductor. Complete datasheet can be - obtained at their site: - http://www.national.com/pf/LM/LM77.html - - 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. -*/ + * lm77.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * + * Copyright (c) 2004 Andras BALI <drewie@freemail.hu> + * + * Heavily based on lm75.c by Frodo Looijaard <frodol@dds.nl>. The LM77 + * is a temperature sensor and thermal window comparator with 0.5 deg + * resolution made by National Semiconductor. Complete datasheet can be + * obtained at their site: + * http://www.national.com/pf/LM/LM77.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ #include <linux/module.h> #include <linux/init.h> @@ -47,59 +43,44 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, #define LM77_REG_TEMP_MIN 0x04 #define LM77_REG_TEMP_MAX 0x05 +enum temp_index { + t_input = 0, + t_crit, + t_min, + t_max, + t_hyst, + t_num_temp +}; + +static const u8 temp_regs[t_num_temp] = { + [t_input] = LM77_REG_TEMP, + [t_min] = LM77_REG_TEMP_MIN, + [t_max] = LM77_REG_TEMP_MAX, + [t_crit] = LM77_REG_TEMP_CRIT, + [t_hyst] = LM77_REG_TEMP_HYST, +}; + /* Each client has this additional data */ struct lm77_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; char valid; unsigned long last_updated; /* In jiffies */ - int temp_input; /* Temperatures */ - int temp_crit; - int temp_min; - int temp_max; - int temp_hyst; + int temp[t_num_temp]; /* index using temp_index */ u8 alarms; }; -static int lm77_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int lm77_detect(struct i2c_client *client, struct i2c_board_info *info); -static void lm77_init_client(struct i2c_client *client); -static int lm77_remove(struct i2c_client *client); -static u16 lm77_read_value(struct i2c_client *client, u8 reg); -static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value); - -static struct lm77_data *lm77_update_device(struct device *dev); - - -static const struct i2c_device_id lm77_id[] = { - { "lm77", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, lm77_id); - -/* This is the driver that will be inserted */ -static struct i2c_driver lm77_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "lm77", - }, - .probe = lm77_probe, - .remove = lm77_remove, - .id_table = lm77_id, - .detect = lm77_detect, - .address_list = normal_i2c, -}; - /* straight from the datasheet */ #define LM77_TEMP_MIN (-55000) #define LM77_TEMP_MAX 125000 -/* In the temperature registers, the low 3 bits are not part of the - temperature values; they are the status bits. */ +/* + * In the temperature registers, the low 3 bits are not part of the + * temperature values; they are the status bits. + */ static inline s16 LM77_TEMP_TO_REG(int temp) { - int ntemp = SENSORS_LIMIT(temp, LM77_TEMP_MIN, LM77_TEMP_MAX); + int ntemp = clamp_val(temp, LM77_TEMP_MIN, LM77_TEMP_MAX); return (ntemp / 500) * 8; } @@ -108,88 +89,120 @@ static inline int LM77_TEMP_FROM_REG(s16 reg) return (reg / 8) * 500; } -/* sysfs stuff */ - -/* read routines for temperature limits */ -#define show(value) \ -static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm77_data *data = lm77_update_device(dev); \ - return sprintf(buf, "%d\n", data->value); \ +/* + * All registers are word-sized, except for the configuration register. + * The LM77 uses the high-byte first convention. + */ +static u16 lm77_read_value(struct i2c_client *client, u8 reg) +{ + if (reg == LM77_REG_CONF) + return i2c_smbus_read_byte_data(client, reg); + else + return i2c_smbus_read_word_swapped(client, reg); } -show(temp_input); -show(temp_crit); -show(temp_min); -show(temp_max); +static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if (reg == LM77_REG_CONF) + return i2c_smbus_write_byte_data(client, reg, value); + else + return i2c_smbus_write_word_swapped(client, reg, value); +} -/* read routines for hysteresis values */ -static ssize_t show_temp_crit_hyst(struct device *dev, struct device_attribute *attr, char *buf) +static struct lm77_data *lm77_update_device(struct device *dev) { - struct lm77_data *data = lm77_update_device(dev); - return sprintf(buf, "%d\n", data->temp_crit - data->temp_hyst); + struct lm77_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int i; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + dev_dbg(&client->dev, "Starting lm77 update\n"); + for (i = 0; i < t_num_temp; i++) { + data->temp[i] = + LM77_TEMP_FROM_REG(lm77_read_value(client, + temp_regs[i])); + } + data->alarms = + lm77_read_value(client, LM77_REG_TEMP) & 0x0007; + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; } -static ssize_t show_temp_min_hyst(struct device *dev, struct device_attribute *attr, char *buf) + +/* sysfs stuff */ + +static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, + char *buf) { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm77_data *data = lm77_update_device(dev); - return sprintf(buf, "%d\n", data->temp_min + data->temp_hyst); + + return sprintf(buf, "%d\n", data->temp[attr->index]); } -static ssize_t show_temp_max_hyst(struct device *dev, struct device_attribute *attr, char *buf) + +static ssize_t show_temp_hyst(struct device *dev, + struct device_attribute *devattr, char *buf) { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm77_data *data = lm77_update_device(dev); - return sprintf(buf, "%d\n", data->temp_max - data->temp_hyst); -} + int nr = attr->index; + int temp; -/* write routines */ -#define set(value, reg) \ -static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct lm77_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - mutex_lock(&data->update_lock); \ - data->value = val; \ - lm77_write_value(client, reg, LM77_TEMP_TO_REG(data->value)); \ - mutex_unlock(&data->update_lock); \ - return count; \ -} + temp = nr == t_min ? data->temp[nr] + data->temp[t_hyst] : + data->temp[nr] - data->temp[t_hyst]; -set(temp_min, LM77_REG_TEMP_MIN); -set(temp_max, LM77_REG_TEMP_MAX); + return sprintf(buf, "%d\n", temp); +} -/* hysteresis is stored as a relative value on the chip, so it has to be - converted first */ -static ssize_t set_temp_crit_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t set_temp(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm77_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm77_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int nr = attr->index; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - data->temp_hyst = data->temp_crit - val; - lm77_write_value(client, LM77_REG_TEMP_HYST, - LM77_TEMP_TO_REG(data->temp_hyst)); + data->temp[nr] = val; + lm77_write_value(client, temp_regs[nr], LM77_TEMP_TO_REG(val)); mutex_unlock(&data->update_lock); return count; } -/* preserve hysteresis when setting T_crit */ -static ssize_t set_temp_crit(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +/* + * hysteresis is stored as a relative value on the chip, so it has to be + * converted first. + */ +static ssize_t set_temp_hyst(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm77_data *data = i2c_get_clientdata(client); - long val = simple_strtoul(buf, NULL, 10); - int oldcrithyst; - + struct lm77_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + mutex_lock(&data->update_lock); - oldcrithyst = data->temp_crit - data->temp_hyst; - data->temp_crit = val; - data->temp_hyst = data->temp_crit - oldcrithyst; - lm77_write_value(client, LM77_REG_TEMP_CRIT, - LM77_TEMP_TO_REG(data->temp_crit)); + data->temp[t_hyst] = data->temp[t_crit] - val; lm77_write_value(client, LM77_REG_TEMP_HYST, - LM77_TEMP_TO_REG(data->temp_hyst)); + LM77_TEMP_TO_REG(data->temp[t_hyst])); mutex_unlock(&data->update_lock); return count; } @@ -202,80 +215,75 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); } -static DEVICE_ATTR(temp1_input, S_IRUGO, - show_temp_input, NULL); -static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, - show_temp_crit, set_temp_crit); -static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, - show_temp_min, set_temp_min); -static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, - show_temp_max, set_temp_max); - -static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, - show_temp_crit_hyst, set_temp_crit_hyst); -static DEVICE_ATTR(temp1_min_hyst, S_IRUGO, - show_temp_min_hyst, NULL); -static DEVICE_ATTR(temp1_max_hyst, S_IRUGO, - show_temp_max_hyst, NULL); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, t_input); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp, set_temp, + t_crit); +static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp, set_temp, + t_min); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp, set_temp, + t_max); + +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp_hyst, + set_temp_hyst, t_crit); +static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO, show_temp_hyst, NULL, t_min); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp_hyst, NULL, t_max); static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 2); static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 1); -static struct attribute *lm77_attributes[] = { - &dev_attr_temp1_input.attr, - &dev_attr_temp1_crit.attr, - &dev_attr_temp1_min.attr, - &dev_attr_temp1_max.attr, - &dev_attr_temp1_crit_hyst.attr, - &dev_attr_temp1_min_hyst.attr, - &dev_attr_temp1_max_hyst.attr, +static struct attribute *lm77_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_min_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, NULL }; - -static const struct attribute_group lm77_group = { - .attrs = lm77_attributes, -}; +ATTRIBUTE_GROUPS(lm77); /* Return 0 if detection is successful, -ENODEV otherwise */ -static int lm77_detect(struct i2c_client *new_client, - struct i2c_board_info *info) +static int lm77_detect(struct i2c_client *client, struct i2c_board_info *info) { - struct i2c_adapter *adapter = new_client->adapter; + struct i2c_adapter *adapter = client->adapter; int i, cur, conf, hyst, crit, min, max; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; - /* Here comes the remaining detection. Since the LM77 has no - register dedicated to identification, we have to rely on the - following tricks: - - 1. the high 4 bits represent the sign and thus they should - always be the same - 2. the high 3 bits are unused in the configuration register - 3. addresses 0x06 and 0x07 return the last read value - 4. registers cycling over 8-address boundaries - - Word-sized registers are high-byte first. */ + /* + * Here comes the remaining detection. Since the LM77 has no + * register dedicated to identification, we have to rely on the + * following tricks: + * + * 1. the high 4 bits represent the sign and thus they should + * always be the same + * 2. the high 3 bits are unused in the configuration register + * 3. addresses 0x06 and 0x07 return the last read value + * 4. registers cycling over 8-address boundaries + * + * Word-sized registers are high-byte first. + */ /* addresses cycling */ - cur = i2c_smbus_read_word_data(new_client, 0); - conf = i2c_smbus_read_byte_data(new_client, 1); - hyst = i2c_smbus_read_word_data(new_client, 2); - crit = i2c_smbus_read_word_data(new_client, 3); - min = i2c_smbus_read_word_data(new_client, 4); - max = i2c_smbus_read_word_data(new_client, 5); + cur = i2c_smbus_read_word_data(client, 0); + conf = i2c_smbus_read_byte_data(client, 1); + hyst = i2c_smbus_read_word_data(client, 2); + crit = i2c_smbus_read_word_data(client, 3); + min = i2c_smbus_read_word_data(client, 4); + max = i2c_smbus_read_word_data(client, 5); for (i = 8; i <= 0xff; i += 8) { - if (i2c_smbus_read_byte_data(new_client, i + 1) != conf - || i2c_smbus_read_word_data(new_client, i + 2) != hyst - || i2c_smbus_read_word_data(new_client, i + 3) != crit - || i2c_smbus_read_word_data(new_client, i + 4) != min - || i2c_smbus_read_word_data(new_client, i + 5) != max) + if (i2c_smbus_read_byte_data(client, i + 1) != conf + || i2c_smbus_read_word_data(client, i + 2) != hyst + || i2c_smbus_read_word_data(client, i + 3) != crit + || i2c_smbus_read_word_data(client, i + 4) != min + || i2c_smbus_read_word_data(client, i + 5) != max) return -ENODEV; } @@ -292,17 +300,17 @@ static int lm77_detect(struct i2c_client *new_client, return -ENODEV; /* 0x06 and 0x07 return the last read value */ - cur = i2c_smbus_read_word_data(new_client, 0); - if (i2c_smbus_read_word_data(new_client, 6) != cur - || i2c_smbus_read_word_data(new_client, 7) != cur) + cur = i2c_smbus_read_word_data(client, 0); + if (i2c_smbus_read_word_data(client, 6) != cur + || i2c_smbus_read_word_data(client, 7) != cur) return -ENODEV; - hyst = i2c_smbus_read_word_data(new_client, 2); - if (i2c_smbus_read_word_data(new_client, 6) != hyst - || i2c_smbus_read_word_data(new_client, 7) != hyst) + hyst = i2c_smbus_read_word_data(client, 2); + if (i2c_smbus_read_word_data(client, 6) != hyst + || i2c_smbus_read_word_data(client, 7) != hyst) return -ENODEV; - min = i2c_smbus_read_word_data(new_client, 4); - if (i2c_smbus_read_word_data(new_client, 6) != min - || i2c_smbus_read_word_data(new_client, 7) != min) + min = i2c_smbus_read_word_data(client, 4); + if (i2c_smbus_read_word_data(client, 6) != min + || i2c_smbus_read_word_data(client, 7) != min) return -ENODEV; strlcpy(info->type, "lm77", I2C_NAME_SIZE); @@ -310,72 +318,6 @@ static int lm77_detect(struct i2c_client *new_client, return 0; } -static int lm77_probe(struct i2c_client *new_client, - const struct i2c_device_id *id) -{ - struct lm77_data *data; - int err; - - data = kzalloc(sizeof(struct lm77_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } - - i2c_set_clientdata(new_client, data); - data->valid = 0; - mutex_init(&data->update_lock); - - /* Initialize the LM77 chip */ - lm77_init_client(new_client); - - /* Register sysfs hooks */ - if ((err = sysfs_create_group(&new_client->dev.kobj, &lm77_group))) - goto exit_free; - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove; - } - - return 0; - -exit_remove: - sysfs_remove_group(&new_client->dev.kobj, &lm77_group); -exit_free: - kfree(data); -exit: - return err; -} - -static int lm77_remove(struct i2c_client *client) -{ - struct lm77_data *data = i2c_get_clientdata(client); - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &lm77_group); - kfree(data); - return 0; -} - -/* All registers are word-sized, except for the configuration register. - The LM77 uses the high-byte first convention. */ -static u16 lm77_read_value(struct i2c_client *client, u8 reg) -{ - if (reg == LM77_REG_CONF) - return i2c_smbus_read_byte_data(client, reg); - else - return swab16(i2c_smbus_read_word_data(client, reg)); -} - -static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value) -{ - if (reg == LM77_REG_CONF) - return i2c_smbus_write_byte_data(client, reg, value); - else - return i2c_smbus_write_word_data(client, reg, swab16(value)); -} - static void lm77_init_client(struct i2c_client *client) { /* Initialize the LM77 chip - turn off shutdown mode */ @@ -384,55 +326,47 @@ static void lm77_init_client(struct i2c_client *client) lm77_write_value(client, LM77_REG_CONF, conf & 0xfe); } -static struct lm77_data *lm77_update_device(struct device *dev) +static int lm77_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct i2c_client *client = to_i2c_client(dev); - struct lm77_data *data = i2c_get_clientdata(client); + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct lm77_data *data; - mutex_lock(&data->update_lock); + data = devm_kzalloc(dev, sizeof(struct lm77_data), GFP_KERNEL); + if (!data) + return -ENOMEM; - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - dev_dbg(&client->dev, "Starting lm77 update\n"); - data->temp_input = - LM77_TEMP_FROM_REG(lm77_read_value(client, - LM77_REG_TEMP)); - data->temp_hyst = - LM77_TEMP_FROM_REG(lm77_read_value(client, - LM77_REG_TEMP_HYST)); - data->temp_crit = - LM77_TEMP_FROM_REG(lm77_read_value(client, - LM77_REG_TEMP_CRIT)); - data->temp_min = - LM77_TEMP_FROM_REG(lm77_read_value(client, - LM77_REG_TEMP_MIN)); - data->temp_max = - LM77_TEMP_FROM_REG(lm77_read_value(client, - LM77_REG_TEMP_MAX)); - data->alarms = - lm77_read_value(client, LM77_REG_TEMP) & 0x0007; - data->last_updated = jiffies; - data->valid = 1; - } + data->client = client; + mutex_init(&data->update_lock); - mutex_unlock(&data->update_lock); + /* Initialize the LM77 chip */ + lm77_init_client(client); - return data; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, lm77_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } -static int __init sensors_lm77_init(void) -{ - return i2c_add_driver(&lm77_driver); -} +static const struct i2c_device_id lm77_id[] = { + { "lm77", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm77_id); -static void __exit sensors_lm77_exit(void) -{ - i2c_del_driver(&lm77_driver); -} +/* This is the driver that will be inserted */ +static struct i2c_driver lm77_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "lm77", + }, + .probe = lm77_probe, + .id_table = lm77_id, + .detect = lm77_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(lm77_driver); MODULE_AUTHOR("Andras BALI <drewie@freemail.hu>"); MODULE_DESCRIPTION("LM77 driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_lm77_init); -module_exit(sensors_lm77_exit); diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index cadcbd90ff3..9efadfc851b 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -1,46 +1,46 @@ /* - lm78.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> - Copyright (c) 2007 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; 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. -*/ + * lm78.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> + * Copyright (c) 2007, 2011 Jean Delvare <jdelvare@suse.de> + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/jiffies.h> #include <linux/i2c.h> -#include <linux/platform_device.h> -#include <linux/ioport.h> #include <linux/hwmon.h> #include <linux/hwmon-vid.h> #include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> -#include <linux/io.h> -/* ISA device, if found */ -static struct platform_device *pdev; +#ifdef CONFIG_ISA +#include <linux/platform_device.h> +#include <linux/ioport.h> +#include <linux/io.h> +#endif /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; -static unsigned short isa_address = 0x290; - enum chips { lm78, lm79 }; /* Many LM78 constants specified below */ @@ -74,14 +74,18 @@ enum chips { lm78, lm79 }; #define LM78_REG_I2C_ADDR 0x48 -/* Conversions. Rounding and limit checking is only done on the TO_REG - variants. */ +/* + * Conversions. Rounding and limit checking is only done on the TO_REG + * variants. + */ -/* IN: mV, (0V to 4.08V) - REG: 16mV/bit */ +/* + * IN: mV (0V to 4.08V) + * REG: 16mV/bit + */ static inline u8 IN_TO_REG(unsigned long val) { - unsigned long nval = SENSORS_LIMIT(val, 0, 4080); + unsigned long nval = clamp_val(val, 0, 4080); return (nval + 8) / 16; } #define IN_FROM_REG(val) ((val) * 16) @@ -90,20 +94,24 @@ static inline u8 FAN_TO_REG(long rpm, int div) { if (rpm <= 0) return 255; - return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254); + if (rpm > 1350000) + return 1; + return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254); } static inline int FAN_FROM_REG(u8 val, int div) { - return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div); + return val == 0 ? -1 : val == 255 ? 0 : 1350000 / (val * div); } -/* TEMP: mC (-128C to +127C) - REG: 1C/bit, two's complement */ +/* + * TEMP: mC (-128C to +127C) + * REG: 1C/bit, two's complement + */ static inline s8 TEMP_TO_REG(int val) { - int nval = SENSORS_LIMIT(val, -128000, 127000) ; - return nval<0 ? (nval-500)/1000 : (nval+500)/1000; + int nval = clamp_val(val, -128000, 127000) ; + return nval < 0 ? (nval - 500) / 1000 : (nval + 500) / 1000; } static inline int TEMP_FROM_REG(s8 val) @@ -141,50 +149,12 @@ struct lm78_data { }; -static int lm78_i2c_detect(struct i2c_client *client, - struct i2c_board_info *info); -static int lm78_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int lm78_i2c_remove(struct i2c_client *client); - -static int __devinit lm78_isa_probe(struct platform_device *pdev); -static int __devexit lm78_isa_remove(struct platform_device *pdev); - static int lm78_read_value(struct lm78_data *data, u8 reg); static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value); static struct lm78_data *lm78_update_device(struct device *dev); static void lm78_init_device(struct lm78_data *data); -static const struct i2c_device_id lm78_i2c_id[] = { - { "lm78", lm78 }, - { "lm79", lm79 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, lm78_i2c_id); - -static struct i2c_driver lm78_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "lm78", - }, - .probe = lm78_i2c_probe, - .remove = lm78_i2c_remove, - .id_table = lm78_i2c_id, - .detect = lm78_i2c_detect, - .address_list = normal_i2c, -}; - -static struct platform_driver lm78_isa_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "lm78", - }, - .probe = lm78_isa_probe, - .remove = __devexit_p(lm78_isa_remove), -}; - - /* 7 Voltages */ static ssize_t show_in(struct device *dev, struct device_attribute *da, char *buf) @@ -215,8 +185,13 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *da, { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct lm78_data *data = dev_get_drvdata(dev); - unsigned long val = simple_strtoul(buf, NULL, 10); int nr = attr->index; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_min[nr] = IN_TO_REG(val); @@ -230,8 +205,13 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *da, { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct lm78_data *data = dev_get_drvdata(dev); - unsigned long val = simple_strtoul(buf, NULL, 10); int nr = attr->index; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_max[nr] = IN_TO_REG(val); @@ -239,7 +219,7 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *da, mutex_unlock(&data->update_lock); return count; } - + #define show_in_offset(offset) \ static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \ show_in, NULL, offset); \ @@ -275,7 +255,12 @@ static ssize_t set_temp_over(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct lm78_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_over = TEMP_TO_REG(val); @@ -295,7 +280,12 @@ static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct lm78_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_hyst = TEMP_TO_REG(val); @@ -318,7 +308,7 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *da, struct lm78_data *data = lm78_update_device(dev); int nr = attr->index; return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], - DIV_FROM_REG(data->fan_div[nr])) ); + DIV_FROM_REG(data->fan_div[nr]))); } static ssize_t show_fan_min(struct device *dev, struct device_attribute *da, @@ -327,8 +317,8 @@ static ssize_t show_fan_min(struct device *dev, struct device_attribute *da, struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct lm78_data *data = lm78_update_device(dev); int nr = attr->index; - return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr], - DIV_FROM_REG(data->fan_div[nr])) ); + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr]))); } static ssize_t set_fan_min(struct device *dev, struct device_attribute *da, @@ -337,7 +327,12 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *da, struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct lm78_data *data = dev_get_drvdata(dev); int nr = attr->index; - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); @@ -354,32 +349,48 @@ static ssize_t show_fan_div(struct device *dev, struct device_attribute *da, return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index])); } -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan divisor. This follows the principle of - least surprise; the user doesn't expect the fan minimum to change just - because the divisor changed. */ +/* + * Note: we save and restore the fan minimum here, because its value is + * determined in part by the fan divisor. This follows the principle of + * least surprise; the user doesn't expect the fan minimum to change just + * because the divisor changed. + */ static ssize_t set_fan_div(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct lm78_data *data = dev_get_drvdata(dev); int nr = attr->index; - unsigned long val = simple_strtoul(buf, NULL, 10); unsigned long min; u8 reg; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])); switch (val) { - case 1: data->fan_div[nr] = 0; break; - case 2: data->fan_div[nr] = 1; break; - case 4: data->fan_div[nr] = 2; break; - case 8: data->fan_div[nr] = 3; break; + case 1: + data->fan_div[nr] = 0; + break; + case 2: + data->fan_div[nr] = 1; + break; + case 4: + data->fan_div[nr] = 2; + break; + case 8: + data->fan_div[nr] = 3; + break; default: - dev_err(dev, "fan_div value %ld not " - "supported. Choose one of 1, 2, 4 or 8!\n", val); + dev_err(dev, + "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n", + val); mutex_unlock(&data->update_lock); return -EINVAL; } @@ -512,8 +523,20 @@ static const struct attribute_group lm78_group = { .attrs = lm78_attributes, }; -/* I2C devices get this name attribute automatically, but for ISA devices - we must create it by ourselves. */ +/* + * ISA related code + */ +#ifdef CONFIG_ISA + +/* ISA device, if found */ +static struct platform_device *pdev; + +static unsigned short isa_address = 0x290; + +/* + * I2C devices get this name attribute automatically, but for ISA devices + * we must create it by ourselves. + */ static ssize_t show_name(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -523,6 +546,11 @@ static ssize_t show_name(struct device *dev, struct device_attribute } static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); +static struct lm78_data *lm78_data_if_isa(void) +{ + return pdev ? platform_get_drvdata(pdev) : NULL; +} + /* Returns 1 if the I2C chip appears to be an alias of the ISA chip */ static int lm78_alias_detect(struct i2c_client *client, u8 chipid) { @@ -538,8 +566,10 @@ static int lm78_alias_detect(struct i2c_client *client, u8 chipid) if ((lm78_read_value(isa, LM78_REG_CHIPID) & 0xfe) != (chipid & 0xfe)) return 0; /* Chip type doesn't match */ - /* We compare all the limit registers, the config register and the - * interrupt mask registers */ + /* + * We compare all the limit registers, the config register and the + * interrupt mask registers + */ for (i = 0x2b; i <= 0x3d; i++) { if (lm78_read_value(isa, i) != i2c_smbus_read_byte_data(client, i)) @@ -556,12 +586,24 @@ static int lm78_alias_detect(struct i2c_client *client, u8 chipid) return 1; } +#else /* !CONFIG_ISA */ + +static int lm78_alias_detect(struct i2c_client *client, u8 chipid) +{ + return 0; +} + +static struct lm78_data *lm78_data_if_isa(void) +{ + return NULL; +} +#endif /* CONFIG_ISA */ static int lm78_i2c_detect(struct i2c_client *client, struct i2c_board_info *info) { int i; - struct lm78_data *isa = pdev ? platform_get_drvdata(pdev) : NULL; + struct lm78_data *isa = lm78_data_if_isa(); const char *client_name; struct i2c_adapter *adapter = client->adapter; int address = client->addr; @@ -569,9 +611,11 @@ static int lm78_i2c_detect(struct i2c_client *client, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* We block updates of the ISA device to minimize the risk of - concurrent access to the same LM78 chip through different - interfaces. */ + /* + * We block updates of the ISA device to minimize the risk of + * concurrent access to the same LM78 chip through different + * interfaces. + */ if (isa) mutex_lock(&isa->update_lock); @@ -595,8 +639,9 @@ static int lm78_i2c_detect(struct i2c_client *client, goto err_nodev; if (lm78_alias_detect(client, i)) { - dev_dbg(&adapter->dev, "Device at 0x%02x appears to " - "be the same as ISA device\n", address); + dev_dbg(&adapter->dev, + "Device at 0x%02x appears to be the same as ISA device\n", + address); goto err_nodev; } @@ -619,7 +664,7 @@ static int lm78_i2c_probe(struct i2c_client *client, struct lm78_data *data; int err; - data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL); + data = devm_kzalloc(&client->dev, sizeof(struct lm78_data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -633,20 +678,18 @@ static int lm78_i2c_probe(struct i2c_client *client, /* Register sysfs hooks */ err = sysfs_create_group(&client->dev.kobj, &lm78_group); if (err) - goto ERROR3; + return err; data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); - goto ERROR4; + goto error; } return 0; -ERROR4: +error: sysfs_remove_group(&client->dev.kobj, &lm78_group); -ERROR3: - kfree(data); return err; } @@ -656,91 +699,41 @@ static int lm78_i2c_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &lm78_group); - kfree(data); return 0; } -static int __devinit lm78_isa_probe(struct platform_device *pdev) -{ - int err; - struct lm78_data *data; - struct resource *res; - - /* Reserve the ISA region */ - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start + LM78_ADDR_REG_OFFSET, 2, "lm78")) { - err = -EBUSY; - goto exit; - } - - if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit_release_region; - } - mutex_init(&data->lock); - data->isa_addr = res->start; - platform_set_drvdata(pdev, data); - - if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) { - data->type = lm79; - data->name = "lm79"; - } else { - data->type = lm78; - data->name = "lm78"; - } - - /* Initialize the LM78 chip */ - lm78_init_device(data); - - /* Register sysfs hooks */ - if ((err = sysfs_create_group(&pdev->dev.kobj, &lm78_group)) - || (err = device_create_file(&pdev->dev, &dev_attr_name))) - goto exit_remove_files; - - data->hwmon_dev = hwmon_device_register(&pdev->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - - exit_remove_files: - sysfs_remove_group(&pdev->dev.kobj, &lm78_group); - device_remove_file(&pdev->dev, &dev_attr_name); - kfree(data); - exit_release_region: - release_region(res->start + LM78_ADDR_REG_OFFSET, 2); - exit: - return err; -} - -static int __devexit lm78_isa_remove(struct platform_device *pdev) -{ - struct lm78_data *data = platform_get_drvdata(pdev); - struct resource *res; - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&pdev->dev.kobj, &lm78_group); - device_remove_file(&pdev->dev, &dev_attr_name); - kfree(data); - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - release_region(res->start + LM78_ADDR_REG_OFFSET, 2); +static const struct i2c_device_id lm78_i2c_id[] = { + { "lm78", lm78 }, + { "lm79", lm79 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm78_i2c_id); - return 0; -} +static struct i2c_driver lm78_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "lm78", + }, + .probe = lm78_i2c_probe, + .remove = lm78_i2c_remove, + .id_table = lm78_i2c_id, + .detect = lm78_i2c_detect, + .address_list = normal_i2c, +}; -/* The SMBus locks itself, but ISA access must be locked explicitly! - We don't want to lock the whole ISA bus, so we lock each client - separately. - We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, - would slow down the LM78 access and should not be necessary. */ +/* + * The SMBus locks itself, but ISA access must be locked explicitly! + * We don't want to lock the whole ISA bus, so we lock each client + * separately. + * We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, + * would slow down the LM78 access and should not be necessary. + */ static int lm78_read_value(struct lm78_data *data, u8 reg) { struct i2c_client *client = data->client; +#ifdef CONFIG_ISA if (!client) { /* ISA device */ int res; mutex_lock(&data->lock); @@ -749,20 +742,15 @@ static int lm78_read_value(struct lm78_data *data, u8 reg) mutex_unlock(&data->lock); return res; } else +#endif return i2c_smbus_read_byte_data(client, reg); } -/* The SMBus locks itself, but ISA access muse be locked explicitly! - We don't want to lock the whole ISA bus, so we lock each client - separately. - We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, - would slow down the LM78 access and should not be necessary. - There are some ugly typecasts here, but the good new is - they should - nowhere else be necessary! */ static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value) { struct i2c_client *client = data->client; +#ifdef CONFIG_ISA if (!client) { /* ISA device */ mutex_lock(&data->lock); outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET); @@ -770,6 +758,7 @@ static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value) mutex_unlock(&data->lock); return 0; } else +#endif return i2c_smbus_write_byte_data(client, reg, value); } @@ -847,26 +836,103 @@ static struct lm78_data *lm78_update_device(struct device *dev) return data; } +#ifdef CONFIG_ISA +static int lm78_isa_probe(struct platform_device *pdev) +{ + int err; + struct lm78_data *data; + struct resource *res; + + /* Reserve the ISA region */ + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!devm_request_region(&pdev->dev, res->start + LM78_ADDR_REG_OFFSET, + 2, "lm78")) + return -EBUSY; + + data = devm_kzalloc(&pdev->dev, sizeof(struct lm78_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + mutex_init(&data->lock); + data->isa_addr = res->start; + platform_set_drvdata(pdev, data); + + if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) { + data->type = lm79; + data->name = "lm79"; + } else { + data->type = lm78; + data->name = "lm78"; + } + + /* Initialize the LM78 chip */ + lm78_init_device(data); + + /* Register sysfs hooks */ + err = sysfs_create_group(&pdev->dev.kobj, &lm78_group); + if (err) + goto exit_remove_files; + err = device_create_file(&pdev->dev, &dev_attr_name); + if (err) + goto exit_remove_files; + + data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove_files; + } + + return 0; + + exit_remove_files: + sysfs_remove_group(&pdev->dev.kobj, &lm78_group); + device_remove_file(&pdev->dev, &dev_attr_name); + return err; +} + +static int lm78_isa_remove(struct platform_device *pdev) +{ + struct lm78_data *data = platform_get_drvdata(pdev); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&pdev->dev.kobj, &lm78_group); + device_remove_file(&pdev->dev, &dev_attr_name); + + return 0; +} + +static struct platform_driver lm78_isa_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "lm78", + }, + .probe = lm78_isa_probe, + .remove = lm78_isa_remove, +}; + /* return 1 if a supported chip is found, 0 otherwise */ static int __init lm78_isa_found(unsigned short address) { int val, save, found = 0; - - /* We have to request the region in two parts because some - boards declare base+4 to base+7 as a PNP device */ - if (!request_region(address, 4, "lm78")) { - pr_debug("lm78: Failed to request low part of region\n"); - return 0; - } - if (!request_region(address + 4, 4, "lm78")) { - pr_debug("lm78: Failed to request high part of region\n"); - release_region(address, 4); - return 0; + int port; + + /* + * Some boards declare base+0 to base+7 as a PNP device, some base+4 + * to base+7 and some base+5 to base+6. So we better request each port + * individually for the probing phase. + */ + for (port = address; port < address + LM78_EXTENT; port++) { + if (!request_region(port, 1, "lm78")) { + pr_debug("Failed to request port 0x%x\n", port); + goto release; + } } #define REALLY_SLOW_IO - /* We need the timeouts for at least some LM78-like - chips. But only if we read 'undefined' registers. */ + /* + * We need the timeouts for at least some LM78-like + * chips. But only if we read 'undefined' registers. + */ val = inb_p(address + 1); if (inb_p(address + 2) != val || inb_p(address + 3) != val @@ -874,8 +940,10 @@ static int __init lm78_isa_found(unsigned short address) goto release; #undef REALLY_SLOW_IO - /* We should be able to change the 7 LSB of the address port. The - MSB (busy flag) should be clear initially, set after the write. */ + /* + * We should be able to change the 7 LSB of the address port. The + * MSB (busy flag) should be clear initially, set after the write. + */ save = inb_p(address + LM78_ADDR_REG_OFFSET); if (save & 0x80) goto release; @@ -921,12 +989,12 @@ static int __init lm78_isa_found(unsigned short address) found = 1; if (found) - pr_info("lm78: Found an %s chip at %#x\n", + pr_info("Found an %s chip at %#x\n", val & 0x80 ? "LM79" : "LM78", (int)address); release: - release_region(address + 4, 4); - release_region(address, 4); + for (port--; port >= address; port--) + release_region(port, 1); return found; } @@ -943,21 +1011,19 @@ static int __init lm78_isa_device_add(unsigned short address) pdev = platform_device_alloc("lm78", address); if (!pdev) { err = -ENOMEM; - printk(KERN_ERR "lm78: Device allocation failed\n"); + pr_err("Device allocation failed\n"); goto exit; } err = platform_device_add_resources(pdev, &res, 1); if (err) { - printk(KERN_ERR "lm78: Device resource addition failed " - "(%d)\n", err); + pr_err("Device resource addition failed (%d)\n", err); goto exit_device_put; } err = platform_device_add(pdev); if (err) { - printk(KERN_ERR "lm78: Device addition failed (%d)\n", - err); + pr_err("Device addition failed (%d)\n", err); goto exit_device_put; } @@ -970,12 +1036,10 @@ static int __init lm78_isa_device_add(unsigned short address) return err; } -static int __init sm_lm78_init(void) +static int __init lm78_isa_register(void) { int res; - /* We register the ISA device first, so that we can skip the - * registration of an I2C interface to the same device. */ if (lm78_isa_found(isa_address)) { res = platform_driver_register(&lm78_isa_driver); if (res) @@ -987,32 +1051,64 @@ static int __init sm_lm78_init(void) goto exit_unreg_isa_driver; } - res = i2c_add_driver(&lm78_driver); - if (res) - goto exit_unreg_isa_device; - return 0; - exit_unreg_isa_device: - platform_device_unregister(pdev); exit_unreg_isa_driver: platform_driver_unregister(&lm78_isa_driver); exit: return res; } -static void __exit sm_lm78_exit(void) +static void lm78_isa_unregister(void) { if (pdev) { platform_device_unregister(pdev); platform_driver_unregister(&lm78_isa_driver); } - i2c_del_driver(&lm78_driver); } +#else /* !CONFIG_ISA */ +static int __init lm78_isa_register(void) +{ + return 0; +} +static void lm78_isa_unregister(void) +{ +} +#endif /* CONFIG_ISA */ + +static int __init sm_lm78_init(void) +{ + int res; + + /* + * We register the ISA device first, so that we can skip the + * registration of an I2C interface to the same device. + */ + res = lm78_isa_register(); + if (res) + goto exit; + + res = i2c_add_driver(&lm78_driver); + if (res) + goto exit_unreg_isa_device; + + return 0; + + exit_unreg_isa_device: + lm78_isa_unregister(); + exit: + return res; +} + +static void __exit sm_lm78_exit(void) +{ + lm78_isa_unregister(); + i2c_del_driver(&lm78_driver); +} -MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"); +MODULE_AUTHOR("Frodo Looijaard, Jean Delvare <jdelvare@suse.de>"); MODULE_DESCRIPTION("LM78/LM79 driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c index 18a0e6c5fe8..4bcd9b88294 100644 --- a/drivers/hwmon/lm80.c +++ b/drivers/hwmon/lm80.c @@ -1,8 +1,8 @@ /* * lm80.c - From lm_sensors, Linux kernel modules for hardware - * monitoring + * monitoring * Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> - * and Philip Edelbrock <phil@netroedge.com> + * and Philip Edelbrock <phil@netroedge.com> * * Ported to Linux 2.6 by Tiago Sousa <mirage@kaotik.org> * @@ -60,254 +60,384 @@ static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, #define LM80_REG_FANDIV 0x05 #define LM80_REG_RES 0x06 +#define LM96080_REG_CONV_RATE 0x07 +#define LM96080_REG_MAN_ID 0x3e +#define LM96080_REG_DEV_ID 0x3f -/* Conversions. Rounding and limit checking is only done on the TO_REG - variants. Note that you should be a bit careful with which arguments - these macros are called: arguments may be evaluated more than once. - Fixing this is just not worth it. */ -#define IN_TO_REG(val) (SENSORS_LIMIT(((val)+5)/10,0,255)) -#define IN_FROM_REG(val) ((val)*10) +/* + * Conversions. Rounding and limit checking is only done on the TO_REG + * variants. Note that you should be a bit careful with which arguments + * these macros are called: arguments may be evaluated more than once. + * Fixing this is just not worth it. + */ + +#define IN_TO_REG(val) (clamp_val(((val) + 5) / 10, 0, 255)) +#define IN_FROM_REG(val) ((val) * 10) static inline unsigned char FAN_TO_REG(unsigned rpm, unsigned div) { if (rpm == 0) return 255; - rpm = SENSORS_LIMIT(rpm, 1, 1000000); - return SENSORS_LIMIT((1350000 + rpm*div / 2) / (rpm*div), 1, 254); + rpm = clamp_val(rpm, 1, 1000000); + return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254); } -#define FAN_FROM_REG(val,div) ((val)==0?-1:\ - (val)==255?0:1350000/((div)*(val))) +#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : \ + (val) == 255 ? 0 : 1350000/((div) * (val))) -static inline long TEMP_FROM_REG(u16 temp) -{ - long res; +#define TEMP_FROM_REG(reg) ((reg) * 125 / 32) +#define TEMP_TO_REG(temp) (DIV_ROUND_CLOSEST(clamp_val((temp), \ + -128000, 127000), 1000) << 8) - temp >>= 4; - if (temp < 0x0800) - res = 625 * (long) temp; - else - res = ((long) temp - 0x01000) * 625; +#define DIV_FROM_REG(val) (1 << (val)) - return res / 10; -} +enum temp_index { + t_input = 0, + t_hot_max, + t_hot_hyst, + t_os_max, + t_os_hyst, + t_num_temp +}; -#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*1000) +static const u8 temp_regs[t_num_temp] = { + [t_input] = LM80_REG_TEMP, + [t_hot_max] = LM80_REG_TEMP_HOT_MAX, + [t_hot_hyst] = LM80_REG_TEMP_HOT_HYST, + [t_os_max] = LM80_REG_TEMP_OS_MAX, + [t_os_hyst] = LM80_REG_TEMP_OS_HYST, +}; -#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT((val)<0?\ - ((val)-500)/1000:((val)+500)/1000,0,255) +enum in_index { + i_input = 0, + i_max, + i_min, + i_num_in +}; -#define DIV_FROM_REG(val) (1 << (val)) +enum fan_index { + f_input, + f_min, + f_num_fan +}; /* * Client data (each client gets its own) */ struct lm80_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; + char error; /* !=0 if error occurred during last update */ char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ - u8 in[7]; /* Register value */ - u8 in_max[7]; /* Register value */ - u8 in_min[7]; /* Register value */ - u8 fan[2]; /* Register value */ - u8 fan_min[2]; /* Register value */ + u8 in[i_num_in][7]; /* Register value, 1st index is enum in_index */ + u8 fan[f_num_fan][2]; /* Register value, 1st index enum fan_index */ u8 fan_div[2]; /* Register encoding, shifted right */ - u16 temp; /* Register values, shifted right */ - u8 temp_hot_max; /* Register value */ - u8 temp_hot_hyst; /* Register value */ - u8 temp_os_max; /* Register value */ - u8 temp_os_hyst; /* Register value */ + s16 temp[t_num_temp]; /* Register values, normalized to 16 bit */ u16 alarms; /* Register encoding, combined */ }; -/* - * Functions declaration - */ +static int lm80_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} -static int lm80_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info); -static void lm80_init_client(struct i2c_client *client); -static int lm80_remove(struct i2c_client *client); -static struct lm80_data *lm80_update_device(struct device *dev); -static int lm80_read_value(struct i2c_client *client, u8 reg); -static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value); +static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} -/* - * Driver data (common to all clients) - */ +/* Called when we have found a new LM80 and after read errors */ +static void lm80_init_client(struct i2c_client *client) +{ + /* + * Reset all except Watchdog values and last conversion values + * This sets fan-divs to 2, among others. This makes most other + * initializations unnecessary + */ + lm80_write_value(client, LM80_REG_CONFIG, 0x80); + /* Set 11-bit temperature resolution */ + lm80_write_value(client, LM80_REG_RES, 0x08); -static const struct i2c_device_id lm80_id[] = { - { "lm80", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, lm80_id); + /* Start monitoring */ + lm80_write_value(client, LM80_REG_CONFIG, 0x01); +} -static struct i2c_driver lm80_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "lm80", - }, - .probe = lm80_probe, - .remove = lm80_remove, - .id_table = lm80_id, - .detect = lm80_detect, - .address_list = normal_i2c, -}; +static struct lm80_data *lm80_update_device(struct device *dev) +{ + struct lm80_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int i; + int rv; + int prev_rv; + struct lm80_data *ret = data; + + mutex_lock(&data->update_lock); + + if (data->error) + lm80_init_client(client); + + if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) { + dev_dbg(dev, "Starting lm80 update\n"); + for (i = 0; i <= 6; i++) { + rv = lm80_read_value(client, LM80_REG_IN(i)); + if (rv < 0) + goto abort; + data->in[i_input][i] = rv; + + rv = lm80_read_value(client, LM80_REG_IN_MIN(i)); + if (rv < 0) + goto abort; + data->in[i_min][i] = rv; + + rv = lm80_read_value(client, LM80_REG_IN_MAX(i)); + if (rv < 0) + goto abort; + data->in[i_max][i] = rv; + } + + rv = lm80_read_value(client, LM80_REG_FAN1); + if (rv < 0) + goto abort; + data->fan[f_input][0] = rv; + + rv = lm80_read_value(client, LM80_REG_FAN_MIN(1)); + if (rv < 0) + goto abort; + data->fan[f_min][0] = rv; + + rv = lm80_read_value(client, LM80_REG_FAN2); + if (rv < 0) + goto abort; + data->fan[f_input][1] = rv; + + rv = lm80_read_value(client, LM80_REG_FAN_MIN(2)); + if (rv < 0) + goto abort; + data->fan[f_min][1] = rv; + + prev_rv = rv = lm80_read_value(client, LM80_REG_TEMP); + if (rv < 0) + goto abort; + rv = lm80_read_value(client, LM80_REG_RES); + if (rv < 0) + goto abort; + data->temp[t_input] = (prev_rv << 8) | (rv & 0xf0); + + for (i = t_input + 1; i < t_num_temp; i++) { + rv = lm80_read_value(client, temp_regs[i]); + if (rv < 0) + goto abort; + data->temp[i] = rv << 8; + } + + rv = lm80_read_value(client, LM80_REG_FANDIV); + if (rv < 0) + goto abort; + data->fan_div[0] = (rv >> 2) & 0x03; + data->fan_div[1] = (rv >> 4) & 0x03; + + prev_rv = rv = lm80_read_value(client, LM80_REG_ALARM1); + if (rv < 0) + goto abort; + rv = lm80_read_value(client, LM80_REG_ALARM2); + if (rv < 0) + goto abort; + data->alarms = prev_rv + (rv << 8); + + data->last_updated = jiffies; + data->valid = 1; + data->error = 0; + } + goto done; + +abort: + ret = ERR_PTR(rv); + data->valid = 0; + data->error = 1; + +done: + mutex_unlock(&data->update_lock); + + return ret; +} /* * Sysfs stuff */ -#define show_in(suffix, value) \ -static ssize_t show_in_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - int nr = to_sensor_dev_attr(attr)->index; \ - struct lm80_data *data = lm80_update_device(dev); \ - return sprintf(buf, "%d\n", IN_FROM_REG(data->value[nr])); \ +static ssize_t show_in(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm80_data *data = lm80_update_device(dev); + int index = to_sensor_dev_attr_2(attr)->index; + int nr = to_sensor_dev_attr_2(attr)->nr; + + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr][index])); } -show_in(min, in_min) -show_in(max, in_max) -show_in(input, in) - -#define set_in(suffix, value, reg) \ -static ssize_t set_in_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - int nr = to_sensor_dev_attr(attr)->index; \ - struct i2c_client *client = to_i2c_client(dev); \ - struct lm80_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - mutex_lock(&data->update_lock);\ - data->value[nr] = IN_TO_REG(val); \ - lm80_write_value(client, reg(nr), data->value[nr]); \ - mutex_unlock(&data->update_lock);\ - return count; \ + +static ssize_t set_in(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lm80_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int index = to_sensor_dev_attr_2(attr)->index; + int nr = to_sensor_dev_attr_2(attr)->nr; + long val; + u8 reg; + int err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + reg = nr == i_min ? LM80_REG_IN_MIN(index) : LM80_REG_IN_MAX(index); + + mutex_lock(&data->update_lock); + data->in[nr][index] = IN_TO_REG(val); + lm80_write_value(client, reg, data->in[nr][index]); + mutex_unlock(&data->update_lock); + return count; } -set_in(min, in_min, LM80_REG_IN_MIN) -set_in(max, in_max, LM80_REG_IN_MAX) - -#define show_fan(suffix, value) \ -static ssize_t show_fan_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - int nr = to_sensor_dev_attr(attr)->index; \ - struct lm80_data *data = lm80_update_device(dev); \ - return sprintf(buf, "%d\n", FAN_FROM_REG(data->value[nr], \ - DIV_FROM_REG(data->fan_div[nr]))); \ + +static ssize_t show_fan(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int index = to_sensor_dev_attr_2(attr)->index; + int nr = to_sensor_dev_attr_2(attr)->nr; + struct lm80_data *data = lm80_update_device(dev); + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr][index], + DIV_FROM_REG(data->fan_div[index]))); } -show_fan(min, fan_min) -show_fan(input, fan) static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr, char *buf) { int nr = to_sensor_dev_attr(attr)->index; struct lm80_data *data = lm80_update_device(dev); + if (IS_ERR(data)) + return PTR_ERR(data); return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr])); } static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int nr = to_sensor_dev_attr(attr)->index; - struct i2c_client *client = to_i2c_client(dev); - struct lm80_data *data = i2c_get_clientdata(client); - long val = simple_strtoul(buf, NULL, 10); + int index = to_sensor_dev_attr_2(attr)->index; + int nr = to_sensor_dev_attr_2(attr)->nr; + struct lm80_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + int err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; mutex_lock(&data->update_lock); - data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); - lm80_write_value(client, LM80_REG_FAN_MIN(nr + 1), data->fan_min[nr]); + data->fan[nr][index] = FAN_TO_REG(val, + DIV_FROM_REG(data->fan_div[index])); + lm80_write_value(client, LM80_REG_FAN_MIN(index + 1), + data->fan[nr][index]); mutex_unlock(&data->update_lock); return count; } -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan divisor. This follows the principle of - least surprise; the user doesn't expect the fan minimum to change just - because the divisor changed. */ +/* + * Note: we save and restore the fan minimum here, because its value is + * determined in part by the fan divisor. This follows the principle of + * least surprise; the user doesn't expect the fan minimum to change just + * because the divisor changed. + */ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int nr = to_sensor_dev_attr(attr)->index; - struct i2c_client *client = to_i2c_client(dev); - struct lm80_data *data = i2c_get_clientdata(client); - unsigned long min, val = simple_strtoul(buf, NULL, 10); + struct lm80_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long min, val; u8 reg; + int err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; /* Save fan_min */ mutex_lock(&data->update_lock); - min = FAN_FROM_REG(data->fan_min[nr], + min = FAN_FROM_REG(data->fan[f_min][nr], DIV_FROM_REG(data->fan_div[nr])); switch (val) { - case 1: data->fan_div[nr] = 0; break; - case 2: data->fan_div[nr] = 1; break; - case 4: data->fan_div[nr] = 2; break; - case 8: data->fan_div[nr] = 3; break; + case 1: + data->fan_div[nr] = 0; + break; + case 2: + data->fan_div[nr] = 1; + break; + case 4: + data->fan_div[nr] = 2; + break; + case 8: + data->fan_div[nr] = 3; + break; default: - dev_err(&client->dev, "fan_div value %ld not " - "supported. Choose one of 1, 2, 4 or 8!\n", val); + dev_err(dev, + "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n", + val); mutex_unlock(&data->update_lock); return -EINVAL; } - reg = (lm80_read_value(client, LM80_REG_FANDIV) & ~(3 << (2 * (nr + 1)))) - | (data->fan_div[nr] << (2 * (nr + 1))); + reg = (lm80_read_value(client, LM80_REG_FANDIV) & + ~(3 << (2 * (nr + 1)))) | (data->fan_div[nr] << (2 * (nr + 1))); lm80_write_value(client, LM80_REG_FANDIV, reg); /* Restore fan_min */ - data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); - lm80_write_value(client, LM80_REG_FAN_MIN(nr + 1), data->fan_min[nr]); + data->fan[f_min][nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); + lm80_write_value(client, LM80_REG_FAN_MIN(nr + 1), + data->fan[f_min][nr]); mutex_unlock(&data->update_lock); return count; } -static ssize_t show_temp_input1(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, + char *buf) { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm80_data *data = lm80_update_device(dev); - return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp)); + if (IS_ERR(data)) + return PTR_ERR(data); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])); } -#define show_temp(suffix, value) \ -static ssize_t show_temp_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm80_data *data = lm80_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_LIMIT_FROM_REG(data->value)); \ -} -show_temp(hot_max, temp_hot_max); -show_temp(hot_hyst, temp_hot_hyst); -show_temp(os_max, temp_os_max); -show_temp(os_hyst, temp_os_hyst); - -#define set_temp(suffix, value, reg) \ -static ssize_t set_temp_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct lm80_data *data = i2c_get_clientdata(client); \ - long val = simple_strtoul(buf, NULL, 10); \ - \ - mutex_lock(&data->update_lock); \ - data->value = TEMP_LIMIT_TO_REG(val); \ - lm80_write_value(client, reg, data->value); \ - mutex_unlock(&data->update_lock); \ - return count; \ +static ssize_t set_temp(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm80_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int nr = attr->index; + long val; + int err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + mutex_lock(&data->update_lock); + data->temp[nr] = TEMP_TO_REG(val); + lm80_write_value(client, temp_regs[nr], data->temp[nr] >> 8); + mutex_unlock(&data->update_lock); + return count; } -set_temp(hot_max, temp_hot_max, LM80_REG_TEMP_HOT_MAX); -set_temp(hot_hyst, temp_hot_hyst, LM80_REG_TEMP_HOT_HYST); -set_temp(os_max, temp_os_max, LM80_REG_TEMP_OS_MAX); -set_temp(os_hyst, temp_os_hyst, LM80_REG_TEMP_OS_HYST); static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) { struct lm80_data *data = lm80_update_device(dev); + if (IS_ERR(data)) + return PTR_ERR(data); return sprintf(buf, "%u\n", data->alarms); } @@ -316,63 +446,65 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, { int bitnr = to_sensor_dev_attr(attr)->index; struct lm80_data *data = lm80_update_device(dev); + if (IS_ERR(data)) + return PTR_ERR(data); return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); } -static SENSOR_DEVICE_ATTR(in0_min, S_IWUSR | S_IRUGO, - show_in_min, set_in_min, 0); -static SENSOR_DEVICE_ATTR(in1_min, S_IWUSR | S_IRUGO, - show_in_min, set_in_min, 1); -static SENSOR_DEVICE_ATTR(in2_min, S_IWUSR | S_IRUGO, - show_in_min, set_in_min, 2); -static SENSOR_DEVICE_ATTR(in3_min, S_IWUSR | S_IRUGO, - show_in_min, set_in_min, 3); -static SENSOR_DEVICE_ATTR(in4_min, S_IWUSR | S_IRUGO, - show_in_min, set_in_min, 4); -static SENSOR_DEVICE_ATTR(in5_min, S_IWUSR | S_IRUGO, - show_in_min, set_in_min, 5); -static SENSOR_DEVICE_ATTR(in6_min, S_IWUSR | S_IRUGO, - show_in_min, set_in_min, 6); -static SENSOR_DEVICE_ATTR(in0_max, S_IWUSR | S_IRUGO, - show_in_max, set_in_max, 0); -static SENSOR_DEVICE_ATTR(in1_max, S_IWUSR | S_IRUGO, - show_in_max, set_in_max, 1); -static SENSOR_DEVICE_ATTR(in2_max, S_IWUSR | S_IRUGO, - show_in_max, set_in_max, 2); -static SENSOR_DEVICE_ATTR(in3_max, S_IWUSR | S_IRUGO, - show_in_max, set_in_max, 3); -static SENSOR_DEVICE_ATTR(in4_max, S_IWUSR | S_IRUGO, - show_in_max, set_in_max, 4); -static SENSOR_DEVICE_ATTR(in5_max, S_IWUSR | S_IRUGO, - show_in_max, set_in_max, 5); -static SENSOR_DEVICE_ATTR(in6_max, S_IWUSR | S_IRUGO, - show_in_max, set_in_max, 6); -static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in_input, NULL, 0); -static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in_input, NULL, 1); -static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in_input, NULL, 2); -static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in_input, NULL, 3); -static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_in_input, NULL, 4); -static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_in_input, NULL, 5); -static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_in_input, NULL, 6); -static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, - show_fan_min, set_fan_min, 0); -static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, - show_fan_min, set_fan_min, 1); -static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input, NULL, 0); -static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input, NULL, 1); +static SENSOR_DEVICE_ATTR_2(in0_min, S_IWUSR | S_IRUGO, + show_in, set_in, i_min, 0); +static SENSOR_DEVICE_ATTR_2(in1_min, S_IWUSR | S_IRUGO, + show_in, set_in, i_min, 1); +static SENSOR_DEVICE_ATTR_2(in2_min, S_IWUSR | S_IRUGO, + show_in, set_in, i_min, 2); +static SENSOR_DEVICE_ATTR_2(in3_min, S_IWUSR | S_IRUGO, + show_in, set_in, i_min, 3); +static SENSOR_DEVICE_ATTR_2(in4_min, S_IWUSR | S_IRUGO, + show_in, set_in, i_min, 4); +static SENSOR_DEVICE_ATTR_2(in5_min, S_IWUSR | S_IRUGO, + show_in, set_in, i_min, 5); +static SENSOR_DEVICE_ATTR_2(in6_min, S_IWUSR | S_IRUGO, + show_in, set_in, i_min, 6); +static SENSOR_DEVICE_ATTR_2(in0_max, S_IWUSR | S_IRUGO, + show_in, set_in, i_max, 0); +static SENSOR_DEVICE_ATTR_2(in1_max, S_IWUSR | S_IRUGO, + show_in, set_in, i_max, 1); +static SENSOR_DEVICE_ATTR_2(in2_max, S_IWUSR | S_IRUGO, + show_in, set_in, i_max, 2); +static SENSOR_DEVICE_ATTR_2(in3_max, S_IWUSR | S_IRUGO, + show_in, set_in, i_max, 3); +static SENSOR_DEVICE_ATTR_2(in4_max, S_IWUSR | S_IRUGO, + show_in, set_in, i_max, 4); +static SENSOR_DEVICE_ATTR_2(in5_max, S_IWUSR | S_IRUGO, + show_in, set_in, i_max, 5); +static SENSOR_DEVICE_ATTR_2(in6_max, S_IWUSR | S_IRUGO, + show_in, set_in, i_max, 6); +static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in, NULL, i_input, 0); +static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in, NULL, i_input, 1); +static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in, NULL, i_input, 2); +static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in, NULL, i_input, 3); +static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in, NULL, i_input, 4); +static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, show_in, NULL, i_input, 5); +static SENSOR_DEVICE_ATTR_2(in6_input, S_IRUGO, show_in, NULL, i_input, 6); +static SENSOR_DEVICE_ATTR_2(fan1_min, S_IWUSR | S_IRUGO, + show_fan, set_fan_min, f_min, 0); +static SENSOR_DEVICE_ATTR_2(fan2_min, S_IWUSR | S_IRUGO, + show_fan, set_fan_min, f_min, 1); +static SENSOR_DEVICE_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, f_input, 0); +static SENSOR_DEVICE_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, f_input, 1); static SENSOR_DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, show_fan_div, set_fan_div, 0); static SENSOR_DEVICE_ATTR(fan2_div, S_IWUSR | S_IRUGO, show_fan_div, set_fan_div, 1); -static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL); -static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_hot_max, - set_temp_hot_max); -static DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_hot_hyst, - set_temp_hot_hyst); -static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp_os_max, - set_temp_os_max); -static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp_os_hyst, - set_temp_os_hyst); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, t_input); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp, + set_temp, t_hot_max); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp, + set_temp, t_hot_hyst); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp, + set_temp, t_os_max); +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp, + set_temp, t_os_hyst); static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); @@ -390,7 +522,7 @@ static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 13); * Real code */ -static struct attribute *lm80_attributes[] = { +static struct attribute *lm80_attrs[] = { &sensor_dev_attr_in0_min.dev_attr.attr, &sensor_dev_attr_in1_min.dev_attr.attr, &sensor_dev_attr_in2_min.dev_attr.attr, @@ -418,11 +550,11 @@ static struct attribute *lm80_attributes[] = { &sensor_dev_attr_fan2_input.dev_attr.attr, &sensor_dev_attr_fan1_div.dev_attr.attr, &sensor_dev_attr_fan2_div.dev_attr.attr, - &dev_attr_temp1_input.attr, - &dev_attr_temp1_max.attr, - &dev_attr_temp1_max_hyst.attr, - &dev_attr_temp1_crit.attr, - &dev_attr_temp1_crit_hyst.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, &dev_attr_alarms.attr, &sensor_dev_attr_in0_alarm.dev_attr.attr, &sensor_dev_attr_in1_alarm.dev_attr.attr, @@ -437,32 +569,50 @@ static struct attribute *lm80_attributes[] = { &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, NULL }; - -static const struct attribute_group lm80_group = { - .attrs = lm80_attributes, -}; +ATTRIBUTE_GROUPS(lm80); /* Return 0 if detection is successful, -ENODEV otherwise */ static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; - int i, cur; + int i, cur, man_id, dev_id; + const char *name = NULL; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* Now, we do the remaining detection. It is lousy. */ - if (lm80_read_value(client, LM80_REG_ALARM2) & 0xc0) + /* First check for unused bits, common to both chip types */ + if ((lm80_read_value(client, LM80_REG_ALARM2) & 0xc0) + || (lm80_read_value(client, LM80_REG_CONFIG) & 0x80)) return -ENODEV; - for (i = 0x2a; i <= 0x3d; i++) { - cur = i2c_smbus_read_byte_data(client, i); - if ((i2c_smbus_read_byte_data(client, i + 0x40) != cur) - || (i2c_smbus_read_byte_data(client, i + 0x80) != cur) - || (i2c_smbus_read_byte_data(client, i + 0xc0) != cur)) - return -ENODEV; + + /* + * The LM96080 has manufacturer and stepping/die rev registers so we + * can just check that. The LM80 does not have such registers so we + * have to use a more expensive trick. + */ + man_id = lm80_read_value(client, LM96080_REG_MAN_ID); + dev_id = lm80_read_value(client, LM96080_REG_DEV_ID); + if (man_id == 0x01 && dev_id == 0x08) { + /* Check more unused bits for confirmation */ + if (lm80_read_value(client, LM96080_REG_CONV_RATE) & 0xfe) + return -ENODEV; + + name = "lm96080"; + } else { + /* Check 6-bit addressing */ + for (i = 0x2a; i <= 0x3d; i++) { + cur = i2c_smbus_read_byte_data(client, i); + if ((i2c_smbus_read_byte_data(client, i + 0x40) != cur) + || (i2c_smbus_read_byte_data(client, i + 0x80) != cur) + || (i2c_smbus_read_byte_data(client, i + 0xc0) != cur)) + return -ENODEV; + } + + name = "lm80"; } - strlcpy(info->type, "lm80", I2C_NAME_SIZE); + strlcpy(info->type, name, I2C_NAME_SIZE); return 0; } @@ -470,145 +620,55 @@ static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info) static int lm80_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; + struct device *hwmon_dev; struct lm80_data *data; - int err; - data = kzalloc(sizeof(struct lm80_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(dev, sizeof(struct lm80_data), GFP_KERNEL); + if (!data) + return -ENOMEM; - i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->update_lock); /* Initialize the LM80 chip */ lm80_init_client(client); /* A few vars need to be filled upon startup */ - data->fan_min[0] = lm80_read_value(client, LM80_REG_FAN_MIN(1)); - data->fan_min[1] = lm80_read_value(client, LM80_REG_FAN_MIN(2)); + data->fan[f_min][0] = lm80_read_value(client, LM80_REG_FAN_MIN(1)); + data->fan[f_min][1] = lm80_read_value(client, LM80_REG_FAN_MIN(2)); - /* Register sysfs hooks */ - if ((err = sysfs_create_group(&client->dev.kobj, &lm80_group))) - goto error_free; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, lm80_groups); - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto error_remove; - } - - return 0; - -error_remove: - sysfs_remove_group(&client->dev.kobj, &lm80_group); -error_free: - kfree(data); -exit: - return err; + return PTR_ERR_OR_ZERO(hwmon_dev); } -static int lm80_remove(struct i2c_client *client) -{ - struct lm80_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &lm80_group); - - kfree(data); - return 0; -} - -static int lm80_read_value(struct i2c_client *client, u8 reg) -{ - return i2c_smbus_read_byte_data(client, reg); -} - -static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - -/* Called when we have found a new LM80. */ -static void lm80_init_client(struct i2c_client *client) -{ - /* Reset all except Watchdog values and last conversion values - This sets fan-divs to 2, among others. This makes most other - initializations unnecessary */ - lm80_write_value(client, LM80_REG_CONFIG, 0x80); - /* Set 11-bit temperature resolution */ - lm80_write_value(client, LM80_REG_RES, 0x08); - - /* Start monitoring */ - lm80_write_value(client, LM80_REG_CONFIG, 0x01); -} - -static struct lm80_data *lm80_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm80_data *data = i2c_get_clientdata(client); - int i; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) { - dev_dbg(&client->dev, "Starting lm80 update\n"); - for (i = 0; i <= 6; i++) { - data->in[i] = - lm80_read_value(client, LM80_REG_IN(i)); - data->in_min[i] = - lm80_read_value(client, LM80_REG_IN_MIN(i)); - data->in_max[i] = - lm80_read_value(client, LM80_REG_IN_MAX(i)); - } - data->fan[0] = lm80_read_value(client, LM80_REG_FAN1); - data->fan_min[0] = - lm80_read_value(client, LM80_REG_FAN_MIN(1)); - data->fan[1] = lm80_read_value(client, LM80_REG_FAN2); - data->fan_min[1] = - lm80_read_value(client, LM80_REG_FAN_MIN(2)); - - data->temp = - (lm80_read_value(client, LM80_REG_TEMP) << 8) | - (lm80_read_value(client, LM80_REG_RES) & 0xf0); - data->temp_os_max = - lm80_read_value(client, LM80_REG_TEMP_OS_MAX); - data->temp_os_hyst = - lm80_read_value(client, LM80_REG_TEMP_OS_HYST); - data->temp_hot_max = - lm80_read_value(client, LM80_REG_TEMP_HOT_MAX); - data->temp_hot_hyst = - lm80_read_value(client, LM80_REG_TEMP_HOT_HYST); - - i = lm80_read_value(client, LM80_REG_FANDIV); - data->fan_div[0] = (i >> 2) & 0x03; - data->fan_div[1] = (i >> 4) & 0x03; - data->alarms = lm80_read_value(client, LM80_REG_ALARM1) + - (lm80_read_value(client, LM80_REG_ALARM2) << 8); - data->last_updated = jiffies; - data->valid = 1; - } - - mutex_unlock(&data->update_lock); +/* + * Driver data (common to all clients) + */ - return data; -} +static const struct i2c_device_id lm80_id[] = { + { "lm80", 0 }, + { "lm96080", 1 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm80_id); -static int __init sensors_lm80_init(void) -{ - return i2c_add_driver(&lm80_driver); -} +static struct i2c_driver lm80_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "lm80", + }, + .probe = lm80_probe, + .id_table = lm80_id, + .detect = lm80_detect, + .address_list = normal_i2c, +}; -static void __exit sensors_lm80_exit(void) -{ - i2c_del_driver(&lm80_driver); -} +module_i2c_driver(lm80_driver); MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and " "Philip Edelbrock <phil@netroedge.com>"); MODULE_DESCRIPTION("LM80 driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_lm80_init); -module_exit(sensors_lm80_exit); diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c index 8290476aee4..9e4d0e1d3c4 100644 --- a/drivers/hwmon/lm83.c +++ b/drivers/hwmon/lm83.c @@ -1,7 +1,7 @@ /* * lm83.c - Part of lm_sensors, Linux kernel modules for hardware * monitoring - * Copyright (C) 2003-2009 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2003-2009 Jean Delvare <jdelvare@suse.de> * * Heavily inspired from the lm78, lm75 and adm1021 drivers. The LM83 is * a sensor chip made by National Semiconductor. It reports up to four @@ -25,10 +25,6 @@ * 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> @@ -111,45 +107,12 @@ static const u8 LM83_REG_W_HIGH[] = { }; /* - * Functions declaration - */ - -static int lm83_detect(struct i2c_client *new_client, - struct i2c_board_info *info); -static int lm83_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int lm83_remove(struct i2c_client *client); -static struct lm83_data *lm83_update_device(struct device *dev); - -/* - * Driver data (common to all clients) - */ - -static const struct i2c_device_id lm83_id[] = { - { "lm83", lm83 }, - { "lm82", lm82 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, lm83_id); - -static struct i2c_driver lm83_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "lm83", - }, - .probe = lm83_probe, - .remove = lm83_remove, - .id_table = lm83_id, - .detect = lm83_detect, - .address_list = normal_i2c, -}; - -/* * Client data (each client gets its own) */ struct lm83_data { - struct device *hwmon_dev; + struct i2c_client *client; + const struct attribute_group *groups[3]; struct mutex update_lock; char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ @@ -161,6 +124,36 @@ struct lm83_data { u16 alarms; /* bitvector, combined */ }; +static struct lm83_data *lm83_update_device(struct device *dev) +{ + struct lm83_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { + int nr; + + dev_dbg(&client->dev, "Updating lm83 data.\n"); + for (nr = 0; nr < 9; nr++) { + data->temp[nr] = + i2c_smbus_read_byte_data(client, + LM83_REG_R_TEMP[nr]); + } + data->alarms = + i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS1) + + (i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS2) + << 8); + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + /* * Sysfs stuff */ @@ -177,10 +170,15 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct lm83_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + struct lm83_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + long val; int nr = attr->index; + int err; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; mutex_lock(&data->update_lock); data->temp[nr] = TEMP_TO_REG(val); @@ -335,17 +333,15 @@ static int lm83_detect(struct i2c_client *new_client, static int lm83_probe(struct i2c_client *new_client, const struct i2c_device_id *id) { + struct device *hwmon_dev; struct lm83_data *data; - int err; - data = kzalloc(sizeof(struct lm83_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&new_client->dev, sizeof(struct lm83_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; - i2c_set_clientdata(new_client, data); - data->valid = 0; + data->client = new_client; mutex_init(&data->update_lock); /* @@ -354,88 +350,40 @@ static int lm83_probe(struct i2c_client *new_client, * at the same register as the LM83 temp3 entry - so we * declare 1 and 3 common, and then 2 and 4 only for the LM83. */ - - if ((err = sysfs_create_group(&new_client->dev.kobj, &lm83_group))) - goto exit_free; - - if (id->driver_data == lm83) { - if ((err = sysfs_create_group(&new_client->dev.kobj, - &lm83_group_opt))) - goto exit_remove_files; - } - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - -exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &lm83_group); - sysfs_remove_group(&new_client->dev.kobj, &lm83_group_opt); -exit_free: - kfree(data); -exit: - return err; -} - -static int lm83_remove(struct i2c_client *client) -{ - struct lm83_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &lm83_group); - sysfs_remove_group(&client->dev.kobj, &lm83_group_opt); - - kfree(data); - return 0; + data->groups[0] = &lm83_group; + if (id->driver_data == lm83) + data->groups[1] = &lm83_group_opt; + + hwmon_dev = devm_hwmon_device_register_with_groups(&new_client->dev, + new_client->name, + data, data->groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } -static struct lm83_data *lm83_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm83_data *data = i2c_get_clientdata(client); - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { - int nr; - - dev_dbg(&client->dev, "Updating lm83 data.\n"); - for (nr = 0; nr < 9; nr++) { - data->temp[nr] = - i2c_smbus_read_byte_data(client, - LM83_REG_R_TEMP[nr]); - } - data->alarms = - i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS1) - + (i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS2) - << 8); - - data->last_updated = jiffies; - data->valid = 1; - } - - mutex_unlock(&data->update_lock); +/* + * Driver data (common to all clients) + */ - return data; -} +static const struct i2c_device_id lm83_id[] = { + { "lm83", lm83 }, + { "lm82", lm82 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm83_id); -static int __init sensors_lm83_init(void) -{ - return i2c_add_driver(&lm83_driver); -} +static struct i2c_driver lm83_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "lm83", + }, + .probe = lm83_probe, + .id_table = lm83_id, + .detect = lm83_detect, + .address_list = normal_i2c, +}; -static void __exit sensors_lm83_exit(void) -{ - i2c_del_driver(&lm83_driver); -} +module_i2c_driver(lm83_driver); -MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); +MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); MODULE_DESCRIPTION("LM83 driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_lm83_init); -module_exit(sensors_lm83_exit); diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index b3841a61559..b0129a54e1a 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -1,28 +1,28 @@ /* - lm85.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> - Copyright (c) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com> - Copyright (c) 2003 Margit Schubert-While <margitsw@t-online.de> - Copyright (c) 2004 Justin Thiessen <jthiessen@penguincomputing.com> - Copyright (C) 2007--2009 Jean Delvare <khali@linux-fr.org> - - Chip details at <http://www.national.com/ds/LM/LM85.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. -*/ + * lm85.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> + * Copyright (c) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com> + * Copyright (c) 2003 Margit Schubert-While <margitsw@t-online.de> + * Copyright (c) 2004 Justin Thiessen <jthiessen@penguincomputing.com> + * Copyright (C) 2007--2014 Jean Delvare <jdelvare@suse.de> + * + * Chip details at <http://www.national.com/ds/LM/LM85.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/module.h> #include <linux/init.h> @@ -39,92 +39,96 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; enum chips { - any_chip, lm85b, lm85c, + lm85, adm1027, adt7463, adt7468, - emc6d100, emc6d102 + emc6d100, emc6d102, emc6d103, emc6d103s }; /* The LM85 registers */ -#define LM85_REG_IN(nr) (0x20 + (nr)) -#define LM85_REG_IN_MIN(nr) (0x44 + (nr) * 2) -#define LM85_REG_IN_MAX(nr) (0x45 + (nr) * 2) +#define LM85_REG_IN(nr) (0x20 + (nr)) +#define LM85_REG_IN_MIN(nr) (0x44 + (nr) * 2) +#define LM85_REG_IN_MAX(nr) (0x45 + (nr) * 2) -#define LM85_REG_TEMP(nr) (0x25 + (nr)) -#define LM85_REG_TEMP_MIN(nr) (0x4e + (nr) * 2) -#define LM85_REG_TEMP_MAX(nr) (0x4f + (nr) * 2) +#define LM85_REG_TEMP(nr) (0x25 + (nr)) +#define LM85_REG_TEMP_MIN(nr) (0x4e + (nr) * 2) +#define LM85_REG_TEMP_MAX(nr) (0x4f + (nr) * 2) /* Fan speeds are LSB, MSB (2 bytes) */ -#define LM85_REG_FAN(nr) (0x28 + (nr) * 2) -#define LM85_REG_FAN_MIN(nr) (0x54 + (nr) * 2) +#define LM85_REG_FAN(nr) (0x28 + (nr) * 2) +#define LM85_REG_FAN_MIN(nr) (0x54 + (nr) * 2) -#define LM85_REG_PWM(nr) (0x30 + (nr)) +#define LM85_REG_PWM(nr) (0x30 + (nr)) -#define LM85_REG_COMPANY 0x3e -#define LM85_REG_VERSTEP 0x3f +#define LM85_REG_COMPANY 0x3e +#define LM85_REG_VERSTEP 0x3f -#define ADT7468_REG_CFG5 0x7c -#define ADT7468_OFF64 0x01 -#define IS_ADT7468_OFF64(data) \ +#define ADT7468_REG_CFG5 0x7c +#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 -#define LM85_COMPANY_ANALOG_DEV 0x41 -#define LM85_COMPANY_SMSC 0x5c -#define LM85_VERSTEP_VMASK 0xf0 -#define LM85_VERSTEP_GENERIC 0x60 -#define LM85_VERSTEP_GENERIC2 0x70 -#define LM85_VERSTEP_LM85C 0x60 -#define LM85_VERSTEP_LM85B 0x62 -#define LM85_VERSTEP_LM96000_1 0x68 -#define LM85_VERSTEP_LM96000_2 0x69 -#define LM85_VERSTEP_ADM1027 0x60 -#define LM85_VERSTEP_ADT7463 0x62 -#define LM85_VERSTEP_ADT7463C 0x6A -#define LM85_VERSTEP_ADT7468_1 0x71 -#define LM85_VERSTEP_ADT7468_2 0x72 -#define LM85_VERSTEP_EMC6D100_A0 0x60 -#define LM85_VERSTEP_EMC6D100_A1 0x61 -#define LM85_VERSTEP_EMC6D102 0x65 - -#define LM85_REG_CONFIG 0x40 - -#define LM85_REG_ALARM1 0x41 -#define LM85_REG_ALARM2 0x42 - -#define LM85_REG_VID 0x43 +#define LM85_COMPANY_NATIONAL 0x01 +#define LM85_COMPANY_ANALOG_DEV 0x41 +#define LM85_COMPANY_SMSC 0x5c +#define LM85_VERSTEP_LM85C 0x60 +#define LM85_VERSTEP_LM85B 0x62 +#define LM85_VERSTEP_LM96000_1 0x68 +#define LM85_VERSTEP_LM96000_2 0x69 +#define LM85_VERSTEP_ADM1027 0x60 +#define LM85_VERSTEP_ADT7463 0x62 +#define LM85_VERSTEP_ADT7463C 0x6A +#define LM85_VERSTEP_ADT7468_1 0x71 +#define LM85_VERSTEP_ADT7468_2 0x72 +#define LM85_VERSTEP_EMC6D100_A0 0x60 +#define LM85_VERSTEP_EMC6D100_A1 0x61 +#define LM85_VERSTEP_EMC6D102 0x65 +#define LM85_VERSTEP_EMC6D103_A0 0x68 +#define LM85_VERSTEP_EMC6D103_A1 0x69 +#define LM85_VERSTEP_EMC6D103S 0x6A /* Also known as EMC6D103:A2 */ + +#define LM85_REG_CONFIG 0x40 + +#define LM85_REG_ALARM1 0x41 +#define LM85_REG_ALARM2 0x42 + +#define LM85_REG_VID 0x43 /* Automated FAN control */ -#define LM85_REG_AFAN_CONFIG(nr) (0x5c + (nr)) -#define LM85_REG_AFAN_RANGE(nr) (0x5f + (nr)) -#define LM85_REG_AFAN_SPIKE1 0x62 -#define LM85_REG_AFAN_MINPWM(nr) (0x64 + (nr)) -#define LM85_REG_AFAN_LIMIT(nr) (0x67 + (nr)) -#define LM85_REG_AFAN_CRITICAL(nr) (0x6a + (nr)) -#define LM85_REG_AFAN_HYST1 0x6d -#define LM85_REG_AFAN_HYST2 0x6e - -#define ADM1027_REG_EXTEND_ADC1 0x76 -#define ADM1027_REG_EXTEND_ADC2 0x77 +#define LM85_REG_AFAN_CONFIG(nr) (0x5c + (nr)) +#define LM85_REG_AFAN_RANGE(nr) (0x5f + (nr)) +#define LM85_REG_AFAN_SPIKE1 0x62 +#define LM85_REG_AFAN_MINPWM(nr) (0x64 + (nr)) +#define LM85_REG_AFAN_LIMIT(nr) (0x67 + (nr)) +#define LM85_REG_AFAN_CRITICAL(nr) (0x6a + (nr)) +#define LM85_REG_AFAN_HYST1 0x6d +#define LM85_REG_AFAN_HYST2 0x6e + +#define ADM1027_REG_EXTEND_ADC1 0x76 +#define ADM1027_REG_EXTEND_ADC2 0x77 #define EMC6D100_REG_ALARM3 0x7d /* IN5, IN6 and IN7 */ -#define EMC6D100_REG_IN(nr) (0x70 + ((nr) - 5)) -#define EMC6D100_REG_IN_MIN(nr) (0x73 + ((nr) - 5) * 2) -#define EMC6D100_REG_IN_MAX(nr) (0x74 + ((nr) - 5) * 2) -#define EMC6D102_REG_EXTEND_ADC1 0x85 -#define EMC6D102_REG_EXTEND_ADC2 0x86 -#define EMC6D102_REG_EXTEND_ADC3 0x87 -#define EMC6D102_REG_EXTEND_ADC4 0x88 - - -/* Conversions. Rounding and limit checking is only done on the TO_REG - variants. Note that you should be a bit careful with which arguments - these macros are called: arguments may be evaluated more than once. +#define EMC6D100_REG_IN(nr) (0x70 + ((nr) - 5)) +#define EMC6D100_REG_IN_MIN(nr) (0x73 + ((nr) - 5) * 2) +#define EMC6D100_REG_IN_MAX(nr) (0x74 + ((nr) - 5) * 2) +#define EMC6D102_REG_EXTEND_ADC1 0x85 +#define EMC6D102_REG_EXTEND_ADC2 0x86 +#define EMC6D102_REG_EXTEND_ADC3 0x87 +#define EMC6D102_REG_EXTEND_ADC4 0x88 + + +/* + * Conversions. Rounding and limit checking is only done on the TO_REG + * variants. Note that you should be a bit careful with which arguments + * these macros are called: arguments may be evaluated more than once. */ -/* IN are scaled acording to built-in resistors */ +/* IN are scaled according to built-in resistors */ static const int lm85_scaling[] = { /* .001 Volts */ 2500, 2250, 3300, 5000, 12000, 3300, 1500, 1800 /*EMC6D100*/ @@ -132,7 +136,7 @@ static const int lm85_scaling[] = { /* .001 Volts */ #define SCALE(val, from, to) (((val) * (to) + ((from) / 2)) / (from)) #define INS_TO_REG(n, val) \ - SENSORS_LIMIT(SCALE(val, lm85_scaling[n], 192), 0, 255) + clamp_val(SCALE(val, lm85_scaling[n], 192), 0, 255) #define INSEXT_FROM_REG(n, val, ext) \ SCALE(((val) << 4) + (ext), 192 << 4, lm85_scaling[n]) @@ -144,23 +148,24 @@ static inline u16 FAN_TO_REG(unsigned long val) { if (!val) return 0xffff; - return SENSORS_LIMIT(5400000 / val, 1, 0xfffe); + return clamp_val(5400000 / val, 1, 0xfffe); } #define FAN_FROM_REG(val) ((val) == 0 ? -1 : (val) == 0xffff ? 0 : \ 5400000 / (val)) /* Temperature is reported in .001 degC increments */ #define TEMP_TO_REG(val) \ - SENSORS_LIMIT(SCALE(val, 1000, 1), -127, 127) + clamp_val(SCALE(val, 1000, 1), -127, 127) #define TEMPEXT_FROM_REG(val, ext) \ SCALE(((val) << 4) + (ext), 16, 1000) #define TEMP_FROM_REG(val) ((val) * 1000) -#define PWM_TO_REG(val) SENSORS_LIMIT(val, 0, 255) +#define PWM_TO_REG(val) clamp_val(val, 0, 255) #define PWM_FROM_REG(val) (val) -/* ZONEs have the following parameters: +/* + * ZONEs have the following parameters: * Limit (low) temp, 1. degC * Hysteresis (below limit), 1. degC (0-15) * Range of speed control, .1 degC (2-80) @@ -222,7 +227,8 @@ static int FREQ_FROM_REG(const int *map, u8 reg) return map[reg & 0x07]; } -/* Since we can't use strings, I'm abusing these numbers +/* + * Since we can't use strings, I'm abusing these numbers * to stand in for the following meanings: * 1 -- PWM responds to Zone 1 * 2 -- PWM responds to Zone 2 @@ -249,10 +255,11 @@ static int ZONE_TO_REG(int zone) return i << 5; } -#define HYST_TO_REG(val) SENSORS_LIMIT(((val) + 500) / 1000, 0, 15) +#define HYST_TO_REG(val) clamp_val(((val) + 500) / 1000, 0, 15) #define HYST_FROM_REG(val) ((val) * 1000) -/* Chip sampling rates +/* + * Chip sampling rates * * Some sensors are not updated more frequently than once per second * so it doesn't make sense to read them more often than that. @@ -268,7 +275,8 @@ static int ZONE_TO_REG(int zone) #define LM85_DATA_INTERVAL (HZ + HZ / 2) #define LM85_CONFIG_INTERVAL (1 * 60 * HZ) -/* LM85 can automatically adjust fan speeds based on temperature +/* + * LM85 can automatically adjust fan speeds based on temperature * This structure encapsulates an entire Zone config. There are * three zones (one for each temperature input) on the lm85 */ @@ -277,11 +285,8 @@ struct lm85_zone { u8 hyst; /* Low limit hysteresis. (0-15) */ u8 range; /* Temp range, encoded */ s8 critical; /* "All fans ON" temp limit */ - u8 off_desired; /* Actual "off" temperature specified. Preserved - * to prevent "drift" as other autofan control - * values change. - */ - u8 max_desired; /* Actual "max" temperature specified. Preserved + u8 max_desired; /* + * Actual "max" temperature specified. Preserved * to prevent "drift" as other autofan control * values change. */ @@ -293,13 +298,17 @@ struct lm85_autofan { u8 min_off; /* Min PWM or OFF below "limit", flag */ }; -/* For each registered chip, we need to keep some data in memory. - The structure is dynamically allocated. */ +/* + * For each registered chip, we need to keep some data in memory. + * The structure is dynamically allocated. + */ struct lm85_data { struct device *hwmon_dev; const int *freq_map; enum chips type; + bool has_vid5; /* true if VID5 is configured for ADT7463 or ADT7468 */ + struct mutex update_lock; int valid; /* !=0 if following fields are valid */ unsigned long last_reading; /* In jiffies */ @@ -339,12 +348,14 @@ static const struct i2c_device_id lm85_id[] = { { "adm1027", adm1027 }, { "adt7463", adt7463 }, { "adt7468", adt7468 }, - { "lm85", any_chip }, - { "lm85b", lm85b }, - { "lm85c", lm85c }, + { "lm85", lm85 }, + { "lm85b", lm85 }, + { "lm85c", lm85 }, { "emc6d100", emc6d100 }, { "emc6d101", emc6d100 }, { "emc6d102", emc6d102 }, + { "emc6d103", emc6d103 }, + { "emc6d103s", emc6d103s }, { } }; MODULE_DEVICE_TABLE(i2c, lm85_id); @@ -385,7 +396,12 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct lm85_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->fan_min[nr] = FAN_TO_REG(val); @@ -413,8 +429,7 @@ static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, struct lm85_data *data = lm85_update_device(dev); int vid; - if ((data->type == adt7463 || data->type == adt7468) && - (data->vid & 0x80)) { + if (data->has_vid5) { /* 6-pin VID (VRM 10) */ vid = vid_from_reg(data->vid & 0x3f, data->vrm); } else { @@ -438,7 +453,14 @@ static ssize_t store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct lm85_data *data = dev_get_drvdata(dev); - data->vrm = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + data->vrm = val; return count; } @@ -495,7 +517,12 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->pwm[nr] = PWM_TO_REG(val); @@ -532,8 +559,13 @@ static ssize_t set_pwm_enable(struct device *dev, struct device_attribute int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); u8 config; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; switch (val) { case 0: @@ -543,8 +575,10 @@ static ssize_t set_pwm_enable(struct device *dev, struct device_attribute config = 7; break; case 2: - /* Here we have to choose arbitrarily one of the 5 possible - configurations; I go for the safest */ + /* + * Here we have to choose arbitrarily one of the 5 possible + * configurations; I go for the safest + */ config = 6; break; default: @@ -567,8 +601,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, @@ -577,13 +617,32 @@ static ssize_t set_pwm_freq(struct device *dev, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; 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; } @@ -625,7 +684,12 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_min[nr] = INS_TO_REG(nr, val); @@ -648,7 +712,12 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_max[nr] = INS_TO_REG(nr, val); @@ -699,7 +768,12 @@ static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; if (IS_ADT7468_OFF64(data)) val += 64; @@ -725,7 +799,12 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; if (IS_ADT7468_OFF64(data)) val += 64; @@ -766,7 +845,12 @@ static ssize_t set_pwm_auto_channels(struct device *dev, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->autofan[nr].config = (data->autofan[nr].config & (~0xe0)) @@ -791,7 +875,12 @@ static ssize_t set_pwm_auto_pwm_min(struct device *dev, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->autofan[nr].min_pwm = PWM_TO_REG(val); @@ -815,8 +904,13 @@ static ssize_t set_pwm_auto_pwm_minctl(struct device *dev, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); u8 tmp; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->autofan[nr].min_off = val; @@ -862,11 +956,15 @@ static ssize_t set_temp_auto_temp_off(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct lm85_data *data = i2c_get_clientdata(client); int min; - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); min = TEMP_FROM_REG(data->zone[nr].limit); - data->zone[nr].off_desired = TEMP_TO_REG(val); data->zone[nr].hyst = HYST_TO_REG(min - val); if (nr == 0 || nr == 1) { lm85_write_value(client, LM85_REG_AFAN_HYST1, @@ -894,7 +992,12 @@ static ssize_t set_temp_auto_temp_min(struct device *dev, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->zone[nr].limit = TEMP_TO_REG(val); @@ -909,18 +1012,6 @@ static ssize_t set_temp_auto_temp_min(struct device *dev, ((data->zone[nr].range & 0x0f) << 4) | (data->pwm_freq[nr] & 0x07)); -/* Update temp_auto_hyst and temp_auto_off */ - data->zone[nr].hyst = HYST_TO_REG(TEMP_FROM_REG( - data->zone[nr].limit) - TEMP_FROM_REG( - data->zone[nr].off_desired)); - if (nr == 0 || nr == 1) { - lm85_write_value(client, LM85_REG_AFAN_HYST1, - (data->zone[0].hyst << 4) - | data->zone[1].hyst); - } else { - lm85_write_value(client, LM85_REG_AFAN_HYST2, - (data->zone[2].hyst << 4)); - } mutex_unlock(&data->update_lock); return count; } @@ -941,7 +1032,12 @@ static ssize_t set_temp_auto_temp_max(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct lm85_data *data = i2c_get_clientdata(client); int min; - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); min = TEMP_FROM_REG(data->zone[nr].limit); @@ -969,7 +1065,12 @@ static ssize_t set_temp_auto_temp_crit(struct device *dev, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->zone[nr].critical = TEMP_TO_REG(val); @@ -1059,13 +1160,7 @@ static struct attribute *lm85_attributes[] = { &sensor_dev_attr_pwm1_auto_pwm_min.dev_attr.attr, &sensor_dev_attr_pwm2_auto_pwm_min.dev_attr.attr, &sensor_dev_attr_pwm3_auto_pwm_min.dev_attr.attr, - &sensor_dev_attr_pwm1_auto_pwm_minctl.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_pwm_minctl.dev_attr.attr, - &sensor_dev_attr_pwm3_auto_pwm_minctl.dev_attr.attr, - &sensor_dev_attr_temp1_auto_temp_off.dev_attr.attr, - &sensor_dev_attr_temp2_auto_temp_off.dev_attr.attr, - &sensor_dev_attr_temp3_auto_temp_off.dev_attr.attr, &sensor_dev_attr_temp1_auto_temp_min.dev_attr.attr, &sensor_dev_attr_temp2_auto_temp_min.dev_attr.attr, &sensor_dev_attr_temp3_auto_temp_min.dev_attr.attr, @@ -1086,6 +1181,28 @@ static const struct attribute_group lm85_group = { .attrs = lm85_attributes, }; +static struct attribute *lm85_attributes_minctl[] = { + &sensor_dev_attr_pwm1_auto_pwm_minctl.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_pwm_minctl.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_pwm_minctl.dev_attr.attr, + NULL +}; + +static const struct attribute_group lm85_group_minctl = { + .attrs = lm85_attributes_minctl, +}; + +static struct attribute *lm85_attributes_temp_off[] = { + &sensor_dev_attr_temp1_auto_temp_off.dev_attr.attr, + &sensor_dev_attr_temp2_auto_temp_off.dev_attr.attr, + &sensor_dev_attr_temp3_auto_temp_off.dev_attr.attr, + NULL +}; + +static const struct attribute_group lm85_group_temp_off = { + .attrs = lm85_attributes_temp_off, +}; + static struct attribute *lm85_attributes_in4[] = { &sensor_dev_attr_in4_input.dev_attr.attr, &sensor_dev_attr_in4_min.dev_attr.attr, @@ -1161,7 +1278,7 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; int address = client->addr; - const char *type_name; + const char *type_name = NULL; int company, verstep; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { @@ -1173,20 +1290,10 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info) company = lm85_read_value(client, LM85_REG_COMPANY); verstep = lm85_read_value(client, LM85_REG_VERSTEP); - dev_dbg(&adapter->dev, "Detecting device at 0x%02x with " - "COMPANY: 0x%02x and VERSTEP: 0x%02x\n", + dev_dbg(&adapter->dev, + "Detecting device at 0x%02x with COMPANY: 0x%02x and VERSTEP: 0x%02x\n", address, company, verstep); - /* All supported chips have the version in common */ - if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC && - (verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC2) { - dev_dbg(&adapter->dev, - "Autodetection failed: unsupported version\n"); - return -ENODEV; - } - type_name = "lm85"; - - /* Now, refine the detection */ if (company == LM85_COMPANY_NATIONAL) { switch (verstep) { case LM85_VERSTEP_LM85C: @@ -1203,6 +1310,7 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info) "Found Winbond WPCD377I, ignoring\n"); return -ENODEV; } + type_name = "lm85"; break; } } else if (company == LM85_COMPANY_ANALOG_DEV) { @@ -1229,25 +1337,44 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info) case LM85_VERSTEP_EMC6D102: type_name = "emc6d102"; break; + case LM85_VERSTEP_EMC6D103_A0: + case LM85_VERSTEP_EMC6D103_A1: + type_name = "emc6d103"; + break; + case LM85_VERSTEP_EMC6D103S: + type_name = "emc6d103s"; + break; } - } else { - dev_dbg(&adapter->dev, - "Autodetection failed: unknown vendor\n"); - return -ENODEV; } + if (!type_name) + return -ENODEV; + strlcpy(info->type, type_name, I2C_NAME_SIZE); return 0; } +static void lm85_remove_files(struct i2c_client *client, struct lm85_data *data) +{ + sysfs_remove_group(&client->dev.kobj, &lm85_group); + if (data->type != emc6d103s) { + sysfs_remove_group(&client->dev.kobj, &lm85_group_minctl); + sysfs_remove_group(&client->dev.kobj, &lm85_group_temp_off); + } + if (!data->has_vid5) + sysfs_remove_group(&client->dev.kobj, &lm85_group_in4); + if (data->type == emc6d100) + sysfs_remove_group(&client->dev.kobj, &lm85_group_in567); +} + static int lm85_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct lm85_data *data; int err; - data = kzalloc(sizeof(struct lm85_data), GFP_KERNEL); + data = devm_kzalloc(&client->dev, sizeof(struct lm85_data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -1259,8 +1386,11 @@ static int lm85_probe(struct i2c_client *client, switch (data->type) { case adm1027: case adt7463: + case adt7468: case emc6d100: case emc6d102: + case emc6d103: + case emc6d103s: data->freq_map = adm1027_freq_map; break; default: @@ -1276,22 +1406,41 @@ static int lm85_probe(struct i2c_client *client, /* Register sysfs hooks */ err = sysfs_create_group(&client->dev.kobj, &lm85_group); if (err) - goto err_kfree; - - /* The ADT7463/68 have an optional VRM 10 mode where pin 21 is used - as a sixth digital VID input rather than an analog input. */ - data->vid = lm85_read_value(client, LM85_REG_VID); - if (!((data->type == adt7463 || data->type == adt7468) && - (data->vid & 0x80))) - if ((err = sysfs_create_group(&client->dev.kobj, - &lm85_group_in4))) + return err; + + /* minctl and temp_off exist on all chips except emc6d103s */ + if (data->type != emc6d103s) { + err = sysfs_create_group(&client->dev.kobj, &lm85_group_minctl); + if (err) + goto err_remove_files; + err = sysfs_create_group(&client->dev.kobj, + &lm85_group_temp_off); + if (err) goto err_remove_files; + } + + /* + * The ADT7463/68 have an optional VRM 10 mode where pin 21 is used + * as a sixth digital VID input rather than an analog input. + */ + if (data->type == adt7463 || data->type == adt7468) { + u8 vid = lm85_read_value(client, LM85_REG_VID); + if (vid & 0x80) + data->has_vid5 = true; + } + + if (!data->has_vid5) { + err = sysfs_create_group(&client->dev.kobj, &lm85_group_in4); + if (err) + goto err_remove_files; + } /* The EMC6D100 has 3 additional voltage inputs */ - if (data->type == emc6d100) - if ((err = sysfs_create_group(&client->dev.kobj, - &lm85_group_in567))) + if (data->type == emc6d100) { + err = sysfs_create_group(&client->dev.kobj, &lm85_group_in567); + if (err) goto err_remove_files; + } data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { @@ -1303,12 +1452,7 @@ static int lm85_probe(struct i2c_client *client, /* Error out and cleanup code */ err_remove_files: - sysfs_remove_group(&client->dev.kobj, &lm85_group); - sysfs_remove_group(&client->dev.kobj, &lm85_group_in4); - if (data->type == emc6d100) - sysfs_remove_group(&client->dev.kobj, &lm85_group_in567); - err_kfree: - kfree(data); + lm85_remove_files(client, data); return err; } @@ -1316,11 +1460,7 @@ static int lm85_remove(struct i2c_client *client) { struct lm85_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &lm85_group); - sysfs_remove_group(&client->dev.kobj, &lm85_group_in4); - if (data->type == emc6d100) - sysfs_remove_group(&client->dev.kobj, &lm85_group_in567); - kfree(data); + lm85_remove_files(client, data); return 0; } @@ -1385,7 +1525,8 @@ static struct lm85_data *lm85_update_device(struct device *dev) /* Things that change quickly */ dev_dbg(&client->dev, "Reading sensor values\n"); - /* Have to read extended bits first to "freeze" the + /* + * Have to read extended bits first to "freeze" the * more significant bits that are read later. * There are 2 additional resolution bits per channel and we * have room for 4, so we shift them to the left. @@ -1416,11 +1557,8 @@ static struct lm85_data *lm85_update_device(struct device *dev) lm85_read_value(client, LM85_REG_FAN(i)); } - if (!((data->type == adt7463 || data->type == adt7468) && - (data->vid & 0x80))) { - data->in[4] = lm85_read_value(client, - LM85_REG_IN(4)); - } + if (!data->has_vid5) + data->in[4] = lm85_read_value(client, LM85_REG_IN(4)); if (data->type == adt7468) data->cfg5 = lm85_read_value(client, ADT7468_REG_CFG5); @@ -1446,10 +1584,12 @@ static struct lm85_data *lm85_update_device(struct device *dev) /* More alarm bits */ data->alarms |= lm85_read_value(client, EMC6D100_REG_ALARM3) << 16; - } else if (data->type == emc6d102) { - /* Have to read LSB bits after the MSB ones because - the reading of the MSB bits has frozen the - LSBs (backward from the ADM1027). + } else if (data->type == emc6d102 || data->type == emc6d103 || + data->type == emc6d103s) { + /* + * Have to read LSB bits after the MSB ones because + * the reading of the MSB bits has frozen the + * LSBs (backward from the ADM1027). */ int ext1 = lm85_read_value(client, EMC6D102_REG_EXTEND_ADC1); @@ -1487,8 +1627,7 @@ static struct lm85_data *lm85_update_device(struct device *dev) lm85_read_value(client, LM85_REG_FAN_MIN(i)); } - if (!((data->type == adt7463 || data->type == adt7468) && - (data->vid & 0x80))) { + if (!data->has_vid5) { data->in_min[4] = lm85_read_value(client, LM85_REG_IN_MIN(4)); data->in_max[4] = lm85_read_value(client, @@ -1532,17 +1671,19 @@ static struct lm85_data *lm85_update_device(struct device *dev) } } - i = lm85_read_value(client, LM85_REG_AFAN_SPIKE1); - data->autofan[0].min_off = (i & 0x20) != 0; - data->autofan[1].min_off = (i & 0x40) != 0; - data->autofan[2].min_off = (i & 0x80) != 0; + if (data->type != emc6d103s) { + i = lm85_read_value(client, LM85_REG_AFAN_SPIKE1); + data->autofan[0].min_off = (i & 0x20) != 0; + data->autofan[1].min_off = (i & 0x40) != 0; + data->autofan[2].min_off = (i & 0x80) != 0; - i = lm85_read_value(client, LM85_REG_AFAN_HYST1); - data->zone[0].hyst = i >> 4; - data->zone[1].hyst = i & 0x0f; + i = lm85_read_value(client, LM85_REG_AFAN_HYST1); + data->zone[0].hyst = i >> 4; + data->zone[1].hyst = i & 0x0f; - i = lm85_read_value(client, LM85_REG_AFAN_HYST2); - data->zone[2].hyst = i >> 4; + i = lm85_read_value(client, LM85_REG_AFAN_HYST2); + data->zone[2].hyst = i >> 4; + } data->last_config = jiffies; } /* last_config */ @@ -1554,22 +1695,10 @@ static struct lm85_data *lm85_update_device(struct device *dev) return data; } - -static int __init sm_lm85_init(void) -{ - return i2c_add_driver(&lm85_driver); -} - -static void __exit sm_lm85_exit(void) -{ - i2c_del_driver(&lm85_driver); -} +module_i2c_driver(lm85_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Philip Pokorny <ppokorny@penguincomputing.com>, " "Margit Schubert-While <margitsw@t-online.de>, " "Justin Thiessen <jthiessen@penguincomputing.com>"); MODULE_DESCRIPTION("LM85-B, LM85-C driver"); - -module_init(sm_lm85_init); -module_exit(sm_lm85_exit); diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index f1e6e7512ff..ba1d83d4805 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -5,7 +5,7 @@ * Philip Edelbrock <phil@netroedge.com> * Stephen Rousset <stephen.rousset@rocketlogix.com> * Dan Eaton <dan.eaton@rocketlogix.com> - * Copyright (C) 2004-2008 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2004-2008 Jean Delvare <jdelvare@suse.de> * * Original port to Linux 2.6 by Jeff Oliver. * @@ -119,20 +119,21 @@ static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C }; * The LM87 uses signed 8-bit values for temperatures. */ -#define IN_FROM_REG(reg,scale) (((reg) * (scale) + 96) / 192) -#define IN_TO_REG(val,scale) ((val) <= 0 ? 0 : \ +#define IN_FROM_REG(reg, scale) (((reg) * (scale) + 96) / 192) +#define IN_TO_REG(val, scale) ((val) <= 0 ? 0 : \ (val) * 192 >= (scale) * 255 ? 255 : \ - ((val) * 192 + (scale)/2) / (scale)) + ((val) * 192 + (scale) / 2) / (scale)) #define TEMP_FROM_REG(reg) ((reg) * 1000) #define TEMP_TO_REG(val) ((val) <= -127500 ? -128 : \ (val) >= 126500 ? 127 : \ - (((val) < 0 ? (val)-500 : (val)+500) / 1000)) + (((val) < 0 ? (val) - 500 : \ + (val) + 500) / 1000)) -#define FAN_FROM_REG(reg,div) ((reg) == 255 || (reg) == 0 ? 0 : \ - (1350000 + (reg)*(div) / 2) / ((reg)*(div))) -#define FAN_TO_REG(val,div) ((val)*(div) * 255 <= 1350000 ? 255 : \ - (1350000 + (val)*(div) / 2) / ((val)*(div))) +#define FAN_FROM_REG(reg, div) ((reg) == 255 || (reg) == 0 ? 0 : \ + (1350000 + (reg)*(div) / 2) / ((reg) * (div))) +#define FAN_TO_REG(val, div) ((val) * (div) * 255 <= 1350000 ? 255 : \ + (1350000 + (val)*(div) / 2) / ((val) * (div))) #define FAN_DIV_FROM_REG(reg) (1 << (reg)) @@ -149,41 +150,6 @@ static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C }; #define CHAN_NO_VID (1 << 7) /* - * Functions declaration - */ - -static int lm87_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int lm87_detect(struct i2c_client *new_client, - struct i2c_board_info *info); -static void lm87_init_client(struct i2c_client *client); -static int lm87_remove(struct i2c_client *client); -static struct lm87_data *lm87_update_device(struct device *dev); - -/* - * Driver data (common to all clients) - */ - -static const struct i2c_device_id lm87_id[] = { - { "lm87", lm87 }, - { "adm1024", adm1024 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, lm87_id); - -static struct i2c_driver lm87_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "lm87", - }, - .probe = lm87_probe, - .remove = lm87_remove, - .id_table = lm87_id, - .detect = lm87_detect, - .address_list = normal_i2c, -}; - -/* * Client data (each client gets its own) */ @@ -217,10 +183,6 @@ struct lm87_data { u8 vrm; }; -/* - * Sysfs stuff - */ - static inline int lm87_read_value(struct i2c_client *client, u8 reg) { return i2c_smbus_read_byte_data(client, reg); @@ -231,79 +193,168 @@ static inline int lm87_write_value(struct i2c_client *client, u8 reg, u8 value) return i2c_smbus_write_byte_data(client, reg, value); } -#define show_in(offset) \ -static ssize_t show_in##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm87_data *data = lm87_update_device(dev); \ - return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \ - data->in_scale[offset])); \ -} \ -static ssize_t show_in##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm87_data *data = lm87_update_device(dev); \ - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \ - data->in_scale[offset])); \ -} \ -static ssize_t show_in##offset##_max(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm87_data *data = lm87_update_device(dev); \ - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \ - data->in_scale[offset])); \ -} \ -static DEVICE_ATTR(in##offset##_input, S_IRUGO, \ - show_in##offset##_input, NULL); -show_in(0); -show_in(1); -show_in(2); -show_in(3); -show_in(4); -show_in(5); -show_in(6); -show_in(7); - -static void set_in_min(struct device *dev, const char *buf, int nr) +static struct lm87_data *lm87_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm87_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + int i, j; + + dev_dbg(&client->dev, "Updating data.\n"); + + i = (data->channel & CHAN_TEMP3) ? 1 : 0; + j = (data->channel & CHAN_TEMP3) ? 5 : 6; + for (; i < j; i++) { + data->in[i] = lm87_read_value(client, + LM87_REG_IN(i)); + data->in_min[i] = lm87_read_value(client, + LM87_REG_IN_MIN(i)); + data->in_max[i] = lm87_read_value(client, + LM87_REG_IN_MAX(i)); + } + + for (i = 0; i < 2; i++) { + if (data->channel & CHAN_NO_FAN(i)) { + data->in[6+i] = lm87_read_value(client, + LM87_REG_AIN(i)); + data->in_max[6+i] = lm87_read_value(client, + LM87_REG_AIN_MAX(i)); + data->in_min[6+i] = lm87_read_value(client, + LM87_REG_AIN_MIN(i)); + + } else { + data->fan[i] = lm87_read_value(client, + LM87_REG_FAN(i)); + data->fan_min[i] = lm87_read_value(client, + LM87_REG_FAN_MIN(i)); + } + } + + j = (data->channel & CHAN_TEMP3) ? 3 : 2; + for (i = 0 ; i < j; i++) { + data->temp[i] = lm87_read_value(client, + LM87_REG_TEMP[i]); + data->temp_high[i] = lm87_read_value(client, + LM87_REG_TEMP_HIGH[i]); + data->temp_low[i] = lm87_read_value(client, + LM87_REG_TEMP_LOW[i]); + } + + i = lm87_read_value(client, LM87_REG_TEMP_HW_INT_LOCK); + j = lm87_read_value(client, LM87_REG_TEMP_HW_INT); + data->temp_crit_int = min(i, j); + + i = lm87_read_value(client, LM87_REG_TEMP_HW_EXT_LOCK); + j = lm87_read_value(client, LM87_REG_TEMP_HW_EXT); + data->temp_crit_ext = min(i, j); + + i = lm87_read_value(client, LM87_REG_VID_FAN_DIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = (i >> 6) & 0x03; + data->vid = (i & 0x0F) + | (lm87_read_value(client, LM87_REG_VID4) & 0x01) + << 4; + + data->alarms = lm87_read_value(client, LM87_REG_ALARMS1) + | (lm87_read_value(client, LM87_REG_ALARMS2) + << 8); + data->aout = lm87_read_value(client, LM87_REG_AOUT); + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +/* + * Sysfs stuff + */ + +static ssize_t show_in_input(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + int nr = to_sensor_dev_attr(attr)->index; + + return sprintf(buf, "%u\n", IN_FROM_REG(data->in[nr], + data->in_scale[nr])); +} + +static ssize_t show_in_min(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + int nr = to_sensor_dev_attr(attr)->index; + + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[nr], + data->in_scale[nr])); +} + +static ssize_t show_in_max(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + int nr = to_sensor_dev_attr(attr)->index; + + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[nr], + data->in_scale[nr])); +} + +static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct lm87_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + int nr = to_sensor_dev_attr(attr)->index; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_min[nr] = IN_TO_REG(val, data->in_scale[nr]); - lm87_write_value(client, nr<6 ? LM87_REG_IN_MIN(nr) : - LM87_REG_AIN_MIN(nr-6), data->in_min[nr]); + lm87_write_value(client, nr < 6 ? LM87_REG_IN_MIN(nr) : + LM87_REG_AIN_MIN(nr - 6), data->in_min[nr]); mutex_unlock(&data->update_lock); + return count; } -static void set_in_max(struct device *dev, const char *buf, int nr) +static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct lm87_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + int nr = to_sensor_dev_attr(attr)->index; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_max[nr] = IN_TO_REG(val, data->in_scale[nr]); - lm87_write_value(client, nr<6 ? LM87_REG_IN_MAX(nr) : - LM87_REG_AIN_MAX(nr-6), data->in_max[nr]); + lm87_write_value(client, nr < 6 ? LM87_REG_IN_MAX(nr) : + LM87_REG_AIN_MAX(nr - 6), data->in_max[nr]); mutex_unlock(&data->update_lock); + return count; } #define set_in(offset) \ -static ssize_t set_in##offset##_min(struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - set_in_min(dev, buf, offset); \ - return count; \ -} \ -static ssize_t set_in##offset##_max(struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - set_in_max(dev, buf, offset); \ - return count; \ -} \ -static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ - show_in##offset##_min, set_in##offset##_min); \ -static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ - show_in##offset##_max, set_in##offset##_max); +static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \ + show_in_input, NULL, offset); \ +static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ + show_in_min, set_in_min, offset); \ +static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ + show_in_max, set_in_max, offset) set_in(0); set_in(1); set_in(2); @@ -313,80 +364,95 @@ set_in(5); set_in(6); set_in(7); -#define show_temp(offset) \ -static ssize_t show_temp##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm87_data *data = lm87_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[offset-1])); \ -} \ -static ssize_t show_temp##offset##_low(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm87_data *data = lm87_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_low[offset-1])); \ -} \ -static ssize_t show_temp##offset##_high(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm87_data *data = lm87_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[offset-1])); \ -}\ -static DEVICE_ATTR(temp##offset##_input, S_IRUGO, \ - show_temp##offset##_input, NULL); -show_temp(1); -show_temp(2); -show_temp(3); - -static void set_temp_low(struct device *dev, const char *buf, int nr) +static ssize_t show_temp_input(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + int nr = to_sensor_dev_attr(attr)->index; + + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr])); +} + +static ssize_t show_temp_low(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + int nr = to_sensor_dev_attr(attr)->index; + + return sprintf(buf, "%d\n", + TEMP_FROM_REG(data->temp_low[nr])); +} + +static ssize_t show_temp_high(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + int nr = to_sensor_dev_attr(attr)->index; + + return sprintf(buf, "%d\n", + TEMP_FROM_REG(data->temp_high[nr])); +} + +static ssize_t set_temp_low(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct lm87_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + int nr = to_sensor_dev_attr(attr)->index; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_low[nr] = TEMP_TO_REG(val); lm87_write_value(client, LM87_REG_TEMP_LOW[nr], data->temp_low[nr]); mutex_unlock(&data->update_lock); + return count; } -static void set_temp_high(struct device *dev, const char *buf, int nr) +static ssize_t set_temp_high(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct lm87_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + int nr = to_sensor_dev_attr(attr)->index; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_high[nr] = TEMP_TO_REG(val); lm87_write_value(client, LM87_REG_TEMP_HIGH[nr], data->temp_high[nr]); mutex_unlock(&data->update_lock); + return count; } #define set_temp(offset) \ -static ssize_t set_temp##offset##_low(struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - set_temp_low(dev, buf, offset-1); \ - return count; \ -} \ -static ssize_t set_temp##offset##_high(struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - set_temp_high(dev, buf, offset-1); \ - return count; \ -} \ -static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ - show_temp##offset##_high, set_temp##offset##_high); \ -static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ - show_temp##offset##_low, set_temp##offset##_low); +static SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO, \ + show_temp_input, NULL, offset - 1); \ +static SENSOR_DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ + show_temp_high, set_temp_high, offset - 1); \ +static SENSOR_DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ + show_temp_low, set_temp_low, offset - 1) set_temp(1); set_temp(2); set_temp(3); -static ssize_t show_temp_crit_int(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_temp_crit_int(struct device *dev, + struct device_attribute *attr, char *buf) { struct lm87_data *data = lm87_update_device(dev); return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_int)); } -static ssize_t show_temp_crit_ext(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_temp_crit_ext(struct device *dev, + struct device_attribute *attr, char *buf) { struct lm87_data *data = lm87_update_device(dev); return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_ext)); @@ -396,64 +462,95 @@ static DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit_int, NULL); static DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp_crit_ext, NULL); static DEVICE_ATTR(temp3_crit, S_IRUGO, show_temp_crit_ext, NULL); -#define show_fan(offset) \ -static ssize_t show_fan##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm87_data *data = lm87_update_device(dev); \ - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[offset-1], \ - FAN_DIV_FROM_REG(data->fan_div[offset-1]))); \ -} \ -static ssize_t show_fan##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm87_data *data = lm87_update_device(dev); \ - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[offset-1], \ - FAN_DIV_FROM_REG(data->fan_div[offset-1]))); \ -} \ -static ssize_t show_fan##offset##_div(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm87_data *data = lm87_update_device(dev); \ - return sprintf(buf, "%d\n", FAN_DIV_FROM_REG(data->fan_div[offset-1])); \ -} \ -static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ - show_fan##offset##_input, NULL); -show_fan(1); -show_fan(2); - -static void set_fan_min(struct device *dev, const char *buf, int nr) +static ssize_t show_fan_input(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + int nr = to_sensor_dev_attr(attr)->index; + + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], + FAN_DIV_FROM_REG(data->fan_div[nr]))); +} + +static ssize_t show_fan_min(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + int nr = to_sensor_dev_attr(attr)->index; + + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr], + FAN_DIV_FROM_REG(data->fan_div[nr]))); +} + +static ssize_t show_fan_div(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + int nr = to_sensor_dev_attr(attr)->index; + + return sprintf(buf, "%d\n", + FAN_DIV_FROM_REG(data->fan_div[nr])); +} + +static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct lm87_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + int nr = to_sensor_dev_attr(attr)->index; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->fan_min[nr] = FAN_TO_REG(val, FAN_DIV_FROM_REG(data->fan_div[nr])); lm87_write_value(client, LM87_REG_FAN_MIN(nr), data->fan_min[nr]); mutex_unlock(&data->update_lock); + return count; } -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan clock divider. This follows the principle - of least surprise; the user doesn't expect the fan minimum to change just - because the divider changed. */ -static ssize_t set_fan_div(struct device *dev, const char *buf, - size_t count, int nr) +/* + * Note: we save and restore the fan minimum here, because its value is + * determined in part by the fan clock divider. This follows the principle + * of least surprise; the user doesn't expect the fan minimum to change just + * because the divider changed. + */ +static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct lm87_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + int nr = to_sensor_dev_attr(attr)->index; + long val; + int err; unsigned long min; u8 reg; + err = kstrtol(buf, 10, &val); + if (err) + return err; + mutex_lock(&data->update_lock); min = FAN_FROM_REG(data->fan_min[nr], FAN_DIV_FROM_REG(data->fan_div[nr])); switch (val) { - case 1: data->fan_div[nr] = 0; break; - case 2: data->fan_div[nr] = 1; break; - case 4: data->fan_div[nr] = 2; break; - case 8: data->fan_div[nr] = 3; break; + case 1: + data->fan_div[nr] = 0; + break; + case 2: + data->fan_div[nr] = 1; + break; + case 4: + data->fan_div[nr] = 2; + break; + case 8: + data->fan_div[nr] = 3; + break; default: mutex_unlock(&data->update_lock); return -EINVAL; @@ -479,61 +576,69 @@ static ssize_t set_fan_div(struct device *dev, const char *buf, } #define set_fan(offset) \ -static ssize_t set_fan##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - set_fan_min(dev, buf, offset-1); \ - return count; \ -} \ -static ssize_t set_fan##offset##_div(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - return set_fan_div(dev, buf, count, offset-1); \ -} \ -static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ - show_fan##offset##_min, set_fan##offset##_min); \ -static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ - show_fan##offset##_div, set_fan##offset##_div); +static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ + show_fan_input, NULL, offset - 1); \ +static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ + show_fan_min, set_fan_min, offset - 1); \ +static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ + show_fan_div, set_fan_div, offset - 1) set_fan(1); set_fan(2); -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, + char *buf) { struct lm87_data *data = lm87_update_device(dev); return sprintf(buf, "%d\n", data->alarms); } static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); -static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_vid(struct device *dev, struct device_attribute *attr, + char *buf) { struct lm87_data *data = lm87_update_device(dev); return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); } static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); -static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, + char *buf) { struct lm87_data *data = dev_get_drvdata(dev); return sprintf(buf, "%d\n", data->vrm); } -static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct lm87_data *data = dev_get_drvdata(dev); - data->vrm = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + data->vrm = val; return count; } static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); -static ssize_t show_aout(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_aout(struct device *dev, struct device_attribute *attr, + char *buf) { struct lm87_data *data = lm87_update_device(dev); return sprintf(buf, "%d\n", AOUT_FROM_REG(data->aout)); } -static ssize_t set_aout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t set_aout(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct lm87_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->aout = AOUT_TO_REG(val); @@ -571,31 +676,31 @@ static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 15); */ static struct attribute *lm87_attributes[] = { - &dev_attr_in1_input.attr, - &dev_attr_in1_min.attr, - &dev_attr_in1_max.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, &sensor_dev_attr_in1_alarm.dev_attr.attr, - &dev_attr_in2_input.attr, - &dev_attr_in2_min.attr, - &dev_attr_in2_max.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, &sensor_dev_attr_in2_alarm.dev_attr.attr, - &dev_attr_in3_input.attr, - &dev_attr_in3_min.attr, - &dev_attr_in3_max.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, &sensor_dev_attr_in3_alarm.dev_attr.attr, - &dev_attr_in4_input.attr, - &dev_attr_in4_min.attr, - &dev_attr_in4_max.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, &sensor_dev_attr_in4_alarm.dev_attr.attr, - &dev_attr_temp1_input.attr, - &dev_attr_temp1_max.attr, - &dev_attr_temp1_min.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, &dev_attr_temp1_crit.attr, &sensor_dev_attr_temp1_alarm.dev_attr.attr, - &dev_attr_temp2_input.attr, - &dev_attr_temp2_max.attr, - &dev_attr_temp2_min.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, &dev_attr_temp2_crit.attr, &sensor_dev_attr_temp2_alarm.dev_attr.attr, &sensor_dev_attr_temp2_fault.dev_attr.attr, @@ -610,70 +715,110 @@ static const struct attribute_group lm87_group = { .attrs = lm87_attributes, }; -static struct attribute *lm87_attributes_opt[] = { - &dev_attr_in6_input.attr, - &dev_attr_in6_min.attr, - &dev_attr_in6_max.attr, +static struct attribute *lm87_attributes_in6[] = { + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in6_min.dev_attr.attr, + &sensor_dev_attr_in6_max.dev_attr.attr, &sensor_dev_attr_in6_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group lm87_group_in6 = { + .attrs = lm87_attributes_in6, +}; - &dev_attr_fan1_input.attr, - &dev_attr_fan1_min.attr, - &dev_attr_fan1_div.attr, +static struct attribute *lm87_attributes_fan1[] = { + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan1_div.dev_attr.attr, &sensor_dev_attr_fan1_alarm.dev_attr.attr, + NULL +}; - &dev_attr_in7_input.attr, - &dev_attr_in7_min.attr, - &dev_attr_in7_max.attr, +static const struct attribute_group lm87_group_fan1 = { + .attrs = lm87_attributes_fan1, +}; + +static struct attribute *lm87_attributes_in7[] = { + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in7_min.dev_attr.attr, + &sensor_dev_attr_in7_max.dev_attr.attr, &sensor_dev_attr_in7_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group lm87_group_in7 = { + .attrs = lm87_attributes_in7, +}; - &dev_attr_fan2_input.attr, - &dev_attr_fan2_min.attr, - &dev_attr_fan2_div.attr, +static struct attribute *lm87_attributes_fan2[] = { + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan2_div.dev_attr.attr, &sensor_dev_attr_fan2_alarm.dev_attr.attr, + NULL +}; - &dev_attr_temp3_input.attr, - &dev_attr_temp3_max.attr, - &dev_attr_temp3_min.attr, +static const struct attribute_group lm87_group_fan2 = { + .attrs = lm87_attributes_fan2, +}; + +static struct attribute *lm87_attributes_temp3[] = { + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, &dev_attr_temp3_crit.attr, &sensor_dev_attr_temp3_alarm.dev_attr.attr, &sensor_dev_attr_temp3_fault.dev_attr.attr, + NULL +}; - &dev_attr_in0_input.attr, - &dev_attr_in0_min.attr, - &dev_attr_in0_max.attr, +static const struct attribute_group lm87_group_temp3 = { + .attrs = lm87_attributes_temp3, +}; + +static struct attribute *lm87_attributes_in0_5[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, &sensor_dev_attr_in0_alarm.dev_attr.attr, - &dev_attr_in5_input.attr, - &dev_attr_in5_min.attr, - &dev_attr_in5_max.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, &sensor_dev_attr_in5_alarm.dev_attr.attr, + NULL +}; +static const struct attribute_group lm87_group_in0_5 = { + .attrs = lm87_attributes_in0_5, +}; + +static struct attribute *lm87_attributes_vid[] = { &dev_attr_cpu0_vid.attr, &dev_attr_vrm.attr, - NULL }; -static const struct attribute_group lm87_group_opt = { - .attrs = lm87_attributes_opt, +static const struct attribute_group lm87_group_vid = { + .attrs = lm87_attributes_vid, }; /* Return 0 if detection is successful, -ENODEV otherwise */ -static int lm87_detect(struct i2c_client *new_client, - struct i2c_board_info *info) +static int lm87_detect(struct i2c_client *client, struct i2c_board_info *info) { - struct i2c_adapter *adapter = new_client->adapter; + struct i2c_adapter *adapter = client->adapter; const char *name; u8 cid, rev; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (lm87_read_value(new_client, LM87_REG_CONFIG) & 0x80) + if (lm87_read_value(client, LM87_REG_CONFIG) & 0x80) return -ENODEV; /* Now, we do the remaining detection. */ - cid = lm87_read_value(new_client, LM87_REG_COMPANY_ID); - rev = lm87_read_value(new_client, LM87_REG_REVISION); + cid = lm87_read_value(client, LM87_REG_COMPANY_ID); + rev = lm87_read_value(client, LM87_REG_REVISION); if (cid == 0x02 /* National Semiconductor */ && (rev >= 0x01 && rev <= 0x08)) @@ -683,7 +828,7 @@ static int lm87_detect(struct i2c_client *new_client, name = "adm1024"; else { dev_dbg(&adapter->dev, "LM87 detection failed at 0x%02x\n", - new_client->addr); + client->addr); return -ENODEV; } @@ -692,24 +837,76 @@ static int lm87_detect(struct i2c_client *new_client, return 0; } -static int lm87_probe(struct i2c_client *new_client, - const struct i2c_device_id *id) +static void lm87_remove_files(struct i2c_client *client) +{ + struct device *dev = &client->dev; + + sysfs_remove_group(&dev->kobj, &lm87_group); + sysfs_remove_group(&dev->kobj, &lm87_group_in6); + sysfs_remove_group(&dev->kobj, &lm87_group_fan1); + sysfs_remove_group(&dev->kobj, &lm87_group_in7); + sysfs_remove_group(&dev->kobj, &lm87_group_fan2); + sysfs_remove_group(&dev->kobj, &lm87_group_temp3); + sysfs_remove_group(&dev->kobj, &lm87_group_in0_5); + sysfs_remove_group(&dev->kobj, &lm87_group_vid); +} + +static void lm87_init_client(struct i2c_client *client) +{ + struct lm87_data *data = i2c_get_clientdata(client); + + if (dev_get_platdata(&client->dev)) { + data->channel = *(u8 *)dev_get_platdata(&client->dev); + lm87_write_value(client, + LM87_REG_CHANNEL_MODE, data->channel); + } else { + data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE); + } + data->config = lm87_read_value(client, LM87_REG_CONFIG) & 0x6F; + + if (!(data->config & 0x01)) { + int i; + + /* Limits are left uninitialized after power-up */ + for (i = 1; i < 6; i++) { + lm87_write_value(client, LM87_REG_IN_MIN(i), 0x00); + lm87_write_value(client, LM87_REG_IN_MAX(i), 0xFF); + } + for (i = 0; i < 2; i++) { + lm87_write_value(client, LM87_REG_TEMP_HIGH[i], 0x7F); + lm87_write_value(client, LM87_REG_TEMP_LOW[i], 0x00); + lm87_write_value(client, LM87_REG_AIN_MIN(i), 0x00); + lm87_write_value(client, LM87_REG_AIN_MAX(i), 0xFF); + } + if (data->channel & CHAN_TEMP3) { + lm87_write_value(client, LM87_REG_TEMP_HIGH[2], 0x7F); + lm87_write_value(client, LM87_REG_TEMP_LOW[2], 0x00); + } else { + lm87_write_value(client, LM87_REG_IN_MIN(0), 0x00); + lm87_write_value(client, LM87_REG_IN_MAX(0), 0xFF); + } + } + + /* Make sure Start is set and INT#_Clear is clear */ + if ((data->config & 0x09) != 0x01) + lm87_write_value(client, LM87_REG_CONFIG, + (data->config & 0x77) | 0x01); +} + +static int lm87_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct lm87_data *data; int err; - data = kzalloc(sizeof(struct lm87_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&client->dev, sizeof(struct lm87_data), GFP_KERNEL); + if (!data) + return -ENOMEM; - i2c_set_clientdata(new_client, data); - data->valid = 0; + i2c_set_clientdata(client, data); mutex_init(&data->update_lock); /* Initialize the LM87 chip */ - lm87_init_client(new_client); + lm87_init_client(client); data->in_scale[0] = 2500; data->in_scale[1] = 2700; @@ -721,97 +918,48 @@ static int lm87_probe(struct i2c_client *new_client, data->in_scale[7] = 1875; /* Register sysfs hooks */ - if ((err = sysfs_create_group(&new_client->dev.kobj, &lm87_group))) - goto exit_free; + err = sysfs_create_group(&client->dev.kobj, &lm87_group); + if (err) + goto exit_stop; if (data->channel & CHAN_NO_FAN(0)) { - if ((err = device_create_file(&new_client->dev, - &dev_attr_in6_input)) - || (err = device_create_file(&new_client->dev, - &dev_attr_in6_min)) - || (err = device_create_file(&new_client->dev, - &dev_attr_in6_max)) - || (err = device_create_file(&new_client->dev, - &sensor_dev_attr_in6_alarm.dev_attr))) + err = sysfs_create_group(&client->dev.kobj, &lm87_group_in6); + if (err) goto exit_remove; } else { - if ((err = device_create_file(&new_client->dev, - &dev_attr_fan1_input)) - || (err = device_create_file(&new_client->dev, - &dev_attr_fan1_min)) - || (err = device_create_file(&new_client->dev, - &dev_attr_fan1_div)) - || (err = device_create_file(&new_client->dev, - &sensor_dev_attr_fan1_alarm.dev_attr))) + err = sysfs_create_group(&client->dev.kobj, &lm87_group_fan1); + if (err) goto exit_remove; } if (data->channel & CHAN_NO_FAN(1)) { - if ((err = device_create_file(&new_client->dev, - &dev_attr_in7_input)) - || (err = device_create_file(&new_client->dev, - &dev_attr_in7_min)) - || (err = device_create_file(&new_client->dev, - &dev_attr_in7_max)) - || (err = device_create_file(&new_client->dev, - &sensor_dev_attr_in7_alarm.dev_attr))) + err = sysfs_create_group(&client->dev.kobj, &lm87_group_in7); + if (err) goto exit_remove; } else { - if ((err = device_create_file(&new_client->dev, - &dev_attr_fan2_input)) - || (err = device_create_file(&new_client->dev, - &dev_attr_fan2_min)) - || (err = device_create_file(&new_client->dev, - &dev_attr_fan2_div)) - || (err = device_create_file(&new_client->dev, - &sensor_dev_attr_fan2_alarm.dev_attr))) + err = sysfs_create_group(&client->dev.kobj, &lm87_group_fan2); + if (err) goto exit_remove; } if (data->channel & CHAN_TEMP3) { - if ((err = device_create_file(&new_client->dev, - &dev_attr_temp3_input)) - || (err = device_create_file(&new_client->dev, - &dev_attr_temp3_max)) - || (err = device_create_file(&new_client->dev, - &dev_attr_temp3_min)) - || (err = device_create_file(&new_client->dev, - &dev_attr_temp3_crit)) - || (err = device_create_file(&new_client->dev, - &sensor_dev_attr_temp3_alarm.dev_attr)) - || (err = device_create_file(&new_client->dev, - &sensor_dev_attr_temp3_fault.dev_attr))) + err = sysfs_create_group(&client->dev.kobj, &lm87_group_temp3); + if (err) goto exit_remove; } else { - if ((err = device_create_file(&new_client->dev, - &dev_attr_in0_input)) - || (err = device_create_file(&new_client->dev, - &dev_attr_in0_min)) - || (err = device_create_file(&new_client->dev, - &dev_attr_in0_max)) - || (err = device_create_file(&new_client->dev, - &sensor_dev_attr_in0_alarm.dev_attr)) - || (err = device_create_file(&new_client->dev, - &dev_attr_in5_input)) - || (err = device_create_file(&new_client->dev, - &dev_attr_in5_min)) - || (err = device_create_file(&new_client->dev, - &dev_attr_in5_max)) - || (err = device_create_file(&new_client->dev, - &sensor_dev_attr_in5_alarm.dev_attr))) + err = sysfs_create_group(&client->dev.kobj, &lm87_group_in0_5); + if (err) goto exit_remove; } if (!(data->channel & CHAN_NO_VID)) { data->vrm = vid_which_vrm(); - if ((err = device_create_file(&new_client->dev, - &dev_attr_cpu0_vid)) - || (err = device_create_file(&new_client->dev, - &dev_attr_vrm))) + err = sysfs_create_group(&client->dev.kobj, &lm87_group_vid); + if (err) goto exit_remove; } - data->hwmon_dev = hwmon_device_register(&new_client->dev); + data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); goto exit_remove; @@ -820,162 +968,48 @@ static int lm87_probe(struct i2c_client *new_client, return 0; exit_remove: - sysfs_remove_group(&new_client->dev.kobj, &lm87_group); - sysfs_remove_group(&new_client->dev.kobj, &lm87_group_opt); -exit_free: - lm87_write_value(new_client, LM87_REG_CONFIG, data->config); - kfree(data); -exit: + lm87_remove_files(client); +exit_stop: + lm87_write_value(client, LM87_REG_CONFIG, data->config); return err; } -static void lm87_init_client(struct i2c_client *client) -{ - struct lm87_data *data = i2c_get_clientdata(client); - - if (client->dev.platform_data) { - data->channel = *(u8 *)client->dev.platform_data; - lm87_write_value(client, - LM87_REG_CHANNEL_MODE, data->channel); - } else { - data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE); - } - data->config = lm87_read_value(client, LM87_REG_CONFIG) & 0x6F; - - if (!(data->config & 0x01)) { - int i; - - /* Limits are left uninitialized after power-up */ - for (i = 1; i < 6; i++) { - lm87_write_value(client, LM87_REG_IN_MIN(i), 0x00); - lm87_write_value(client, LM87_REG_IN_MAX(i), 0xFF); - } - for (i = 0; i < 2; i++) { - lm87_write_value(client, LM87_REG_TEMP_HIGH[i], 0x7F); - lm87_write_value(client, LM87_REG_TEMP_LOW[i], 0x00); - lm87_write_value(client, LM87_REG_AIN_MIN(i), 0x00); - lm87_write_value(client, LM87_REG_AIN_MAX(i), 0xFF); - } - if (data->channel & CHAN_TEMP3) { - lm87_write_value(client, LM87_REG_TEMP_HIGH[2], 0x7F); - lm87_write_value(client, LM87_REG_TEMP_LOW[2], 0x00); - } else { - lm87_write_value(client, LM87_REG_IN_MIN(0), 0x00); - lm87_write_value(client, LM87_REG_IN_MAX(0), 0xFF); - } - } - - /* Make sure Start is set and INT#_Clear is clear */ - if ((data->config & 0x09) != 0x01) - lm87_write_value(client, LM87_REG_CONFIG, - (data->config & 0x77) | 0x01); -} - static int lm87_remove(struct i2c_client *client) { struct lm87_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &lm87_group); - sysfs_remove_group(&client->dev.kobj, &lm87_group_opt); + lm87_remove_files(client); lm87_write_value(client, LM87_REG_CONFIG, data->config); - kfree(data); return 0; } -static struct lm87_data *lm87_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm87_data *data = i2c_get_clientdata(client); - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - int i, j; - - dev_dbg(&client->dev, "Updating data.\n"); - - i = (data->channel & CHAN_TEMP3) ? 1 : 0; - j = (data->channel & CHAN_TEMP3) ? 5 : 6; - for (; i < j; i++) { - data->in[i] = lm87_read_value(client, - LM87_REG_IN(i)); - data->in_min[i] = lm87_read_value(client, - LM87_REG_IN_MIN(i)); - data->in_max[i] = lm87_read_value(client, - LM87_REG_IN_MAX(i)); - } - - for (i = 0; i < 2; i++) { - if (data->channel & CHAN_NO_FAN(i)) { - data->in[6+i] = lm87_read_value(client, - LM87_REG_AIN(i)); - data->in_max[6+i] = lm87_read_value(client, - LM87_REG_AIN_MAX(i)); - data->in_min[6+i] = lm87_read_value(client, - LM87_REG_AIN_MIN(i)); - - } else { - data->fan[i] = lm87_read_value(client, - LM87_REG_FAN(i)); - data->fan_min[i] = lm87_read_value(client, - LM87_REG_FAN_MIN(i)); - } - } - - j = (data->channel & CHAN_TEMP3) ? 3 : 2; - for (i = 0 ; i < j; i++) { - data->temp[i] = lm87_read_value(client, - LM87_REG_TEMP[i]); - data->temp_high[i] = lm87_read_value(client, - LM87_REG_TEMP_HIGH[i]); - data->temp_low[i] = lm87_read_value(client, - LM87_REG_TEMP_LOW[i]); - } - - i = lm87_read_value(client, LM87_REG_TEMP_HW_INT_LOCK); - j = lm87_read_value(client, LM87_REG_TEMP_HW_INT); - data->temp_crit_int = min(i, j); - - i = lm87_read_value(client, LM87_REG_TEMP_HW_EXT_LOCK); - j = lm87_read_value(client, LM87_REG_TEMP_HW_EXT); - data->temp_crit_ext = min(i, j); - - i = lm87_read_value(client, LM87_REG_VID_FAN_DIV); - data->fan_div[0] = (i >> 4) & 0x03; - data->fan_div[1] = (i >> 6) & 0x03; - data->vid = (i & 0x0F) - | (lm87_read_value(client, LM87_REG_VID4) & 0x01) - << 4; - - data->alarms = lm87_read_value(client, LM87_REG_ALARMS1) - | (lm87_read_value(client, LM87_REG_ALARMS2) - << 8); - data->aout = lm87_read_value(client, LM87_REG_AOUT); - - data->last_updated = jiffies; - data->valid = 1; - } - - mutex_unlock(&data->update_lock); +/* + * Driver data (common to all clients) + */ - return data; -} +static const struct i2c_device_id lm87_id[] = { + { "lm87", lm87 }, + { "adm1024", adm1024 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm87_id); -static int __init sensors_lm87_init(void) -{ - return i2c_add_driver(&lm87_driver); -} +static struct i2c_driver lm87_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "lm87", + }, + .probe = lm87_probe, + .remove = lm87_remove, + .id_table = lm87_id, + .detect = lm87_detect, + .address_list = normal_i2c, +}; -static void __exit sensors_lm87_exit(void) -{ - i2c_del_driver(&lm87_driver); -} +module_i2c_driver(lm87_driver); -MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org> and others"); +MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de> and others"); MODULE_DESCRIPTION("LM87 driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_lm87_init); -module_exit(sensors_lm87_exit); diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 7c9bdc16742..c9ff08dbe10 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1,7 +1,7 @@ /* * lm90.c - Part of lm_sensors, Linux kernel modules for hardware * monitoring - * Copyright (C) 2003-2009 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2003-2010 Jean Delvare <jdelvare@suse.de> * * Based on the lm83 driver. The LM90 is a sensor chip made by National * Semiconductor. It reports up to two temperatures (its own plus up to @@ -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,10 +44,26 @@ * chips. The MAX6680 and MAX6681 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 - * temperature value registers. + * 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 ADT7461 and ADT7461A from Analog Devices as well as + * NCT1008 from ON Semiconductor. The chips are supported in both compatibility + * and extended mode. They are mostly compatible with LM90 except for a data + * format difference for the temperature value registers. + * + * This driver also supports the SA56004 from Philips. This device is + * pin-compatible with the LM86, the ED/EDP parts are also address-compatible. + * + * This driver also supports the G781 from GMT. This device is compatible + * with the ADM1032. + * + * This driver also supports TMP451 from Texas Instruments. This device is + * supported in both compatibility and extended mode. It's mostly compatible + * with ADT7461 except for local temperature low byte register and max + * conversion rate. * * Since the LM90 was the first chipset supported by this driver, most * comments will refer to this chipset, but are actually general and @@ -76,24 +94,30 @@ #include <linux/err.h> #include <linux/mutex.h> #include <linux/sysfs.h> +#include <linux/interrupt.h> +#include <linux/regulator/consumer.h> /* * 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. - * ADM1032-2, ADT7461-2, LM89-1, LM99-1 and MAX6646 have address 0x4d. + * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, ADT7461A, MAX6649, + * MAX6657, MAX6658, NCT1008 and W83L771 have address 0x4c. + * ADM1032-2, ADT7461-2, ADT7461A-2, LM89-1, LM99-1, MAX6646, and NCT1008D + * 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. + * SA56004 can have address 0x48 through 0x4F. */ static const unsigned short normal_i2c[] = { - 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; + 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x48, 0x49, 0x4a, 0x4b, 0x4c, + 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; -enum chips { lm90, adm1032, lm99, lm86, max6657, adt7461, max6680, max6646 }; +enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, + max6646, w83l771, max6696, sa56004, g781, tmp451 }; /* * The LM90 registers @@ -134,25 +158,53 @@ 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 */ +/* SA56004 registers */ + +#define SA56004_REG_R_LOCAL_TEMPL 0x22 + +#define LM90_DEF_CONVRATE_RVAL 6 /* Def conversion rate register value */ +#define LM90_MAX_CONVRATE_MS 16000 /* Maximum conversion rate in ms */ + +/* TMP451 registers */ +#define TMP451_REG_R_LOCAL_TEMPL 0x15 /* - * 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 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_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 */ + +/* LM90 status */ +#define LM90_STATUS_LTHRM (1 << 0) /* local THERM limit tripped */ +#define LM90_STATUS_RTHRM (1 << 1) /* remote THERM limit tripped */ +#define LM90_STATUS_ROPEN (1 << 2) /* remote is an open circuit */ +#define LM90_STATUS_RLOW (1 << 3) /* remote low temp limit tripped */ +#define LM90_STATUS_RHIGH (1 << 4) /* remote high temp limit tripped */ +#define LM90_STATUS_LLOW (1 << 5) /* local low temp limit tripped */ +#define LM90_STATUS_LHIGH (1 << 6) /* local high temp limit tripped */ + +#define MAX6696_STATUS2_R2THRM (1 << 1) /* remote2 THERM limit tripped */ +#define MAX6696_STATUS2_R2OPEN (1 << 2) /* remote2 is an open circuit */ +#define MAX6696_STATUS2_R2LOW (1 << 3) /* remote2 low temp limit tripped */ +#define MAX6696_STATUS2_R2HIGH (1 << 4) /* remote2 high temp limit tripped */ +#define MAX6696_STATUS2_ROT2 (1 << 5) /* remote emergency limit tripped */ +#define MAX6696_STATUS2_R2OT2 (1 << 6) /* remote2 emergency limit tripped */ +#define MAX6696_STATUS2_LOT2 (1 << 7) /* local emergency limit tripped */ /* * Driver data (common to all clients) @@ -161,6 +213,8 @@ static struct lm90_data *lm90_update_device(struct device *dev); static const struct i2c_device_id lm90_id[] = { { "adm1032", adm1032 }, { "adt7461", adt7461 }, + { "adt7461a", adt7461 }, + { "g781", g781 }, { "lm90", lm90 }, { "lm86", lm86 }, { "lm89", lm86 }, @@ -170,23 +224,140 @@ 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 }, + { "nct1008", adt7461 }, + { "w83l771", w83l771 }, + { "sa56004", sa56004 }, + { "tmp451", tmp451 }, { } }; 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 */ + u8 reg_local_ext; /* Extended local temp register (optional) */ +}; + +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, }, - .probe = lm90_probe, - .remove = lm90_remove, - .id_table = lm90_id, - .detect = lm90_detect, - .address_list = normal_i2c, + [adt7461] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_BROKEN_ALERT, + .alert_alarms = 0x7c, + .max_convrate = 10, + }, + [g781] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_BROKEN_ALERT, + .alert_alarms = 0x7c, + .max_convrate = 8, + }, + [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] = { + .alert_alarms = 0x7c, + .max_convrate = 6, + .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, + }, + [max6657] = { + .alert_alarms = 0x7c, + .max_convrate = 8, + .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, + }, + [max6659] = { + .flags = LM90_HAVE_EMERGENCY, + .alert_alarms = 0x7c, + .max_convrate = 8, + .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, + }, + [max6680] = { + .flags = LM90_HAVE_OFFSET, + .alert_alarms = 0x7c, + .max_convrate = 7, + }, + [max6696] = { + .flags = LM90_HAVE_EMERGENCY + | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3, + .alert_alarms = 0x1c7c, + .max_convrate = 6, + .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, + }, + [w83l771] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, + .alert_alarms = 0x7c, + .max_convrate = 8, + }, + [sa56004] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, + .alert_alarms = 0x7b, + .max_convrate = 9, + .reg_local_ext = SA56004_REG_R_LOCAL_TEMPL, + }, + [tmp451] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT + | LM90_HAVE_BROKEN_ALERT, + .alert_alarms = 0x7c, + .max_convrate = 9, + .reg_local_ext = TMP451_REG_R_LOCAL_TEMPL, + } +}; + +/* + * TEMP8 register index + */ +enum lm90_temp8_reg_index { + LOCAL_LOW = 0, + LOCAL_HIGH, + LOCAL_CRIT, + REMOTE_CRIT, + LOCAL_EMERG, /* max6659 and max6695/96 */ + REMOTE_EMERG, /* max6659 and max6695/96 */ + REMOTE2_CRIT, /* max6695/96 only */ + REMOTE2_EMERG, /* max6695/96 only */ + TEMP8_REG_NUM +}; + +/* + * TEMP11 register index + */ +enum lm90_temp11_reg_index { + REMOTE_TEMP = 0, + REMOTE_LOW, + REMOTE_HIGH, + REMOTE_OFFSET, /* except max6646, max6657/58/59, and max6695/96 */ + LOCAL_TEMP, + REMOTE2_TEMP, /* max6695/96 only */ + REMOTE2_LOW, /* max6695/96 only */ + REMOTE2_HIGH, /* max6695/96 only */ + TEMP11_REG_NUM }; /* @@ -194,28 +365,270 @@ static struct i2c_driver lm90_driver = { */ struct lm90_data { + struct i2c_client *client; struct device *hwmon_dev; + const struct attribute_group *groups[6]; struct mutex update_lock; + struct regulator *regulator; 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 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 */ + u8 reg_local_ext; /* local extension register offset */ /* registers values */ - s8 temp8[4]; /* 0: local low limit - 1: local high limit - 2: local critical limit - 3: remote critical limit */ - s16 temp11[5]; /* 0: remote input - 1: remote low limit - 2: remote high limit - 3: remote offset (except max6646 and max6657) - 4: local input */ + s8 temp8[TEMP8_REG_NUM]; + s16 temp11[TEMP11_REG_NUM]; 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 + * between 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 lm90_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long next_update; + + mutex_lock(&data->update_lock); + + next_update = data->last_updated + + msecs_to_jiffies(data->update_interval); + 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[LOCAL_LOW]); + lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, + &data->temp8[LOCAL_HIGH]); + lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, + &data->temp8[LOCAL_CRIT]); + lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, + &data->temp8[REMOTE_CRIT]); + lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst); + + if (data->reg_local_ext) { + lm90_read16(client, LM90_REG_R_LOCAL_TEMP, + data->reg_local_ext, + &data->temp11[LOCAL_TEMP]); + } else { + if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, + &h) == 0) + data->temp11[LOCAL_TEMP] = h << 8; + } + lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, + LM90_REG_R_REMOTE_TEMPL, + &data->temp11[REMOTE_TEMP]); + + if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0) { + data->temp11[REMOTE_LOW] = h << 8; + if ((data->flags & LM90_HAVE_REM_LIMIT_EXT) + && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, + &l) == 0) + data->temp11[REMOTE_LOW] |= l; + } + if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0) { + data->temp11[REMOTE_HIGH] = h << 8; + if ((data->flags & LM90_HAVE_REM_LIMIT_EXT) + && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, + &l) == 0) + data->temp11[REMOTE_HIGH] |= 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[REMOTE_OFFSET] = (h << 8) | l; + } + if (data->flags & LM90_HAVE_EMERGENCY) { + lm90_read_reg(client, MAX6659_REG_R_LOCAL_EMERG, + &data->temp8[LOCAL_EMERG]); + lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG, + &data->temp8[REMOTE_EMERG]); + } + 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[REMOTE2_CRIT]); + lm90_read_reg(client, MAX6659_REG_R_REMOTE_EMERG, + &data->temp8[REMOTE2_EMERG]); + lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, + LM90_REG_R_REMOTE_TEMPL, + &data->temp11[REMOTE2_TEMP]); + if (!lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h)) + data->temp11[REMOTE2_LOW] = h << 8; + if (!lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h)) + data->temp11[REMOTE2_HIGH] = 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. @@ -353,7 +766,7 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, struct lm90_data *data = lm90_update_device(dev); int temp; - if (data->kind == adt7461) + if (data->kind == adt7461 || data->kind == tmp451) temp = temp_from_u8_adt7461(data, data->temp8[attr->index]); else if (data->kind == max6646) temp = temp_from_u8(data->temp8[attr->index]); @@ -370,31 +783,44 @@ 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[TEMP8_REG_NUM] = { 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); + struct lm90_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; int nr = attr->index; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; /* +16 degrees offset for temp2 for the LM99 */ if (data->kind == lm99 && attr->index == 3) val -= 16000; mutex_lock(&data->update_lock); - if (data->kind == adt7461) + if (data->kind == adt7461 || data->kind == tmp451) data->temp8[nr] = temp_to_u8_adt7461(data, val); else if (data->kind == max6646) 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; } @@ -402,11 +828,11 @@ 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; - if (data->kind == adt7461) + if (data->kind == adt7461 || data->kind == tmp451) temp = temp_from_u16_adt7461(data, data->temp11[attr->index]); else if (data->kind == max6646) temp = temp_from_u16(data->temp11[attr->index]); @@ -423,53 +849,65 @@ 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 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; + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct lm90_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int nr = attr->nr; + int index = attr->index; + long val; + int err; + + err = kstrtol(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; + if (data->kind == adt7461 || data->kind == tmp451) + 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); struct lm90_data *data = lm90_update_device(dev); int temp; - if (data->kind == adt7461) + if (data->kind == adt7461 || data->kind == tmp451) temp = temp_from_u8_adt7461(data, data->temp8[attr->index]); else if (data->kind == max6646) temp = temp_from_u8(data->temp8[attr->index]); @@ -486,18 +924,23 @@ static ssize_t show_temphyst(struct device *dev, struct device_attribute *devatt static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm90_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + struct lm90_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + long val; + int err; int temp; + err = kstrtol(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]); + if (data->kind == adt7461 || data->kind == tmp451) + temp = temp_from_u8_adt7461(data, data->temp8[LOCAL_CRIT]); else if (data->kind == max6646) - temp = temp_from_u8(data->temp8[2]); + temp = temp_from_u8(data->temp8[LOCAL_CRIT]); else - temp = temp_from_s8(data->temp8[2]); + temp = temp_from_s8(data->temp8[LOCAL_CRIT]); data->temp_hyst = hyst_to_reg(temp - val); i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, @@ -523,25 +966,56 @@ 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 lm90_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + lm90_set_convrate(client, data, clamp_val(val, 0, 100000)); + mutex_unlock(&data->update_lock); + + return count; +} + +static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp11, NULL, + 0, LOCAL_TEMP); +static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp11, NULL, + 0, REMOTE_TEMP); 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); + set_temp8, LOCAL_LOW); +static SENSOR_DEVICE_ATTR_2(temp2_min, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 0, REMOTE_LOW); 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); + set_temp8, LOCAL_HIGH); +static SENSOR_DEVICE_ATTR_2(temp2_max, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 1, REMOTE_HIGH); static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 2); + set_temp8, LOCAL_CRIT); static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 3); + set_temp8, REMOTE_CRIT); 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); + set_temphyst, LOCAL_CRIT); +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, + REMOTE_CRIT); +static SENSOR_DEVICE_ATTR_2(temp2_offset, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 2, REMOTE_OFFSET); /* Individual alarm files */ static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0); @@ -554,6 +1028,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, @@ -574,6 +1051,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 }; @@ -581,6 +1059,97 @@ static const struct attribute_group lm90_group = { .attrs = lm90_attributes, }; +static struct attribute *lm90_temp2_offset_attributes[] = { + &sensor_dev_attr_temp2_offset.dev_attr.attr, + NULL +}; + +static const struct attribute_group lm90_temp2_offset_group = { + .attrs = lm90_temp2_offset_attributes, +}; + +/* + * Additional attributes for devices with emergency sensors + */ +static SENSOR_DEVICE_ATTR(temp1_emergency, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, LOCAL_EMERG); +static SENSOR_DEVICE_ATTR(temp2_emergency, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, REMOTE_EMERG); +static SENSOR_DEVICE_ATTR(temp1_emergency_hyst, S_IRUGO, show_temphyst, + NULL, LOCAL_EMERG); +static SENSOR_DEVICE_ATTR(temp2_emergency_hyst, S_IRUGO, show_temphyst, + NULL, REMOTE_EMERG); + +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, REMOTE2_TEMP); +static SENSOR_DEVICE_ATTR_2(temp3_min, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 3, REMOTE2_LOW); +static SENSOR_DEVICE_ATTR_2(temp3_max, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 4, REMOTE2_HIGH); +static SENSOR_DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, REMOTE2_CRIT); +static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_temphyst, NULL, + REMOTE2_CRIT); +static SENSOR_DEVICE_ATTR(temp3_emergency, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, REMOTE2_EMERG); +static SENSOR_DEVICE_ATTR(temp3_emergency_hyst, S_IRUGO, show_temphyst, + NULL, REMOTE2_EMERG); + +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) @@ -593,7 +1162,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 = kstrtol(buf, 10, &val); + if (err < 0) + return err; switch (val) { case 0: @@ -615,75 +1189,38 @@ 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, +static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info) { - struct i2c_adapter *adapter = new_client->adapter; - int address = new_client->addr; + struct i2c_adapter *adapter = client->adapter; + int address = client->addr; const char *name = NULL; - int man_id, chip_id, reg_config1, reg_convrate; + int man_id, chip_id, config1, config2, convrate; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; /* detection and identification */ - if ((man_id = i2c_smbus_read_byte_data(new_client, - LM90_REG_R_MAN_ID)) < 0 - || (chip_id = i2c_smbus_read_byte_data(new_client, - LM90_REG_R_CHIP_ID)) < 0 - || (reg_config1 = i2c_smbus_read_byte_data(new_client, - LM90_REG_R_CONFIG1)) < 0 - || (reg_convrate = i2c_smbus_read_byte_data(new_client, - LM90_REG_R_CONVRATE)) < 0) + man_id = i2c_smbus_read_byte_data(client, LM90_REG_R_MAN_ID); + chip_id = i2c_smbus_read_byte_data(client, LM90_REG_R_CHIP_ID); + config1 = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG1); + convrate = i2c_smbus_read_byte_data(client, LM90_REG_R_CONVRATE); + if (man_id < 0 || chip_id < 0 || config1 < 0 || convrate < 0) return -ENODEV; - if ((address == 0x4C || address == 0x4D) - && man_id == 0x01) { /* National Semiconductor */ - int reg_config2; - - reg_config2 = i2c_smbus_read_byte_data(new_client, - LM90_REG_R_CONFIG2); - if (reg_config2 < 0) + if (man_id == 0x01 || man_id == 0x5C || man_id == 0x41) { + config2 = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG2); + if (config2 < 0) return -ENODEV; + } else + config2 = 0; /* Make compiler happy */ - if ((reg_config1 & 0x2A) == 0x00 - && (reg_config2 & 0xF8) == 0x00 - && reg_convrate <= 0x09) { + if ((address == 0x4C || address == 0x4D) + && man_id == 0x01) { /* National Semiconductor */ + if ((config1 & 0x2A) == 0x00 + && (config2 & 0xF8) == 0x00 + && convrate <= 0x09) { if (address == 0x4C && (chip_id & 0xF0) == 0x20) { /* LM90 */ name = "lm90"; @@ -707,22 +1244,48 @@ static int lm90_detect(struct i2c_client *new_client, if ((address == 0x4C || address == 0x4D) && man_id == 0x41) { /* Analog Devices */ if ((chip_id & 0xF0) == 0x40 /* ADM1032 */ - && (reg_config1 & 0x3F) == 0x00 - && reg_convrate <= 0x0A) { + && (config1 & 0x3F) == 0x00 + && convrate <= 0x0A) { name = "adm1032"; - /* The ADM1032 supports PEC, but only if combined - transactions are not used. */ + /* + * The ADM1032 supports PEC, but only if combined + * transactions are not used. + */ if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) info->flags |= I2C_CLIENT_PEC; } else if (chip_id == 0x51 /* ADT7461 */ - && (reg_config1 & 0x1B) == 0x00 - && reg_convrate <= 0x0A) { + && (config1 & 0x1B) == 0x00 + && convrate <= 0x0A) { name = "adt7461"; + } else + if (chip_id == 0x57 /* ADT7461A, NCT1008 */ + && (config1 & 0x1B) == 0x00 + && convrate <= 0x0A) { + name = "adt7461a"; } } else if (man_id == 0x4D) { /* Maxim */ + int emerg, emerg2, 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. + */ + emerg = i2c_smbus_read_byte_data(client, + MAX6659_REG_R_REMOTE_EMERG); + man_id = i2c_smbus_read_byte_data(client, + LM90_REG_R_MAN_ID); + emerg2 = i2c_smbus_read_byte_data(client, + MAX6659_REG_R_REMOTE_EMERG); + status2 = i2c_smbus_read_byte_data(client, + MAX6696_REG_R_STATUS2); + if (emerg < 0 || man_id < 0 || emerg2 < 0 || status2 < 0) + return -ENODEV; + /* * The MAX6657, MAX6658 and MAX6659 do NOT have a chip_id * register. Reading from that address will return the last @@ -730,12 +1293,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) - && (reg_config1 & 0x1F) == (man_id & 0x0F) - && reg_convrate <= 0x09) { - name = "max6657"; + && (address == 0x4C || address == 0x4D || address == 0x4E) + && (config1 & 0x1F) == (man_id & 0x0F) + && convrate <= 0x09) { + 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 + && (config1 & 0x10) == 0x00 + && (status2 & 0x01) == 0x00 + && emerg == emerg2 + && convrate <= 0x07) { + name = "max6696"; } else /* * The chip_id register of the MAX6680 and MAX6681 holds the @@ -744,8 +1333,8 @@ static int lm90_detect(struct i2c_client *new_client, * second to last bit of config1 (software reset). */ if (chip_id == 0x01 - && (reg_config1 & 0x03) == 0x00 - && reg_convrate <= 0x07) { + && (config1 & 0x03) == 0x00 + && convrate <= 0x07) { name = "max6680"; } else /* @@ -754,10 +1343,53 @@ static int lm90_detect(struct i2c_client *new_client, * register are unused and should return zero when read. */ if (chip_id == 0x59 - && (reg_config1 & 0x3f) == 0x00 - && reg_convrate <= 0x07) { + && (config1 & 0x3f) == 0x00 + && convrate <= 0x07) { name = "max6646"; } + } else + if (address == 0x4C + && man_id == 0x5C) { /* Winbond/Nuvoton */ + if ((config1 & 0x2A) == 0x00 + && (config2 & 0xF8) == 0x00) { + if (chip_id == 0x01 /* W83L771W/G */ + && convrate <= 0x09) { + name = "w83l771"; + } else + if ((chip_id & 0xFE) == 0x10 /* W83L771AWG/ASG */ + && convrate <= 0x08) { + name = "w83l771"; + } + } + } else + if (address >= 0x48 && address <= 0x4F + && man_id == 0xA1) { /* NXP Semiconductor/Philips */ + if (chip_id == 0x00 + && (config1 & 0x2A) == 0x00 + && (config2 & 0xFE) == 0x00 + && convrate <= 0x09) { + name = "sa56004"; + } + } else + if ((address == 0x4C || address == 0x4D) + && man_id == 0x47) { /* GMT */ + if (chip_id == 0x01 /* G781 */ + && (config1 & 0x3F) == 0x00 + && convrate <= 0x08) + name = "g781"; + } else + if (address == 0x4C + && man_id == 0x55) { /* Texas Instruments */ + int local_ext; + + local_ext = i2c_smbus_read_byte_data(client, + TMP451_REG_R_LOCAL_TEMPL); + + if (chip_id == 0x00 /* TMP451 */ + && (config1 & 0x1B) == 0x00 + && convrate <= 0x09 + && (local_ext & 0x0F) == 0x00) + name = "tmp451"; } if (!name) { /* identification failed */ @@ -772,80 +1404,37 @@ static int lm90_detect(struct i2c_client *new_client, return 0; } -static int lm90_probe(struct i2c_client *new_client, - const struct i2c_device_id *id) +static void lm90_restore_conf(struct i2c_client *client, struct lm90_data *data) { - struct i2c_adapter *adapter = to_i2c_adapter(new_client->dev.parent); - struct lm90_data *data; - int err; - - data = kzalloc(sizeof(struct lm90_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } - i2c_set_clientdata(new_client, data); - mutex_init(&data->update_lock); - - /* Set the device type */ - data->kind = id->driver_data; - if (data->kind == adm1032) { - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) - new_client->flags &= ~I2C_CLIENT_PEC; - } - - /* Initialize the LM90 chip */ - lm90_init_client(new_client); - - /* Register sysfs hooks */ - if ((err = sysfs_create_group(&new_client->dev.kobj, &lm90_group))) - goto exit_free; - if (new_client->flags & I2C_CLIENT_PEC) { - if ((err = device_create_file(&new_client->dev, - &dev_attr_pec))) - 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))) - goto exit_remove_files; - } - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - -exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &lm90_group); - device_remove_file(&new_client->dev, &dev_attr_pec); -exit_free: - kfree(data); -exit: - return err; + /* 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); } -static void lm90_init_client(struct i2c_client *client) +static void lm90_init_client(struct i2c_client *client, struct lm90_data *data) { - u8 config, config_orig; - struct lm90_data *data = i2c_get_clientdata(client); + u8 config, convrate; + + 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. */ - i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, - 5); /* 2 Hz */ + 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; } - config_orig = config; + data->config_orig = config; /* Check Temperature Range Select */ - if (data->kind == adt7461) { + if (data->kind == adt7461 || data->kind == tmp451) { if (config & 0x04) data->flags |= LM90_FLAG_ADT7461_EXT; } @@ -855,139 +1444,226 @@ static void lm90_init_client(struct i2c_client *client) * 0.125 degree resolution) and range (0x08, extend range * to -64 degree) mode for the remote temperature sensor. */ - if (data->kind == max6680) { + if (data->kind == max6680) config |= 0x18; - } + + /* + * Select external channel 0 for max6695/96 + */ + if (data->kind == max6696) + config &= ~0x08; config &= 0xBF; /* run */ - if (config != config_orig) /* Only write if changed */ + 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) +static bool lm90_is_tripped(struct i2c_client *client, u16 *status) { struct lm90_data *data = i2c_get_clientdata(client); + u8 st, st2 = 0; + + lm90_read_reg(client, LM90_REG_R_STATUS, &st); + + if (data->kind == max6696) + lm90_read_reg(client, MAX6696_REG_R_STATUS2, &st2); + + *status = st | (st2 << 8); + + if ((st & 0x7f) == 0 && (st2 & 0xfe) == 0) + return false; + + if ((st & (LM90_STATUS_LLOW | LM90_STATUS_LHIGH | LM90_STATUS_LTHRM)) || + (st2 & MAX6696_STATUS2_LOT2)) + dev_warn(&client->dev, + "temp%d out of range, please check!\n", 1); + if ((st & (LM90_STATUS_RLOW | LM90_STATUS_RHIGH | LM90_STATUS_RTHRM)) || + (st2 & MAX6696_STATUS2_ROT2)) + dev_warn(&client->dev, + "temp%d out of range, please check!\n", 2); + if (st & LM90_STATUS_ROPEN) + dev_warn(&client->dev, + "temp%d diode open, please check!\n", 2); + if (st2 & (MAX6696_STATUS2_R2LOW | MAX6696_STATUS2_R2HIGH | + MAX6696_STATUS2_R2THRM | MAX6696_STATUS2_R2OT2)) + dev_warn(&client->dev, + "temp%d out of range, please check!\n", 3); + if (st2 & MAX6696_STATUS2_R2OPEN) + dev_warn(&client->dev, + "temp%d diode open, please check!\n", 3); + + return true; +} - 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); +static irqreturn_t lm90_irq_thread(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + u16 status; - kfree(data); - return 0; + if (lm90_is_tripped(client, &status)) + return IRQ_HANDLED; + else + return IRQ_NONE; } -static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value) +static int lm90_probe(struct i2c_client *client, + const struct i2c_device_id *id) { + struct device *dev = &client->dev; + struct i2c_adapter *adapter = to_i2c_adapter(dev->parent); + struct lm90_data *data; + struct regulator *regulator; + int groups = 0; 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))) + regulator = devm_regulator_get(dev, "vcc"); + if (IS_ERR(regulator)) + return PTR_ERR(regulator); + + err = regulator_enable(regulator); + if (err < 0) { + dev_err(dev, "Failed to enable regulator: %d\n", err); return err; - if (oldh != newh) { - err = lm90_read_reg(client, regl, &l); - if (err) - return err; } - *value = (newh << 8) | l; - return 0; -} + data = devm_kzalloc(dev, sizeof(struct lm90_data), GFP_KERNEL); + if (!data) + return -ENOMEM; -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); + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); - mutex_lock(&data->update_lock); + data->regulator = regulator; - if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { - u8 h, l; + /* Set the device type */ + data->kind = id->driver_data; + if (data->kind == adm1032) { + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) + client->flags &= ~I2C_CLIENT_PEC; + } - 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); + /* + * Different devices have different alarm bits triggering the + * ALERT# output + */ + data->alert_alarms = lm90_params[data->kind].alert_alarms; - 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]); + /* Set chip capabilities */ + data->flags = lm90_params[data->kind].flags; + data->reg_local_ext = lm90_params[data->kind].reg_local_ext; - 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; - } + /* Set maximum conversion rate */ + data->max_convrate = lm90_params[data->kind].max_convrate; - 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); + /* Initialize the LM90 chip */ + lm90_init_client(client, data); - data->last_updated = jiffies; - data->valid = 1; + /* Register sysfs hooks */ + data->groups[groups++] = &lm90_group; + + if (data->flags & LM90_HAVE_OFFSET) + data->groups[groups++] = &lm90_temp2_offset_group; + + if (data->flags & LM90_HAVE_EMERGENCY) + data->groups[groups++] = &lm90_emergency_group; + + if (data->flags & LM90_HAVE_EMERGENCY_ALARM) + data->groups[groups++] = &lm90_emergency_alarm_group; + + if (data->flags & LM90_HAVE_TEMP3) + data->groups[groups++] = &lm90_temp3_group; + + if (client->flags & I2C_CLIENT_PEC) { + err = device_create_file(dev, &dev_attr_pec); + if (err) + goto exit_restore; } - mutex_unlock(&data->update_lock); + data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name, + data, data->groups); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove_pec; + } - return data; + if (client->irq) { + dev_dbg(dev, "IRQ: %d\n", client->irq); + err = devm_request_threaded_irq(dev, client->irq, + NULL, lm90_irq_thread, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "lm90", client); + if (err < 0) { + dev_err(dev, "cannot request IRQ %d\n", client->irq); + goto exit_unregister; + } + } + + return 0; + +exit_unregister: + hwmon_device_unregister(data->hwmon_dev); +exit_remove_pec: + device_remove_file(dev, &dev_attr_pec); +exit_restore: + lm90_restore_conf(client, data); + regulator_disable(data->regulator); + + return err; } -static int __init sensors_lm90_init(void) +static int lm90_remove(struct i2c_client *client) { - return i2c_add_driver(&lm90_driver); + struct lm90_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + device_remove_file(&client->dev, &dev_attr_pec); + lm90_restore_conf(client, data); + regulator_disable(data->regulator); + + return 0; } -static void __exit sensors_lm90_exit(void) +static void lm90_alert(struct i2c_client *client, unsigned int flag) { - i2c_del_driver(&lm90_driver); + u16 alarms; + + if (lm90_is_tripped(client, &alarms)) { + /* + * Disable ALERT# output, because these chips don't implement + * SMBus alert correctly; they should only hold the alert line + * low briefly. + */ + struct lm90_data *data = i2c_get_clientdata(client); + + if ((data->flags & LM90_HAVE_BROKEN_ALERT) + && (alarms & data->alert_alarms)) { + u8 config; + dev_dbg(&client->dev, "Disabling ALERT#\n"); + lm90_read_reg(client, LM90_REG_R_CONFIG1, &config); + i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, + config | 0x80); + } + } else { + dev_info(&client->dev, "Everything OK\n"); + } } -MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); +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, +}; + +module_i2c_driver(lm90_driver); + +MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); MODULE_DESCRIPTION("LM90/ADM1032 driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_lm90_init); -module_exit(sensors_lm90_exit); diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c index 7c31e6205f8..d2060e245ff 100644 --- a/drivers/hwmon/lm92.c +++ b/drivers/hwmon/lm92.c @@ -1,6 +1,6 @@ /* * lm92 - Hardware monitoring driver - * Copyright (C) 2005-2008 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2005-2008 Jean Delvare <jdelvare@suse.de> * * Based on the lm90 driver, with some ideas taken from the lm_sensors * lm92 driver as well. @@ -34,10 +34,6 @@ * 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> @@ -48,9 +44,12 @@ #include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/jiffies.h> -/* The LM92 and MAX6635 have 2 two-state pins for address selection, - resulting in 4 possible addresses. */ +/* + * The LM92 and MAX6635 have 2 two-state pins for address selection, + * resulting in 4 possible addresses. + */ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, I2C_CLIENT_END }; @@ -63,11 +62,13 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, #define LM92_REG_TEMP_HIGH 0x05 /* 16-bit, RW */ #define LM92_REG_MAN_ID 0x07 /* 16-bit, RO, LM92 only */ -/* The LM92 uses signed 13-bit values with LSB = 0.0625 degree Celsius, - left-justified in 16-bit registers. No rounding is done, with such - a resolution it's just not worth it. Note that the MAX6635 doesn't - make use of the 4 lower bits for limits (i.e. effective resolution - for limits is 1 degree Celsius). */ +/* + * The LM92 uses signed 13-bit values with LSB = 0.0625 degree Celsius, + * left-justified in 16-bit registers. No rounding is done, with such + * a resolution it's just not worth it. Note that the MAX6635 doesn't + * make use of the 4 lower bits for limits (i.e. effective resolution + * for limits is 1 degree Celsius). + */ static inline int TEMP_FROM_REG(s16 reg) { return reg / 8 * 625 / 10; @@ -88,46 +89,53 @@ static inline u8 ALARMS_FROM_REG(s16 reg) return reg & 0x0007; } -/* Driver data (common to all clients) */ -static struct i2c_driver lm92_driver; +enum temp_index { + t_input, + t_crit, + t_min, + t_max, + t_hyst, + t_num_regs +}; + +static const u8 regs[t_num_regs] = { + [t_input] = LM92_REG_TEMP, + [t_crit] = LM92_REG_TEMP_CRIT, + [t_min] = LM92_REG_TEMP_LOW, + [t_max] = LM92_REG_TEMP_HIGH, + [t_hyst] = LM92_REG_TEMP_HYST, +}; /* Client data (each client gets its own) */ struct lm92_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ /* registers values */ - s16 temp1_input, temp1_crit, temp1_min, temp1_max, temp1_hyst; + s16 temp[t_num_regs]; /* index with enum temp_index */ }; - /* * Sysfs attributes and callback functions */ static struct lm92_data *lm92_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct lm92_data *data = i2c_get_clientdata(client); + struct lm92_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int i; mutex_lock(&data->update_lock); if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { dev_dbg(&client->dev, "Updating lm92 data\n"); - data->temp1_input = swab16(i2c_smbus_read_word_data(client, - LM92_REG_TEMP)); - data->temp1_hyst = swab16(i2c_smbus_read_word_data(client, - LM92_REG_TEMP_HYST)); - data->temp1_crit = swab16(i2c_smbus_read_word_data(client, - LM92_REG_TEMP_CRIT)); - data->temp1_min = swab16(i2c_smbus_read_word_data(client, - LM92_REG_TEMP_LOW)); - data->temp1_max = swab16(i2c_smbus_read_word_data(client, - LM92_REG_TEMP_HIGH)); - + for (i = 0; i < t_num_regs; i++) { + data->temp[i] = + i2c_smbus_read_word_swapped(client, regs[i]); + } data->last_updated = jiffies; data->valid = 1; } @@ -137,73 +145,80 @@ static struct lm92_data *lm92_update_device(struct device *dev) return data; } -#define show_temp(value) \ -static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm92_data *data = lm92_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \ -} -show_temp(temp1_input); -show_temp(temp1_crit); -show_temp(temp1_min); -show_temp(temp1_max); - -#define set_temp(value, reg) \ -static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct lm92_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - mutex_lock(&data->update_lock); \ - data->value = TEMP_TO_REG(val); \ - i2c_smbus_write_word_data(client, reg, swab16(data->value)); \ - mutex_unlock(&data->update_lock); \ - return count; \ +static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm92_data *data = lm92_update_device(dev); + + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])); } -set_temp(temp1_crit, LM92_REG_TEMP_CRIT); -set_temp(temp1_min, LM92_REG_TEMP_LOW); -set_temp(temp1_max, LM92_REG_TEMP_HIGH); -static ssize_t show_temp1_crit_hyst(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t set_temp(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) { - struct lm92_data *data = lm92_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_crit) - - TEMP_FROM_REG(data->temp1_hyst)); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm92_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int nr = attr->index; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + data->temp[nr] = TEMP_TO_REG(val); + i2c_smbus_write_word_swapped(client, regs[nr], data->temp[nr]); + mutex_unlock(&data->update_lock); + return count; } -static ssize_t show_temp1_max_hyst(struct device *dev, struct device_attribute *attr, char *buf) + +static ssize_t show_temp_hyst(struct device *dev, + struct device_attribute *devattr, char *buf) { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm92_data *data = lm92_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_max) - - TEMP_FROM_REG(data->temp1_hyst)); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index]) + - TEMP_FROM_REG(data->temp[t_hyst])); } -static ssize_t show_temp1_min_hyst(struct device *dev, struct device_attribute *attr, char *buf) + +static ssize_t show_temp_min_hyst(struct device *dev, + struct device_attribute *attr, char *buf) { struct lm92_data *data = lm92_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_min) - + TEMP_FROM_REG(data->temp1_hyst)); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[t_min]) + + TEMP_FROM_REG(data->temp[t_hyst])); } -static ssize_t set_temp1_crit_hyst(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) +static ssize_t set_temp_hyst(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm92_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm92_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - data->temp1_hyst = TEMP_FROM_REG(data->temp1_crit) - val; - i2c_smbus_write_word_data(client, LM92_REG_TEMP_HYST, - swab16(TEMP_TO_REG(data->temp1_hyst))); + data->temp[t_hyst] = TEMP_FROM_REG(data->temp[attr->index]) - val; + i2c_smbus_write_word_swapped(client, LM92_REG_TEMP_HYST, + TEMP_TO_REG(data->temp[t_hyst])); mutex_unlock(&data->update_lock); return count; } -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, + char *buf) { struct lm92_data *data = lm92_update_device(dev); - return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->temp1_input)); + return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->temp[t_input])); } static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, @@ -211,26 +226,25 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, { int bitnr = to_sensor_dev_attr(attr)->index; struct lm92_data *data = lm92_update_device(dev); - return sprintf(buf, "%d\n", (data->temp1_input >> bitnr) & 1); + return sprintf(buf, "%d\n", (data->temp[t_input] >> bitnr) & 1); } -static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp1_input, NULL); -static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp1_crit, - set_temp1_crit); -static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp1_crit_hyst, - set_temp1_crit_hyst); -static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp1_min, - set_temp1_min); -static DEVICE_ATTR(temp1_min_hyst, S_IRUGO, show_temp1_min_hyst, NULL); -static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp1_max, - set_temp1_max); -static DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp1_max_hyst, NULL); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, t_input); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp, set_temp, + t_crit); +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp_hyst, + set_temp_hyst, t_crit); +static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp, set_temp, + t_min); +static DEVICE_ATTR(temp1_min_hyst, S_IRUGO, show_temp_min_hyst, NULL); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp, set_temp, + t_max); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp_hyst, NULL, t_max); static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 2); static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 1); - /* * Detection and registration */ @@ -246,26 +260,30 @@ static void lm92_init_client(struct i2c_client *client) config & 0xFE); } -/* The MAX6635 has no identification register, so we have to use tricks - to identify it reliably. This is somewhat slow. - Note that we do NOT rely on the 2 MSB of the configuration register - always reading 0, as suggested by the datasheet, because it was once - reported not to be true. */ +/* + * The MAX6635 has no identification register, so we have to use tricks + * to identify it reliably. This is somewhat slow. + * Note that we do NOT rely on the 2 MSB of the configuration register + * always reading 0, as suggested by the datasheet, because it was once + * reported not to be true. + */ static int max6635_check(struct i2c_client *client) { u16 temp_low, temp_high, temp_hyst, temp_crit; u8 conf; int i; - /* No manufacturer ID register, so a read from this address will - always return the last read value. */ + /* + * No manufacturer ID register, so a read from this address will + * always return the last read value. + */ temp_low = i2c_smbus_read_word_data(client, LM92_REG_TEMP_LOW); if (i2c_smbus_read_word_data(client, LM92_REG_MAN_ID) != temp_low) return 0; temp_high = i2c_smbus_read_word_data(client, LM92_REG_TEMP_HIGH); if (i2c_smbus_read_word_data(client, LM92_REG_MAN_ID) != temp_high) return 0; - + /* Limits are stored as integer values (signed, 9-bit). */ if ((temp_low & 0x7f00) || (temp_high & 0x7f00)) return 0; @@ -274,46 +292,45 @@ static int max6635_check(struct i2c_client *client) if ((temp_hyst & 0x7f00) || (temp_crit & 0x7f00)) return 0; - /* Registers addresses were found to cycle over 16-byte boundaries. - We don't test all registers with all offsets so as to save some - reads and time, but this should still be sufficient to dismiss - non-MAX6635 chips. */ + /* + * Registers addresses were found to cycle over 16-byte boundaries. + * We don't test all registers with all offsets so as to save some + * reads and time, but this should still be sufficient to dismiss + * non-MAX6635 chips. + */ conf = i2c_smbus_read_byte_data(client, LM92_REG_CONFIG); - for (i=16; i<96; i*=2) { + for (i = 16; i < 96; i *= 2) { if (temp_hyst != i2c_smbus_read_word_data(client, - LM92_REG_TEMP_HYST + i - 16) + LM92_REG_TEMP_HYST + i - 16) || temp_crit != i2c_smbus_read_word_data(client, - LM92_REG_TEMP_CRIT + i) + LM92_REG_TEMP_CRIT + i) || temp_low != i2c_smbus_read_word_data(client, LM92_REG_TEMP_LOW + i + 16) || temp_high != i2c_smbus_read_word_data(client, - LM92_REG_TEMP_HIGH + i + 32) + LM92_REG_TEMP_HIGH + i + 32) || conf != i2c_smbus_read_byte_data(client, - LM92_REG_CONFIG + i)) + LM92_REG_CONFIG + i)) return 0; } return 1; } -static struct attribute *lm92_attributes[] = { - &dev_attr_temp1_input.attr, - &dev_attr_temp1_crit.attr, - &dev_attr_temp1_crit_hyst.attr, - &dev_attr_temp1_min.attr, +static struct attribute *lm92_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, &dev_attr_temp1_min_hyst.attr, - &dev_attr_temp1_max.attr, - &dev_attr_temp1_max_hyst.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, &dev_attr_alarms.attr, &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, NULL }; - -static const struct attribute_group lm92_group = { - .attrs = lm92_attributes, -}; +ATTRIBUTE_GROUPS(lm92); /* Return 0 if detection is successful, -ENODEV otherwise */ static int lm92_detect(struct i2c_client *new_client, @@ -345,51 +362,24 @@ static int lm92_detect(struct i2c_client *new_client, static int lm92_probe(struct i2c_client *new_client, const struct i2c_device_id *id) { + struct device *hwmon_dev; struct lm92_data *data; - int err; - data = kzalloc(sizeof(struct lm92_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&new_client->dev, sizeof(struct lm92_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; - i2c_set_clientdata(new_client, data); - data->valid = 0; + data->client = new_client; mutex_init(&data->update_lock); /* Initialize the chipset */ lm92_init_client(new_client); - /* Register sysfs hooks */ - if ((err = sysfs_create_group(&new_client->dev.kobj, &lm92_group))) - goto exit_free; - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove; - } - - return 0; - -exit_remove: - sysfs_remove_group(&new_client->dev.kobj, &lm92_group); -exit_free: - kfree(data); -exit: - return err; -} - -static int lm92_remove(struct i2c_client *client) -{ - struct lm92_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &lm92_group); - - kfree(data); - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(&new_client->dev, + new_client->name, + data, lm92_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } @@ -410,25 +400,13 @@ static struct i2c_driver lm92_driver = { .name = "lm92", }, .probe = lm92_probe, - .remove = lm92_remove, .id_table = lm92_id, .detect = lm92_detect, .address_list = normal_i2c, }; -static int __init sensors_lm92_init(void) -{ - return i2c_add_driver(&lm92_driver); -} - -static void __exit sensors_lm92_exit(void) -{ - i2c_del_driver(&lm92_driver); -} +module_i2c_driver(lm92_driver); -MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); +MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); MODULE_DESCRIPTION("LM92/MAX6635 driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_lm92_init); -module_exit(sensors_lm92_exit); diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c index 6669255aadc..6c2df576f25 100644 --- a/drivers/hwmon/lm93.c +++ b/drivers/hwmon/lm93.c @@ -1,42 +1,42 @@ /* - lm93.c - Part of lm_sensors, Linux kernel modules for hardware monitoring - - Author/Maintainer: Mark M. Hoffman <mhoffman@lightlink.com> - Copyright (c) 2004 Utilitek Systems, Inc. - - derived in part from lm78.c: - Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> - - derived in part from lm85.c: - Copyright (c) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com> - Copyright (c) 2003 Margit Schubert-While <margitsw@t-online.de> - - derived in part from w83l785ts.c: - Copyright (c) 2003-2004 Jean Delvare <khali@linux-fr.org> - - Ported to Linux 2.6 by Eric J. Bowersox <ericb@aspsys.com> - Copyright (c) 2005 Aspen Systems, Inc. - - Adapted to 2.6.20 by Carsten Emde <cbe@osadl.org> - Copyright (c) 2006 Carsten Emde, Open Source Automation Development Lab - - Modified for mainline integration by Hans J. Koch <hjk@linutronix.de> - Copyright (c) 2007 Hans J. Koch, Linutronix GmbH - - 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. -*/ + * lm93.c - Part of lm_sensors, Linux kernel modules for hardware monitoring + * + * Author/Maintainer: Mark M. Hoffman <mhoffman@lightlink.com> + * Copyright (c) 2004 Utilitek Systems, Inc. + * + * derived in part from lm78.c: + * Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> + * + * derived in part from lm85.c: + * Copyright (c) 2002, 2003 Philip Pokorny <ppokorny@penguincomputing.com> + * Copyright (c) 2003 Margit Schubert-While <margitsw@t-online.de> + * + * derived in part from w83l785ts.c: + * Copyright (c) 2003-2004 Jean Delvare <jdelvare@suse.de> + * + * Ported to Linux 2.6 by Eric J. Bowersox <ericb@aspsys.com> + * Copyright (c) 2005 Aspen Systems, Inc. + * + * Adapted to 2.6.20 by Carsten Emde <cbe@osadl.org> + * Copyright (c) 2006 Carsten Emde, Open Source Automation Development Lab + * + * Modified for mainline integration by Hans J. Koch <hjk@hansjkoch.de> + * Copyright (c) 2007 Hans J. Koch, Linutronix GmbH + * + * 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> @@ -47,6 +47,7 @@ #include <linux/hwmon-vid.h> #include <linux/err.h> #include <linux/delay.h> +#include <linux/jiffies.h> /* LM93 REGISTER ADDRESSES */ @@ -83,7 +84,7 @@ #define LM93_REG_FAN_MIN(nr) (0xb4 + (nr) * 2) /* pwm outputs: pwm1-pwm2 (nr => 0-1, reg => 0-3) */ -#define LM93_REG_PWM_CTL(nr,reg) (0xc8 + (reg) + (nr) * 4) +#define LM93_REG_PWM_CTL(nr, reg) (0xc8 + (reg) + (nr) * 4) #define LM93_PWM_CTL1 0x0 #define LM93_PWM_CTL2 0x1 #define LM93_PWM_CTL3 0x2 @@ -135,6 +136,11 @@ #define LM93_MFR_ID 0x73 #define LM93_MFR_ID_PROTOTYPE 0x72 +/* LM94 REGISTER VALUES */ +#define LM94_MFR_ID_2 0x7a +#define LM94_MFR_ID 0x79 +#define LM94_MFR_ID_PROTOTYPE 0x78 + /* SMBus capabilities */ #define LM93_SMBUS_FUNC_FULL (I2C_FUNC_SMBUS_BYTE_DATA | \ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA) @@ -146,16 +152,16 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; /* Insmod parameters */ -static int disable_block; +static bool disable_block; module_param(disable_block, bool, 0); MODULE_PARM_DESC(disable_block, "Set to non-zero to disable SMBus block data transactions."); -static int init; +static bool init; module_param(init, bool, 0); MODULE_PARM_DESC(init, "Set to non-zero to force chip initialization."); -static int vccp_limit_type[2] = {0,0}; +static int vccp_limit_type[2] = {0, 0}; module_param_array(vccp_limit_type, int, NULL, 0); MODULE_PARM_DESC(vccp_limit_type, "Configures in7 and in8 limit modes."); @@ -182,8 +188,10 @@ static const struct { u8 cmd; u8 len; } lm93_block_read_cmds[12] = { { 0xfd, 9 }, }; -/* ALARMS: SYSCTL format described further below - REG: 64 bits in 8 registers, as immediately below */ +/* + * ALARMS: SYSCTL format described further below + * REG: 64 bits in 8 registers, as immediately below + */ struct block1_t { u8 host_status_1; u8 host_status_2; @@ -212,8 +220,10 @@ struct lm93_data { /* register values, arranged by block read groups */ struct block1_t block1; - /* temp1 - temp4: unfiltered readings - temp1 - temp2: filtered readings */ + /* + * temp1 - temp4: unfiltered readings + * temp1 - temp2: filtered readings + */ u8 block2[6]; /* vin1 - vin16: readings */ @@ -290,14 +300,18 @@ struct lm93_data { u8 sfc2; u8 sf_tach_to_pwm; - /* The two PWM CTL2 registers can read something other than what was - last written for the OVR_DC field (duty cycle override). So, we - save the user-commanded value here. */ + /* + * The two PWM CTL2 registers can read something other than what was + * last written for the OVR_DC field (duty cycle override). So, we + * save the user-commanded value here. + */ u8 pwm_override[2]; }; -/* VID: mV - REG: 6-bits, right justified, *always* using Intel VRM/VRD 10 */ +/* + * VID: mV + * REG: 6-bits, right justified, *always* using Intel VRM/VRD 10 + */ static int LM93_VID_FROM_REG(u8 reg) { return vid_from_reg((reg & 0x3f), 100); @@ -312,12 +326,13 @@ static const u8 lm93_vin_reg_max[16] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd1, }; -/* Values from the datasheet. They're here for documentation only. -static const u8 lm93_vin_reg_nom[16] = { - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, - 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x40, 0xc0, -}; -*/ +/* + * Values from the datasheet. They're here for documentation only. + * static const u8 lm93_vin_reg_nom[16] = { + * 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + * 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x40, 0xc0, + * }; + */ /* min, max, and nominal voltage readings, per channel (mV)*/ static const unsigned long lm93_vin_val_min[16] = { @@ -329,92 +344,101 @@ static const unsigned long lm93_vin_val_max[16] = { 1236, 1236, 1236, 1600, 2000, 2000, 1600, 1600, 4400, 6500, 3333, 2625, 1312, 1312, 1236, 3600, }; -/* Values from the datasheet. They're here for documentation only. -static const unsigned long lm93_vin_val_nom[16] = { - 927, 927, 927, 1200, 1500, 1500, 1200, 1200, - 3300, 5000, 2500, 1969, 984, 984, 309, 3300, -}; -*/ +/* + * Values from the datasheet. They're here for documentation only. + * static const unsigned long lm93_vin_val_nom[16] = { + * 927, 927, 927, 1200, 1500, 1500, 1200, 1200, + * 3300, 5000, 2500, 1969, 984, 984, 309, 3300, + * }; + */ static unsigned LM93_IN_FROM_REG(int nr, u8 reg) { - const long uV_max = lm93_vin_val_max[nr] * 1000; - const long uV_min = lm93_vin_val_min[nr] * 1000; + const long uv_max = lm93_vin_val_max[nr] * 1000; + const long uv_min = lm93_vin_val_min[nr] * 1000; - const long slope = (uV_max - uV_min) / + const long slope = (uv_max - uv_min) / (lm93_vin_reg_max[nr] - lm93_vin_reg_min[nr]); - const long intercept = uV_min - slope * lm93_vin_reg_min[nr]; + const long intercept = uv_min - slope * lm93_vin_reg_min[nr]; return (slope * reg + intercept + 500) / 1000; } -/* IN: mV, limits determined by channel nr - REG: scaling determined by channel nr */ +/* + * IN: mV, limits determined by channel nr + * REG: scaling determined by channel nr + */ static u8 LM93_IN_TO_REG(int nr, unsigned val) { /* range limit */ - const long mV = SENSORS_LIMIT(val, - lm93_vin_val_min[nr], lm93_vin_val_max[nr]); + const long mv = clamp_val(val, + lm93_vin_val_min[nr], lm93_vin_val_max[nr]); /* try not to lose too much precision here */ - const long uV = mV * 1000; - const long uV_max = lm93_vin_val_max[nr] * 1000; - const long uV_min = lm93_vin_val_min[nr] * 1000; + const long uv = mv * 1000; + const long uv_max = lm93_vin_val_max[nr] * 1000; + const long uv_min = lm93_vin_val_min[nr] * 1000; /* convert */ - const long slope = (uV_max - uV_min) / + const long slope = (uv_max - uv_min) / (lm93_vin_reg_max[nr] - lm93_vin_reg_min[nr]); - const long intercept = uV_min - slope * lm93_vin_reg_min[nr]; + const long intercept = uv_min - slope * lm93_vin_reg_min[nr]; - u8 result = ((uV - intercept + (slope/2)) / slope); - result = SENSORS_LIMIT(result, - lm93_vin_reg_min[nr], lm93_vin_reg_max[nr]); + u8 result = ((uv - intercept + (slope/2)) / slope); + result = clamp_val(result, + lm93_vin_reg_min[nr], lm93_vin_reg_max[nr]); return result; } /* vid in mV, upper == 0 indicates low limit, otherwise upper limit */ static unsigned LM93_IN_REL_FROM_REG(u8 reg, int upper, int vid) { - const long uV_offset = upper ? (((reg >> 4 & 0x0f) + 1) * 12500) : + const long uv_offset = upper ? (((reg >> 4 & 0x0f) + 1) * 12500) : (((reg >> 0 & 0x0f) + 1) * -25000); - const long uV_vid = vid * 1000; - return (uV_vid + uV_offset + 5000) / 10000; + const long uv_vid = vid * 1000; + return (uv_vid + uv_offset + 5000) / 10000; } -#define LM93_IN_MIN_FROM_REG(reg,vid) LM93_IN_REL_FROM_REG(reg,0,vid) -#define LM93_IN_MAX_FROM_REG(reg,vid) LM93_IN_REL_FROM_REG(reg,1,vid) +#define LM93_IN_MIN_FROM_REG(reg, vid) LM93_IN_REL_FROM_REG((reg), 0, (vid)) +#define LM93_IN_MAX_FROM_REG(reg, vid) LM93_IN_REL_FROM_REG((reg), 1, (vid)) -/* vid in mV , upper == 0 indicates low limit, otherwise upper limit - upper also determines which nibble of the register is returned - (the other nibble will be 0x0) */ +/* + * vid in mV , upper == 0 indicates low limit, otherwise upper limit + * upper also determines which nibble of the register is returned + * (the other nibble will be 0x0) + */ static u8 LM93_IN_REL_TO_REG(unsigned val, int upper, int vid) { - long uV_offset = vid * 1000 - val * 10000; + long uv_offset = vid * 1000 - val * 10000; if (upper) { - uV_offset = SENSORS_LIMIT(uV_offset, 12500, 200000); - return (u8)((uV_offset / 12500 - 1) << 4); + uv_offset = clamp_val(uv_offset, 12500, 200000); + return (u8)((uv_offset / 12500 - 1) << 4); } else { - uV_offset = SENSORS_LIMIT(uV_offset, -400000, -25000); - return (u8)((uV_offset / -25000 - 1) << 0); + uv_offset = clamp_val(uv_offset, -400000, -25000); + return (u8)((uv_offset / -25000 - 1) << 0); } } -/* TEMP: 1/1000 degrees C (-128C to +127C) - REG: 1C/bit, two's complement */ +/* + * TEMP: 1/1000 degrees C (-128C to +127C) + * REG: 1C/bit, two's complement + */ static int LM93_TEMP_FROM_REG(u8 reg) { return (s8)reg * 1000; } #define LM93_TEMP_MIN (-128000) -#define LM93_TEMP_MAX ( 127000) +#define LM93_TEMP_MAX (127000) -/* TEMP: 1/1000 degrees C (-128C to +127C) - REG: 1C/bit, two's complement */ +/* + * TEMP: 1/1000 degrees C (-128C to +127C) + * REG: 1C/bit, two's complement + */ static u8 LM93_TEMP_TO_REG(long temp) { - int ntemp = SENSORS_LIMIT(temp, LM93_TEMP_MIN, LM93_TEMP_MAX); - ntemp += (ntemp<0 ? -500 : 500); + int ntemp = clamp_val(temp, LM93_TEMP_MIN, LM93_TEMP_MAX); + ntemp += (ntemp < 0 ? -500 : 500); return (u8)(ntemp / 1000); } @@ -425,26 +449,30 @@ static int LM93_TEMP_OFFSET_MODE_FROM_REG(u8 sfc2, int nr) return sfc2 & (nr < 2 ? 0x10 : 0x20); } -/* This function is common to all 4-bit temperature offsets - reg is 4 bits right justified - mode 0 => 1C/bit, mode !0 => 0.5C/bit */ +/* + * This function is common to all 4-bit temperature offsets + * reg is 4 bits right justified + * mode 0 => 1C/bit, mode !0 => 0.5C/bit + */ static int LM93_TEMP_OFFSET_FROM_REG(u8 reg, int mode) { return (reg & 0x0f) * (mode ? 5 : 10); } -#define LM93_TEMP_OFFSET_MIN ( 0) +#define LM93_TEMP_OFFSET_MIN (0) #define LM93_TEMP_OFFSET_MAX0 (150) -#define LM93_TEMP_OFFSET_MAX1 ( 75) +#define LM93_TEMP_OFFSET_MAX1 (75) -/* This function is common to all 4-bit temperature offsets - returns 4 bits right justified - mode 0 => 1C/bit, mode !0 => 0.5C/bit */ +/* + * This function is common to all 4-bit temperature offsets + * returns 4 bits right justified + * mode 0 => 1C/bit, mode !0 => 0.5C/bit + */ static u8 LM93_TEMP_OFFSET_TO_REG(int off, int mode) { int factor = mode ? 5 : 10; - off = SENSORS_LIMIT(off, LM93_TEMP_OFFSET_MIN, + off = clamp_val(off, LM93_TEMP_OFFSET_MIN, mode ? LM93_TEMP_OFFSET_MAX1 : LM93_TEMP_OFFSET_MAX0); return (u8)((off + factor/2) / factor); } @@ -461,9 +489,11 @@ static int LM93_TEMP_AUTO_OFFSET_FROM_REG(u8 reg, int nr, int mode) return LM93_TEMP_OFFSET_FROM_REG(reg >> 4 & 0x0f, mode); } -/* TEMP: 1/10 degrees C (0C to +15C (mode 0) or +7.5C (mode non-zero)) - REG: 1.0C/bit (mode 0) or 0.5C/bit (mode non-zero) - 0 <= nr <= 3 */ +/* + * TEMP: 1/10 degrees C (0C to +15C (mode 0) or +7.5C (mode non-zero)) + * REG: 1.0C/bit (mode 0) or 0.5C/bit (mode non-zero) + * 0 <= nr <= 3 + */ static u8 LM93_TEMP_AUTO_OFFSET_TO_REG(u8 old, int off, int nr, int mode) { u8 new = LM93_TEMP_OFFSET_TO_REG(off, mode); @@ -527,9 +557,12 @@ static u8 LM93_AUTO_BOOST_HYST_TO_REG(struct lm93_data *data, long hyst, return reg; } -/* PWM: 0-255 per sensors documentation - REG: 0-13 as mapped below... right justified */ -typedef enum { LM93_PWM_MAP_HI_FREQ, LM93_PWM_MAP_LO_FREQ } pwm_freq_t; +/* + * PWM: 0-255 per sensors documentation + * REG: 0-13 as mapped below... right justified + */ +enum pwm_freq { LM93_PWM_MAP_HI_FREQ, LM93_PWM_MAP_LO_FREQ }; + static int lm93_pwm_map[2][16] = { { 0x00, /* 0.00% */ 0x40, /* 25.00% */ @@ -553,13 +586,13 @@ static int lm93_pwm_map[2][16] = { }, }; -static int LM93_PWM_FROM_REG(u8 reg, pwm_freq_t freq) +static int LM93_PWM_FROM_REG(u8 reg, enum pwm_freq freq) { return lm93_pwm_map[freq][reg & 0x0f]; } /* round up to nearest match */ -static u8 LM93_PWM_TO_REG(int pwm, pwm_freq_t freq) +static u8 LM93_PWM_TO_REG(int pwm, enum pwm_freq freq) { int i; for (i = 0; i < 13; i++) @@ -573,7 +606,7 @@ static u8 LM93_PWM_TO_REG(int pwm, pwm_freq_t freq) static int LM93_FAN_FROM_REG(u16 regs) { const u16 count = le16_to_cpu(regs) >> 2; - return count==0 ? -1 : count==0x3fff ? 0: 1350000 / count; + return count == 0 ? -1 : count == 0x3fff ? 0 : 1350000 / count; } /* @@ -587,16 +620,18 @@ static u16 LM93_FAN_TO_REG(long rpm) if (rpm == 0) { count = 0x3fff; } else { - rpm = SENSORS_LIMIT(rpm, 1, 1000000); - count = SENSORS_LIMIT((1350000 + rpm) / rpm, 1, 0x3ffe); + rpm = clamp_val(rpm, 1, 1000000); + count = clamp_val((1350000 + rpm) / rpm, 1, 0x3ffe); } regs = count << 2; return cpu_to_le16(regs); } -/* PWM FREQ: HZ - REG: 0-7 as mapped below */ +/* + * PWM FREQ: HZ + * REG: 0-7 as mapped below + */ static int lm93_pwm_freq_map[8] = { 22500, 96, 84, 72, 60, 48, 36, 12 }; @@ -618,8 +653,10 @@ static u8 LM93_PWM_FREQ_TO_REG(int freq) return (u8)i; } -/* TIME: 1/100 seconds - * REG: 0-7 as mapped below */ +/* + * TIME: 1/100 seconds + * REG: 0-7 as mapped below + */ static int lm93_spinup_time_map[8] = { 0, 10, 25, 40, 70, 100, 200, 400, }; @@ -649,24 +686,30 @@ static int LM93_RAMP_FROM_REG(u8 reg) return (reg & 0x0f) * 5; } -/* RAMP: 1/100 seconds - REG: 50mS/bit 4-bits right justified */ +/* + * RAMP: 1/100 seconds + * REG: 50mS/bit 4-bits right justified + */ static u8 LM93_RAMP_TO_REG(int ramp) { - ramp = SENSORS_LIMIT(ramp, LM93_RAMP_MIN, LM93_RAMP_MAX); + ramp = clamp_val(ramp, LM93_RAMP_MIN, LM93_RAMP_MAX); return (u8)((ramp + 2) / 5); } -/* PROCHOT: 0-255, 0 => 0%, 255 => > 96.6% - * REG: (same) */ +/* + * PROCHOT: 0-255, 0 => 0%, 255 => > 96.6% + * REG: (same) + */ static u8 LM93_PROCHOT_TO_REG(long prochot) { - prochot = SENSORS_LIMIT(prochot, 0, 255); + prochot = clamp_val(prochot, 0, 255); return (u8)prochot; } -/* PROCHOT-INTERVAL: 73 - 37200 (1/100 seconds) - * REG: 0-9 as mapped below */ +/* + * PROCHOT-INTERVAL: 73 - 37200 (1/100 seconds) + * REG: 0-9 as mapped below + */ static int lm93_interval_map[10] = { 73, 146, 290, 580, 1170, 2330, 4660, 9320, 18600, 37200, }; @@ -688,22 +731,25 @@ static u8 LM93_INTERVAL_TO_REG(long interval) return (u8)i; } -/* GPIO: 0-255, GPIO0 is LSB - * REG: inverted */ +/* + * GPIO: 0-255, GPIO0 is LSB + * REG: inverted + */ static unsigned LM93_GPI_FROM_REG(u8 reg) { return ~reg & 0xff; } -/* alarm bitmask definitions - The LM93 has nearly 64 bits of error status... I've pared that down to - what I think is a useful subset in order to fit it into 32 bits. - - Especially note that the #VRD_HOT alarms are missing because we provide - that information as values in another sysfs file. - - If libsensors is extended to support 64 bit values, this could be revisited. -*/ +/* + * alarm bitmask definitions + * The LM93 has nearly 64 bits of error status... I've pared that down to + * what I think is a useful subset in order to fit it into 32 bits. + * + * Especially note that the #VRD_HOT alarms are missing because we provide + * that information as values in another sysfs file. + * + * If libsensors is extended to support 64 bit values, this could be revisited. + */ #define LM93_ALARM_IN1 0x00000001 #define LM93_ALARM_IN2 0x00000002 #define LM93_ALARM_IN3 0x00000004 @@ -767,19 +813,21 @@ static u8 lm93_read_byte(struct i2c_client *client, u8 reg) int value, i; /* retry in case of read errors */ - for (i=1; i<=MAX_RETRIES; i++) { - if ((value = i2c_smbus_read_byte_data(client, reg)) >= 0) { + for (i = 1; i <= MAX_RETRIES; i++) { + value = i2c_smbus_read_byte_data(client, reg); + if (value >= 0) { return value; } else { - dev_warn(&client->dev,"lm93: read byte data failed, " - "address 0x%02x.\n", reg); + dev_warn(&client->dev, + "lm93: read byte data failed, address 0x%02x.\n", + reg); mdelay(i + 3); } } /* <TODO> what to return in case of error? */ - dev_err(&client->dev,"lm93: All read byte retries failed!!\n"); + dev_err(&client->dev, "lm93: All read byte retries failed!!\n"); return 0; } @@ -791,8 +839,9 @@ static int lm93_write_byte(struct i2c_client *client, u8 reg, u8 value) result = i2c_smbus_write_byte_data(client, reg, value); if (result < 0) - dev_warn(&client->dev,"lm93: write byte data failed, " - "0x%02x at address 0x%02x.\n", value, reg); + dev_warn(&client->dev, + "lm93: write byte data failed, 0x%02x at address 0x%02x.\n", + value, reg); return result; } @@ -802,19 +851,21 @@ static u16 lm93_read_word(struct i2c_client *client, u8 reg) int value, i; /* retry in case of read errors */ - for (i=1; i<=MAX_RETRIES; i++) { - if ((value = i2c_smbus_read_word_data(client, reg)) >= 0) { + for (i = 1; i <= MAX_RETRIES; i++) { + value = i2c_smbus_read_word_data(client, reg); + if (value >= 0) { return value; } else { - dev_warn(&client->dev,"lm93: read word data failed, " - "address 0x%02x.\n", reg); + dev_warn(&client->dev, + "lm93: read word data failed, address 0x%02x.\n", + reg); mdelay(i + 3); } } /* <TODO> what to return in case of error? */ - dev_err(&client->dev,"lm93: All read word retries failed!!\n"); + dev_err(&client->dev, "lm93: All read word retries failed!!\n"); return 0; } @@ -826,8 +877,9 @@ static int lm93_write_word(struct i2c_client *client, u8 reg, u16 value) result = i2c_smbus_write_word_data(client, reg, value); if (result < 0) - dev_warn(&client->dev,"lm93: write word data failed, " - "0x%04x at address 0x%02x.\n", value, reg); + dev_warn(&client->dev, + "lm93: write word data failed, 0x%04x at address 0x%02x.\n", + value, reg); return result; } @@ -835,13 +887,13 @@ static int lm93_write_word(struct i2c_client *client, u8 reg, u16 value) static u8 lm93_block_buffer[I2C_SMBUS_BLOCK_MAX]; /* - read block data into values, retry if not expected length - fbn => index to lm93_block_read_cmds table - (Fixed Block Number - section 14.5.2 of LM93 datasheet) -*/ + * read block data into values, retry if not expected length + * fbn => index to lm93_block_read_cmds table + * (Fixed Block Number - section 14.5.2 of LM93 datasheet) + */ static void lm93_read_block(struct i2c_client *client, u8 fbn, u8 *values) { - int i, result=0; + int i, result = 0; for (i = 1; i <= MAX_RETRIES; i++) { result = i2c_smbus_read_block_data(client, @@ -850,15 +902,16 @@ static void lm93_read_block(struct i2c_client *client, u8 fbn, u8 *values) if (result == lm93_block_read_cmds[fbn].len) { break; } else { - dev_warn(&client->dev,"lm93: block read data failed, " - "command 0x%02x.\n", + dev_warn(&client->dev, + "lm93: block read data failed, command 0x%02x.\n", lm93_block_read_cmds[fbn].cmd); mdelay(i + 3); } } if (result == lm93_block_read_cmds[fbn].len) { - memcpy(values,lm93_block_buffer,lm93_block_read_cmds[fbn].len); + memcpy(values, lm93_block_buffer, + lm93_block_read_cmds[fbn].len); } else { /* <TODO> what to do in case of error? */ } @@ -959,7 +1012,7 @@ static void lm93_update_client_common(struct lm93_data *data, static void lm93_update_client_full(struct lm93_data *data, struct i2c_client *client) { - dev_dbg(&client->dev,"starting device update (block data enabled)\n"); + dev_dbg(&client->dev, "starting device update (block data enabled)\n"); /* in1 - in16: values & limits */ lm93_read_block(client, 3, (u8 *)(data->block3)); @@ -991,10 +1044,10 @@ static void lm93_update_client_full(struct lm93_data *data, static void lm93_update_client_min(struct lm93_data *data, struct i2c_client *client) { - int i,j; + int i, j; u8 *ptr; - dev_dbg(&client->dev,"starting device update (block data disabled)\n"); + dev_dbg(&client->dev, "starting device update (block data disabled)\n"); /* in1 - in16: values & limits */ for (i = 0; i < 16; i++) { @@ -1032,7 +1085,7 @@ static void lm93_update_client_min(struct lm93_data *data, for (i = 0; i < 2; i++) { for (j = 0; j < 4; j++) { data->block9[i][j] = - lm93_read_byte(client, LM93_REG_PWM_CTL(i,j)); + lm93_read_byte(client, LM93_REG_PWM_CTL(i, j)); } } @@ -1092,14 +1145,13 @@ static ssize_t show_in_min(struct device *dev, int vccp = nr - 6; long rc, vid; - if ((nr==6 || nr==7) && (vccp_limit_type[vccp])) { + if ((nr == 6 || nr == 7) && vccp_limit_type[vccp]) { vid = LM93_VID_FROM_REG(data->vid[vccp]); rc = LM93_IN_MIN_FROM_REG(data->vccp_limits[vccp], vid); + } else { + rc = LM93_IN_FROM_REG(nr, data->block7[nr].min); } - else { - rc = LM93_IN_FROM_REG(nr, data->block7[nr].min); \ - } - return sprintf(buf, "%ld\n", rc); \ + return sprintf(buf, "%ld\n", rc); } static ssize_t store_in_min(struct device *dev, struct device_attribute *attr, @@ -1108,20 +1160,24 @@ static ssize_t store_in_min(struct device *dev, struct device_attribute *attr, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); int vccp = nr - 6; long vid; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - if ((nr==6 || nr==7) && (vccp_limit_type[vccp])) { + if ((nr == 6 || nr == 7) && vccp_limit_type[vccp]) { vid = LM93_VID_FROM_REG(data->vid[vccp]); data->vccp_limits[vccp] = (data->vccp_limits[vccp] & 0xf0) | LM93_IN_REL_TO_REG(val, 0, vid); lm93_write_byte(client, LM93_REG_VCCP_LIMIT_OFF(vccp), data->vccp_limits[vccp]); - } - else { - data->block7[nr].min = LM93_IN_TO_REG(nr,val); + } else { + data->block7[nr].min = LM93_IN_TO_REG(nr, val); lm93_write_byte(client, LM93_REG_IN_MIN(nr), data->block7[nr].min); } @@ -1170,14 +1226,13 @@ static ssize_t show_in_max(struct device *dev, int vccp = nr - 6; long rc, vid; - if ((nr==6 || nr==7) && (vccp_limit_type[vccp])) { + if ((nr == 6 || nr == 7) && vccp_limit_type[vccp]) { vid = LM93_VID_FROM_REG(data->vid[vccp]); - rc = LM93_IN_MAX_FROM_REG(data->vccp_limits[vccp],vid); - } - else { - rc = LM93_IN_FROM_REG(nr,data->block7[nr].max); \ + rc = LM93_IN_MAX_FROM_REG(data->vccp_limits[vccp], vid); + } else { + rc = LM93_IN_FROM_REG(nr, data->block7[nr].max); } - return sprintf(buf,"%ld\n",rc); \ + return sprintf(buf, "%ld\n", rc); } static ssize_t store_in_max(struct device *dev, struct device_attribute *attr, @@ -1186,20 +1241,24 @@ static ssize_t store_in_max(struct device *dev, struct device_attribute *attr, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); int vccp = nr - 6; long vid; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - if ((nr==6 || nr==7) && (vccp_limit_type[vccp])) { + if ((nr == 6 || nr == 7) && vccp_limit_type[vccp]) { vid = LM93_VID_FROM_REG(data->vid[vccp]); data->vccp_limits[vccp] = (data->vccp_limits[vccp] & 0x0f) | LM93_IN_REL_TO_REG(val, 1, vid); lm93_write_byte(client, LM93_REG_VCCP_LIMIT_OFF(vccp), data->vccp_limits[vccp]); - } - else { - data->block7[nr].max = LM93_IN_TO_REG(nr,val); + } else { + data->block7[nr].max = LM93_IN_TO_REG(nr, val); lm93_write_byte(client, LM93_REG_IN_MAX(nr), data->block7[nr].max); } @@ -1245,7 +1304,7 @@ static ssize_t show_temp(struct device *dev, { int nr = (to_sensor_dev_attr(attr))->index; struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n",LM93_TEMP_FROM_REG(data->block2[nr])); + return sprintf(buf, "%d\n", LM93_TEMP_FROM_REG(data->block2[nr])); } static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); @@ -1257,7 +1316,7 @@ static ssize_t show_temp_min(struct device *dev, { int nr = (to_sensor_dev_attr(attr))->index; struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n",LM93_TEMP_FROM_REG(data->temp_lim[nr].min)); + return sprintf(buf, "%d\n", LM93_TEMP_FROM_REG(data->temp_lim[nr].min)); } static ssize_t store_temp_min(struct device *dev, struct device_attribute *attr, @@ -1266,7 +1325,12 @@ static ssize_t store_temp_min(struct device *dev, struct device_attribute *attr, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_lim[nr].min = LM93_TEMP_TO_REG(val); @@ -1287,7 +1351,7 @@ static ssize_t show_temp_max(struct device *dev, { int nr = (to_sensor_dev_attr(attr))->index; struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n",LM93_TEMP_FROM_REG(data->temp_lim[nr].max)); + return sprintf(buf, "%d\n", LM93_TEMP_FROM_REG(data->temp_lim[nr].max)); } static ssize_t store_temp_max(struct device *dev, struct device_attribute *attr, @@ -1296,7 +1360,12 @@ static ssize_t store_temp_max(struct device *dev, struct device_attribute *attr, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_lim[nr].max = LM93_TEMP_TO_REG(val); @@ -1317,7 +1386,7 @@ static ssize_t show_temp_auto_base(struct device *dev, { int nr = (to_sensor_dev_attr(attr))->index; struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n",LM93_TEMP_FROM_REG(data->block10.base[nr])); + return sprintf(buf, "%d\n", LM93_TEMP_FROM_REG(data->block10.base[nr])); } static ssize_t store_temp_auto_base(struct device *dev, @@ -1327,7 +1396,12 @@ static ssize_t store_temp_auto_base(struct device *dev, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->block10.base[nr] = LM93_TEMP_TO_REG(val); @@ -1344,11 +1418,11 @@ static SENSOR_DEVICE_ATTR(temp3_auto_base, S_IWUSR | S_IRUGO, show_temp_auto_base, store_temp_auto_base, 2); static ssize_t show_temp_auto_boost(struct device *dev, - struct device_attribute *attr,char *buf) + struct device_attribute *attr, char *buf) { int nr = (to_sensor_dev_attr(attr))->index; struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n",LM93_TEMP_FROM_REG(data->boost[nr])); + return sprintf(buf, "%d\n", LM93_TEMP_FROM_REG(data->boost[nr])); } static ssize_t store_temp_auto_boost(struct device *dev, @@ -1358,7 +1432,12 @@ static ssize_t store_temp_auto_boost(struct device *dev, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->boost[nr] = LM93_TEMP_TO_REG(val); @@ -1381,7 +1460,7 @@ static ssize_t show_temp_auto_boost_hyst(struct device *dev, int nr = (to_sensor_dev_attr(attr))->index; struct lm93_data *data = lm93_update_device(dev); int mode = LM93_TEMP_OFFSET_MODE_FROM_REG(data->sfc2, nr); - return sprintf(buf,"%d\n", + return sprintf(buf, "%d\n", LM93_AUTO_BOOST_HYST_FROM_REGS(data, nr, mode)); } @@ -1392,7 +1471,12 @@ static ssize_t store_temp_auto_boost_hyst(struct device *dev, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); /* force 0.5C/bit mode */ @@ -1424,9 +1508,9 @@ static ssize_t show_temp_auto_offset(struct device *dev, int ofs = s_attr->nr; struct lm93_data *data = lm93_update_device(dev); int mode = LM93_TEMP_OFFSET_MODE_FROM_REG(data->sfc2, nr); - return sprintf(buf,"%d\n", + return sprintf(buf, "%d\n", LM93_TEMP_AUTO_OFFSET_FROM_REG(data->block10.offset[ofs], - nr,mode)); + nr, mode)); } static ssize_t store_temp_auto_offset(struct device *dev, @@ -1438,7 +1522,12 @@ static ssize_t store_temp_auto_offset(struct device *dev, int ofs = s_attr->nr; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); /* force 0.5C/bit mode */ @@ -1534,7 +1623,7 @@ static ssize_t show_temp_auto_pwm_min(struct device *dev, struct lm93_data *data = lm93_update_device(dev); reg = data->auto_pwm_min_hyst[nr/2] >> 4 & 0x0f; ctl4 = data->block9[nr][LM93_PWM_CTL4]; - return sprintf(buf,"%d\n",LM93_PWM_FROM_REG(reg, (ctl4 & 0x07) ? + return sprintf(buf, "%d\n", LM93_PWM_FROM_REG(reg, (ctl4 & 0x07) ? LM93_PWM_MAP_LO_FREQ : LM93_PWM_MAP_HI_FREQ)); } @@ -1545,12 +1634,17 @@ static ssize_t store_temp_auto_pwm_min(struct device *dev, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); u8 reg, ctl4; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); reg = lm93_read_byte(client, LM93_REG_PWM_MIN_HYST(nr)); - ctl4 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr,LM93_PWM_CTL4)); + ctl4 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL4)); reg = (reg & 0x0f) | LM93_PWM_TO_REG(val, (ctl4 & 0x07) ? LM93_PWM_MAP_LO_FREQ : @@ -1577,8 +1671,8 @@ static ssize_t show_temp_auto_offset_hyst(struct device *dev, int nr = (to_sensor_dev_attr(attr))->index; struct lm93_data *data = lm93_update_device(dev); int mode = LM93_TEMP_OFFSET_MODE_FROM_REG(data->sfc2, nr); - return sprintf(buf,"%d\n",LM93_TEMP_OFFSET_FROM_REG( - data->auto_pwm_min_hyst[nr/2], mode)); + return sprintf(buf, "%d\n", LM93_TEMP_OFFSET_FROM_REG( + data->auto_pwm_min_hyst[nr / 2], mode)); } static ssize_t store_temp_auto_offset_hyst(struct device *dev, @@ -1588,8 +1682,13 @@ static ssize_t store_temp_auto_offset_hyst(struct device *dev, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); u8 reg; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); /* force 0.5C/bit mode */ @@ -1621,7 +1720,7 @@ static ssize_t show_fan_input(struct device *dev, int nr = s_attr->index; struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n",LM93_FAN_FROM_REG(data->block5[nr])); + return sprintf(buf, "%d\n", LM93_FAN_FROM_REG(data->block5[nr])); } static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input, NULL, 0); @@ -1635,7 +1734,7 @@ static ssize_t show_fan_min(struct device *dev, int nr = (to_sensor_dev_attr(attr))->index; struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n",LM93_FAN_FROM_REG(data->block8[nr])); + return sprintf(buf, "%d\n", LM93_FAN_FROM_REG(data->block8[nr])); } static ssize_t store_fan_min(struct device *dev, struct device_attribute *attr, @@ -1644,11 +1743,16 @@ static ssize_t store_fan_min(struct device *dev, struct device_attribute *attr, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->block8[nr] = LM93_FAN_TO_REG(val); - lm93_write_word(client,LM93_REG_FAN_MIN(nr),data->block8[nr]); + lm93_write_word(client, LM93_REG_FAN_MIN(nr), data->block8[nr]); mutex_unlock(&data->update_lock); return count; } @@ -1662,18 +1766,19 @@ static SENSOR_DEVICE_ATTR(fan3_min, S_IWUSR | S_IRUGO, static SENSOR_DEVICE_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 3); -/* some tedious bit-twiddling here to deal with the register format: - - data->sf_tach_to_pwm: (tach to pwm mapping bits) - - bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 - T4:P2 T4:P1 T3:P2 T3:P1 T2:P2 T2:P1 T1:P2 T1:P1 - - data->sfc2: (enable bits) - - bit | 3 | 2 | 1 | 0 - T4 T3 T2 T1 -*/ +/* + * some tedious bit-twiddling here to deal with the register format: + * + * data->sf_tach_to_pwm: (tach to pwm mapping bits) + * + * bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + * T4:P2 T4:P1 T3:P2 T3:P1 T2:P2 T2:P1 T1:P2 T1:P1 + * + * data->sfc2: (enable bits) + * + * bit | 3 | 2 | 1 | 0 + * T4 T3 T2 T1 + */ static ssize_t show_fan_smart_tach(struct device *dev, struct device_attribute *attr, char *buf) @@ -1689,11 +1794,13 @@ static ssize_t show_fan_smart_tach(struct device *dev, /* if there's a mapping and it's enabled */ if (mapping && ((data->sfc2 >> nr) & 0x01)) rc = mapping; - return sprintf(buf,"%ld\n",rc); + return sprintf(buf, "%ld\n", rc); } -/* helper function - must grab data->update_lock before calling - fan is 0-3, indicating fan1-fan4 */ +/* + * helper function - must grab data->update_lock before calling + * fan is 0-3, indicating fan1-fan4 + */ static void lm93_write_fan_smart_tach(struct i2c_client *client, struct lm93_data *data, int fan, long value) { @@ -1719,15 +1826,20 @@ static ssize_t store_fan_smart_tach(struct device *dev, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); /* sanity test, ignore the write otherwise */ - if (0 <= val && val <= 2) { + if (val <= 2) { /* can't enable if pwm freq is 22.5KHz */ if (val) { u8 ctl4 = lm93_read_byte(client, - LM93_REG_PWM_CTL(val-1,LM93_PWM_CTL4)); + LM93_REG_PWM_CTL(val - 1, LM93_PWM_CTL4)); if ((ctl4 & 0x07) == 0) val = 0; } @@ -1761,7 +1873,7 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, else /* show present h/w value if manual pwm disabled */ rc = LM93_PWM_FROM_REG(ctl2 >> 4, (ctl4 & 0x07) ? LM93_PWM_MAP_LO_FREQ : LM93_PWM_MAP_HI_FREQ); - return sprintf(buf,"%ld\n",rc); + return sprintf(buf, "%ld\n", rc); } static ssize_t store_pwm(struct device *dev, struct device_attribute *attr, @@ -1770,19 +1882,24 @@ static ssize_t store_pwm(struct device *dev, struct device_attribute *attr, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); u8 ctl2, ctl4; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - ctl2 = lm93_read_byte(client,LM93_REG_PWM_CTL(nr,LM93_PWM_CTL2)); - ctl4 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr,LM93_PWM_CTL4)); - ctl2 = (ctl2 & 0x0f) | LM93_PWM_TO_REG(val,(ctl4 & 0x07) ? + ctl2 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL2)); + ctl4 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL4)); + ctl2 = (ctl2 & 0x0f) | LM93_PWM_TO_REG(val, (ctl4 & 0x07) ? LM93_PWM_MAP_LO_FREQ : LM93_PWM_MAP_HI_FREQ) << 4; /* save user commanded value */ data->pwm_override[nr] = LM93_PWM_FROM_REG(ctl2 >> 4, (ctl4 & 0x07) ? LM93_PWM_MAP_LO_FREQ : LM93_PWM_MAP_HI_FREQ); - lm93_write_byte(client,LM93_REG_PWM_CTL(nr,LM93_PWM_CTL2),ctl2); + lm93_write_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL2), ctl2); mutex_unlock(&data->update_lock); return count; } @@ -1803,7 +1920,7 @@ static ssize_t show_pwm_enable(struct device *dev, rc = ((ctl2 & 0xF0) == 0xF0) ? 0 : 1; else rc = 2; - return sprintf(buf,"%ld\n",rc); + return sprintf(buf, "%ld\n", rc); } static ssize_t store_pwm_enable(struct device *dev, @@ -1813,26 +1930,33 @@ static ssize_t store_pwm_enable(struct device *dev, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); u8 ctl2; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - ctl2 = lm93_read_byte(client,LM93_REG_PWM_CTL(nr,LM93_PWM_CTL2)); + ctl2 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL2)); switch (val) { case 0: ctl2 |= 0xF1; /* enable manual override, set PWM to max */ break; - case 1: ctl2 |= 0x01; /* enable manual override */ + case 1: + ctl2 |= 0x01; /* enable manual override */ break; - case 2: ctl2 &= ~0x01; /* disable manual override */ + case 2: + ctl2 &= ~0x01; /* disable manual override */ break; default: mutex_unlock(&data->update_lock); return -EINVAL; } - lm93_write_byte(client,LM93_REG_PWM_CTL(nr,LM93_PWM_CTL2),ctl2); + lm93_write_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL2), ctl2); mutex_unlock(&data->update_lock); return count; } @@ -1850,12 +1974,14 @@ static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr, u8 ctl4; ctl4 = data->block9[nr][LM93_PWM_CTL4]; - return sprintf(buf,"%d\n",LM93_PWM_FREQ_FROM_REG(ctl4)); + return sprintf(buf, "%d\n", LM93_PWM_FREQ_FROM_REG(ctl4)); } -/* helper function - must grab data->update_lock before calling - pwm is 0-1, indicating pwm1-pwm2 - this disables smart tach for all tach channels bound to the given pwm */ +/* + * helper function - must grab data->update_lock before calling + * pwm is 0-1, indicating pwm1-pwm2 + * this disables smart tach for all tach channels bound to the given pwm + */ static void lm93_disable_fan_smart_tach(struct i2c_client *client, struct lm93_data *data, int pwm) { @@ -1882,17 +2008,22 @@ static ssize_t store_pwm_freq(struct device *dev, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); u8 ctl4; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - ctl4 = lm93_read_byte(client,LM93_REG_PWM_CTL(nr,LM93_PWM_CTL4)); + ctl4 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL4)); ctl4 = (ctl4 & 0xf8) | LM93_PWM_FREQ_TO_REG(val); data->block9[nr][LM93_PWM_CTL4] = ctl4; /* ctl4 == 0 -> 22.5KHz -> disable smart tach */ if (!ctl4) lm93_disable_fan_smart_tach(client, data, nr); - lm93_write_byte(client, LM93_REG_PWM_CTL(nr,LM93_PWM_CTL4), ctl4); + lm93_write_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL4), ctl4); mutex_unlock(&data->update_lock); return count; } @@ -1907,7 +2038,7 @@ static ssize_t show_pwm_auto_channels(struct device *dev, { int nr = (to_sensor_dev_attr(attr))->index; struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n",data->block9[nr][LM93_PWM_CTL1]); + return sprintf(buf, "%d\n", data->block9[nr][LM93_PWM_CTL1]); } static ssize_t store_pwm_auto_channels(struct device *dev, @@ -1917,11 +2048,16 @@ static ssize_t store_pwm_auto_channels(struct device *dev, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - data->block9[nr][LM93_PWM_CTL1] = SENSORS_LIMIT(val, 0, 255); - lm93_write_byte(client, LM93_REG_PWM_CTL(nr,LM93_PWM_CTL1), + data->block9[nr][LM93_PWM_CTL1] = clamp_val(val, 0, 255); + lm93_write_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL1), data->block9[nr][LM93_PWM_CTL1]); mutex_unlock(&data->update_lock); return count; @@ -1933,7 +2069,7 @@ static SENSOR_DEVICE_ATTR(pwm2_auto_channels, S_IWUSR | S_IRUGO, show_pwm_auto_channels, store_pwm_auto_channels, 1); static ssize_t show_pwm_auto_spinup_min(struct device *dev, - struct device_attribute *attr,char *buf) + struct device_attribute *attr, char *buf) { int nr = (to_sensor_dev_attr(attr))->index; struct lm93_data *data = lm93_update_device(dev); @@ -1941,7 +2077,7 @@ static ssize_t show_pwm_auto_spinup_min(struct device *dev, ctl3 = data->block9[nr][LM93_PWM_CTL3]; ctl4 = data->block9[nr][LM93_PWM_CTL4]; - return sprintf(buf,"%d\n", + return sprintf(buf, "%d\n", LM93_PWM_FROM_REG(ctl3 & 0x0f, (ctl4 & 0x07) ? LM93_PWM_MAP_LO_FREQ : LM93_PWM_MAP_HI_FREQ)); } @@ -1953,17 +2089,22 @@ static ssize_t store_pwm_auto_spinup_min(struct device *dev, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); u8 ctl3, ctl4; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - ctl3 = lm93_read_byte(client,LM93_REG_PWM_CTL(nr, LM93_PWM_CTL3)); - ctl4 = lm93_read_byte(client,LM93_REG_PWM_CTL(nr, LM93_PWM_CTL4)); - ctl3 = (ctl3 & 0xf0) | LM93_PWM_TO_REG(val, (ctl4 & 0x07) ? + ctl3 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL3)); + ctl4 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL4)); + ctl3 = (ctl3 & 0xf0) | LM93_PWM_TO_REG(val, (ctl4 & 0x07) ? LM93_PWM_MAP_LO_FREQ : LM93_PWM_MAP_HI_FREQ); data->block9[nr][LM93_PWM_CTL3] = ctl3; - lm93_write_byte(client,LM93_REG_PWM_CTL(nr, LM93_PWM_CTL3), ctl3); + lm93_write_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL3), ctl3); mutex_unlock(&data->update_lock); return count; } @@ -1980,7 +2121,7 @@ static ssize_t show_pwm_auto_spinup_time(struct device *dev, { int nr = (to_sensor_dev_attr(attr))->index; struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n",LM93_SPINUP_TIME_FROM_REG( + return sprintf(buf, "%d\n", LM93_SPINUP_TIME_FROM_REG( data->block9[nr][LM93_PWM_CTL3])); } @@ -1991,14 +2132,19 @@ static ssize_t store_pwm_auto_spinup_time(struct device *dev, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); u8 ctl3; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - ctl3 = lm93_read_byte(client,LM93_REG_PWM_CTL(nr, LM93_PWM_CTL3)); + ctl3 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL3)); ctl3 = (ctl3 & 0x1f) | (LM93_SPINUP_TIME_TO_REG(val) << 5 & 0xe0); data->block9[nr][LM93_PWM_CTL3] = ctl3; - lm93_write_byte(client,LM93_REG_PWM_CTL(nr, LM93_PWM_CTL3), ctl3); + lm93_write_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL3), ctl3); mutex_unlock(&data->update_lock); return count; } @@ -2014,7 +2160,7 @@ static ssize_t show_pwm_auto_prochot_ramp(struct device *dev, struct device_attribute *attr, char *buf) { struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n", + return sprintf(buf, "%d\n", LM93_RAMP_FROM_REG(data->pwm_ramp_ctl >> 4 & 0x0f)); } @@ -2024,8 +2170,13 @@ static ssize_t store_pwm_auto_prochot_ramp(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); u8 ramp; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); ramp = lm93_read_byte(client, LM93_REG_PWM_RAMP_CTL); @@ -2043,7 +2194,7 @@ static ssize_t show_pwm_auto_vrdhot_ramp(struct device *dev, struct device_attribute *attr, char *buf) { struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n", + return sprintf(buf, "%d\n", LM93_RAMP_FROM_REG(data->pwm_ramp_ctl & 0x0f)); } @@ -2053,8 +2204,13 @@ static ssize_t store_pwm_auto_vrdhot_ramp(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); u8 ramp; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); ramp = lm93_read_byte(client, LM93_REG_PWM_RAMP_CTL); @@ -2073,7 +2229,7 @@ static ssize_t show_vid(struct device *dev, struct device_attribute *attr, { int nr = (to_sensor_dev_attr(attr))->index; struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n",LM93_VID_FROM_REG(data->vid[nr])); + return sprintf(buf, "%d\n", LM93_VID_FROM_REG(data->vid[nr])); } static SENSOR_DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL, 0); @@ -2084,7 +2240,7 @@ static ssize_t show_prochot(struct device *dev, struct device_attribute *attr, { int nr = (to_sensor_dev_attr(attr))->index; struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n",data->block4[nr].cur); + return sprintf(buf, "%d\n", data->block4[nr].cur); } static SENSOR_DEVICE_ATTR(prochot1, S_IRUGO, show_prochot, NULL, 0); @@ -2095,7 +2251,7 @@ static ssize_t show_prochot_avg(struct device *dev, { int nr = (to_sensor_dev_attr(attr))->index; struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n",data->block4[nr].avg); + return sprintf(buf, "%d\n", data->block4[nr].avg); } static SENSOR_DEVICE_ATTR(prochot1_avg, S_IRUGO, show_prochot_avg, NULL, 0); @@ -2106,7 +2262,7 @@ static ssize_t show_prochot_max(struct device *dev, { int nr = (to_sensor_dev_attr(attr))->index; struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n",data->prochot_max[nr]); + return sprintf(buf, "%d\n", data->prochot_max[nr]); } static ssize_t store_prochot_max(struct device *dev, @@ -2116,7 +2272,12 @@ static ssize_t store_prochot_max(struct device *dev, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->prochot_max[nr] = LM93_PROCHOT_TO_REG(val); @@ -2138,7 +2299,7 @@ static ssize_t show_prochot_override(struct device *dev, { int nr = (to_sensor_dev_attr(attr))->index; struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n", + return sprintf(buf, "%d\n", (data->prochot_override & prochot_override_mask[nr]) ? 1 : 0); } @@ -2149,7 +2310,12 @@ static ssize_t store_prochot_override(struct device *dev, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); if (val) @@ -2173,11 +2339,11 @@ static ssize_t show_prochot_interval(struct device *dev, int nr = (to_sensor_dev_attr(attr))->index; struct lm93_data *data = lm93_update_device(dev); u8 tmp; - if (nr==1) + if (nr == 1) tmp = (data->prochot_interval & 0xf0) >> 4; else tmp = data->prochot_interval & 0x0f; - return sprintf(buf,"%d\n",LM93_INTERVAL_FROM_REG(tmp)); + return sprintf(buf, "%d\n", LM93_INTERVAL_FROM_REG(tmp)); } static ssize_t store_prochot_interval(struct device *dev, @@ -2187,12 +2353,17 @@ static ssize_t store_prochot_interval(struct device *dev, int nr = (to_sensor_dev_attr(attr))->index; struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); u8 tmp; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); tmp = lm93_read_byte(client, LM93_REG_PROCHOT_INTERVAL); - if (nr==1) + if (nr == 1) tmp = (tmp & 0x0f) | (LM93_INTERVAL_TO_REG(val) << 4); else tmp = (tmp & 0xf0) | LM93_INTERVAL_TO_REG(val); @@ -2212,7 +2383,7 @@ static ssize_t show_prochot_override_duty_cycle(struct device *dev, char *buf) { struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n",data->prochot_override & 0x0f); + return sprintf(buf, "%d\n", data->prochot_override & 0x0f); } static ssize_t store_prochot_override_duty_cycle(struct device *dev, @@ -2221,11 +2392,16 @@ static ssize_t store_prochot_override_duty_cycle(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->prochot_override = (data->prochot_override & 0xf0) | - SENSORS_LIMIT(val, 0, 15); + clamp_val(val, 0, 15); lm93_write_byte(client, LM93_REG_PROCHOT_OVERRIDE, data->prochot_override); mutex_unlock(&data->update_lock); @@ -2240,7 +2416,7 @@ static ssize_t show_prochot_short(struct device *dev, struct device_attribute *attr, char *buf) { struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n",(data->config & 0x10) ? 1 : 0); + return sprintf(buf, "%d\n", (data->config & 0x10) ? 1 : 0); } static ssize_t store_prochot_short(struct device *dev, @@ -2249,7 +2425,12 @@ static ssize_t store_prochot_short(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct lm93_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); if (val) @@ -2269,8 +2450,8 @@ static ssize_t show_vrdhot(struct device *dev, struct device_attribute *attr, { int nr = (to_sensor_dev_attr(attr))->index; struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n", - data->block1.host_status_1 & (1 << (nr+4)) ? 1 : 0); + return sprintf(buf, "%d\n", + data->block1.host_status_1 & (1 << (nr + 4)) ? 1 : 0); } static SENSOR_DEVICE_ATTR(vrdhot1, S_IRUGO, show_vrdhot, NULL, 0); @@ -2280,7 +2461,7 @@ static ssize_t show_gpio(struct device *dev, struct device_attribute *attr, char *buf) { struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n",LM93_GPI_FROM_REG(data->gpi)); + return sprintf(buf, "%d\n", LM93_GPI_FROM_REG(data->gpi)); } static DEVICE_ATTR(gpio, S_IRUGO, show_gpio, NULL); @@ -2289,7 +2470,7 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) { struct lm93_data *data = lm93_update_device(dev); - return sprintf(buf,"%d\n",LM93_ALARMS_FROM_REG(data->block1)); + return sprintf(buf, "%d\n", LM93_ALARMS_FROM_REG(data->block1)); } static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); @@ -2489,14 +2670,14 @@ static void lm93_init_client(struct i2c_client *client) lm93_write_byte(client, LM93_REG_CONFIG, reg | 0x01); /* spin until ready */ - for (i=0; i<20; i++) { + for (i = 0; i < 20; i++) { msleep(10); if ((lm93_read_byte(client, LM93_REG_CONFIG) & 0x80) == 0x80) return; } - dev_warn(&client->dev,"timed out waiting for sensor " - "chip to signal ready!\n"); + dev_warn(&client->dev, + "timed out waiting for sensor chip to signal ready!\n"); } /* Return 0 if detection is successful, -ENODEV otherwise */ @@ -2504,6 +2685,7 @@ static int lm93_detect(struct i2c_client *client, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; int mfr, ver; + const char *name; if (!i2c_check_functionality(adapter, LM93_SMBUS_FUNC_MIN)) return -ENODEV; @@ -2517,14 +2699,24 @@ static int lm93_detect(struct i2c_client *client, struct i2c_board_info *info) } ver = lm93_read_byte(client, LM93_REG_VER); - if (ver != LM93_MFR_ID && ver != LM93_MFR_ID_PROTOTYPE) { + switch (ver) { + case LM93_MFR_ID: + case LM93_MFR_ID_PROTOTYPE: + name = "lm93"; + break; + case LM94_MFR_ID_2: + case LM94_MFR_ID: + case LM94_MFR_ID_PROTOTYPE: + name = "lm94"; + break; + default: dev_dbg(&adapter->dev, "detect failed, bad version id 0x%02x!\n", ver); return -ENODEV; } - strlcpy(info->type, "lm93", I2C_NAME_SIZE); - dev_dbg(&adapter->dev,"loading %s at %d,0x%02x\n", + strlcpy(info->type, name, I2C_NAME_SIZE); + dev_dbg(&adapter->dev, "loading %s at %d, 0x%02x\n", client->name, i2c_adapter_id(client->adapter), client->addr); @@ -2545,26 +2737,21 @@ static int lm93_probe(struct i2c_client *client, dev_dbg(&client->dev, "using SMBus block data transactions\n"); update = lm93_update_client_full; } else if ((LM93_SMBUS_FUNC_MIN & func) == LM93_SMBUS_FUNC_MIN) { - dev_dbg(&client->dev, "disabled SMBus block data " - "transactions\n"); + dev_dbg(&client->dev, + "disabled SMBus block data transactions\n"); update = lm93_update_client_min; } else { - dev_dbg(&client->dev, "detect failed, " - "smbus byte and/or word data not supported!\n"); - err = -ENODEV; - goto err_out; + dev_dbg(&client->dev, + "detect failed, smbus byte and/or word data not supported!\n"); + return -ENODEV; } - data = kzalloc(sizeof(struct lm93_data), GFP_KERNEL); - if (!data) { - dev_dbg(&client->dev, "out of memory!\n"); - err = -ENOMEM; - goto err_out; - } + data = devm_kzalloc(&client->dev, sizeof(struct lm93_data), GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); /* housekeeping */ - data->valid = 0; data->update = update; mutex_init(&data->update_lock); @@ -2573,19 +2760,16 @@ static int lm93_probe(struct i2c_client *client, err = sysfs_create_group(&client->dev.kobj, &lm93_attr_grp); if (err) - goto err_free; + return err; /* Register hwmon driver class */ data->hwmon_dev = hwmon_device_register(&client->dev); - if ( !IS_ERR(data->hwmon_dev)) + if (!IS_ERR(data->hwmon_dev)) return 0; err = PTR_ERR(data->hwmon_dev); dev_err(&client->dev, "error registering hwmon device.\n"); sysfs_remove_group(&client->dev.kobj, &lm93_attr_grp); -err_free: - kfree(data); -err_out: return err; } @@ -2596,12 +2780,12 @@ static int lm93_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &lm93_attr_grp); - kfree(data); return 0; } static const struct i2c_device_id lm93_id[] = { { "lm93", 0 }, + { "lm94", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, lm93_id); @@ -2618,20 +2802,9 @@ static struct i2c_driver lm93_driver = { .address_list = normal_i2c, }; -static int __init lm93_init(void) -{ - return i2c_add_driver(&lm93_driver); -} - -static void __exit lm93_exit(void) -{ - i2c_del_driver(&lm93_driver); -} +module_i2c_driver(lm93_driver); MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>, " - "Hans J. Koch <hjk@linutronix.de"); + "Hans J. Koch <hjk@hansjkoch.de>"); MODULE_DESCRIPTION("LM93 driver"); MODULE_LICENSE("GPL"); - -module_init(lm93_init); -module_exit(lm93_exit); diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c new file mode 100644 index 00000000000..411202bdaf6 --- /dev/null +++ b/drivers/hwmon/lm95234.c @@ -0,0 +1,729 @@ +/* + * Driver for Texas Instruments / National Semiconductor LM95234 + * + * Copyright (c) 2013 Guenter Roeck <linux@roeck-us.net> + * + * Derived from lm95241.c + * Copyright (C) 2008, 2010 Davide Rizzo <elpa.rizzo@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/sysfs.h> + +#define DRVNAME "lm95234" + +static const unsigned short normal_i2c[] = { 0x18, 0x4d, 0x4e, I2C_CLIENT_END }; + +/* LM95234 registers */ +#define LM95234_REG_MAN_ID 0xFE +#define LM95234_REG_CHIP_ID 0xFF +#define LM95234_REG_STATUS 0x02 +#define LM95234_REG_CONFIG 0x03 +#define LM95234_REG_CONVRATE 0x04 +#define LM95234_REG_STS_FAULT 0x07 +#define LM95234_REG_STS_TCRIT1 0x08 +#define LM95234_REG_STS_TCRIT2 0x09 +#define LM95234_REG_TEMPH(x) ((x) + 0x10) +#define LM95234_REG_TEMPL(x) ((x) + 0x20) +#define LM95234_REG_UTEMPH(x) ((x) + 0x19) /* Remote only */ +#define LM95234_REG_UTEMPL(x) ((x) + 0x29) +#define LM95234_REG_REM_MODEL 0x30 +#define LM95234_REG_REM_MODEL_STS 0x38 +#define LM95234_REG_OFFSET(x) ((x) + 0x31) /* Remote only */ +#define LM95234_REG_TCRIT1(x) ((x) + 0x40) +#define LM95234_REG_TCRIT2(x) ((x) + 0x49) /* Remote channel 1,2 */ +#define LM95234_REG_TCRIT_HYST 0x5a + +#define NATSEMI_MAN_ID 0x01 +#define LM95234_CHIP_ID 0x79 + +/* Client data (each client gets its own) */ +struct lm95234_data { + struct i2c_client *client; + struct mutex update_lock; + unsigned long last_updated, interval; /* in jiffies */ + bool valid; /* false until following fields are valid */ + /* registers values */ + int temp[5]; /* temperature (signed) */ + u32 status; /* fault/alarm status */ + u8 tcrit1[5]; /* critical temperature limit */ + u8 tcrit2[2]; /* high temperature limit */ + s8 toffset[4]; /* remote temperature offset */ + u8 thyst; /* common hysteresis */ + + u8 sensor_type; /* temperature sensor type */ +}; + +static int lm95234_read_temp(struct i2c_client *client, int index, int *t) +{ + int val; + u16 temp = 0; + + if (index) { + val = i2c_smbus_read_byte_data(client, + LM95234_REG_UTEMPH(index - 1)); + if (val < 0) + return val; + temp = val << 8; + val = i2c_smbus_read_byte_data(client, + LM95234_REG_UTEMPL(index - 1)); + if (val < 0) + return val; + temp |= val; + *t = temp; + } + /* + * Read signed temperature if unsigned temperature is 0, + * or if this is the local sensor. + */ + if (!temp) { + val = i2c_smbus_read_byte_data(client, + LM95234_REG_TEMPH(index)); + if (val < 0) + return val; + temp = val << 8; + val = i2c_smbus_read_byte_data(client, + LM95234_REG_TEMPL(index)); + if (val < 0) + return val; + temp |= val; + *t = (s16)temp; + } + return 0; +} + +static u16 update_intervals[] = { 143, 364, 1000, 2500 }; + +/* Fill value cache. Must be called with update lock held. */ + +static int lm95234_fill_cache(struct lm95234_data *data, + struct i2c_client *client) +{ + int i, ret; + + ret = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE); + if (ret < 0) + return ret; + + data->interval = msecs_to_jiffies(update_intervals[ret & 0x03]); + + for (i = 0; i < ARRAY_SIZE(data->tcrit1); i++) { + ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT1(i)); + if (ret < 0) + return ret; + data->tcrit1[i] = ret; + } + for (i = 0; i < ARRAY_SIZE(data->tcrit2); i++) { + ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT2(i)); + if (ret < 0) + return ret; + data->tcrit2[i] = ret; + } + for (i = 0; i < ARRAY_SIZE(data->toffset); i++) { + ret = i2c_smbus_read_byte_data(client, LM95234_REG_OFFSET(i)); + if (ret < 0) + return ret; + data->toffset[i] = ret; + } + + ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT_HYST); + if (ret < 0) + return ret; + data->thyst = ret; + + ret = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL); + if (ret < 0) + return ret; + data->sensor_type = ret; + + return 0; +} + +static int lm95234_update_device(struct lm95234_data *data) +{ + struct i2c_client *client = data->client; + int ret; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + data->interval) || + !data->valid) { + int i; + + if (!data->valid) { + ret = lm95234_fill_cache(data, client); + if (ret < 0) + goto abort; + } + + data->valid = false; + for (i = 0; i < ARRAY_SIZE(data->temp); i++) { + ret = lm95234_read_temp(client, i, &data->temp[i]); + if (ret < 0) + goto abort; + } + + ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_FAULT); + if (ret < 0) + goto abort; + data->status = ret; + + ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_TCRIT1); + if (ret < 0) + goto abort; + data->status |= ret << 8; + + ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_TCRIT2); + if (ret < 0) + goto abort; + data->status |= ret << 16; + + data->last_updated = jiffies; + data->valid = true; + } + ret = 0; +abort: + mutex_unlock(&data->update_lock); + + return ret; +} + +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95234_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(data); + + if (ret) + return ret; + + return sprintf(buf, "%d\n", + DIV_ROUND_CLOSEST(data->temp[index] * 125, 32)); +} + +static ssize_t show_alarm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lm95234_data *data = dev_get_drvdata(dev); + u32 mask = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(data); + + if (ret) + return ret; + + return sprintf(buf, "%u", !!(data->status & mask)); +} + +static ssize_t show_type(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95234_data *data = dev_get_drvdata(dev); + u8 mask = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(data); + + if (ret) + return ret; + + return sprintf(buf, data->sensor_type & mask ? "1\n" : "2\n"); +} + +static ssize_t set_type(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lm95234_data *data = dev_get_drvdata(dev); + unsigned long val; + u8 mask = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(data); + + if (ret) + return ret; + + ret = kstrtoul(buf, 10, &val); + if (ret < 0) + return ret; + + if (val != 1 && val != 2) + return -EINVAL; + + mutex_lock(&data->update_lock); + if (val == 1) + data->sensor_type |= mask; + else + data->sensor_type &= ~mask; + data->valid = false; + i2c_smbus_write_byte_data(data->client, LM95234_REG_REM_MODEL, + data->sensor_type); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_tcrit2(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95234_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(data); + + if (ret) + return ret; + + return sprintf(buf, "%u", data->tcrit2[index] * 1000); +} + +static ssize_t set_tcrit2(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lm95234_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(attr)->index; + long val; + int ret = lm95234_update_device(data); + + if (ret) + return ret; + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, index ? 255 : 127); + + mutex_lock(&data->update_lock); + data->tcrit2[index] = val; + i2c_smbus_write_byte_data(data->client, LM95234_REG_TCRIT2(index), val); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_tcrit2_hyst(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lm95234_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(data); + + if (ret) + return ret; + + /* Result can be negative, so be careful with unsigned operands */ + return sprintf(buf, "%d", + ((int)data->tcrit2[index] - (int)data->thyst) * 1000); +} + +static ssize_t show_tcrit1(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95234_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(attr)->index; + + return sprintf(buf, "%u", data->tcrit1[index] * 1000); +} + +static ssize_t set_tcrit1(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lm95234_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(data); + long val; + + if (ret) + return ret; + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 255); + + mutex_lock(&data->update_lock); + data->tcrit1[index] = val; + i2c_smbus_write_byte_data(data->client, LM95234_REG_TCRIT1(index), val); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_tcrit1_hyst(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lm95234_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(data); + + if (ret) + return ret; + + /* Result can be negative, so be careful with unsigned operands */ + return sprintf(buf, "%d", + ((int)data->tcrit1[index] - (int)data->thyst) * 1000); +} + +static ssize_t set_tcrit1_hyst(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lm95234_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(data); + long val; + + if (ret) + return ret; + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + val = DIV_ROUND_CLOSEST(val, 1000); + val = clamp_val((int)data->tcrit1[index] - val, 0, 31); + + mutex_lock(&data->update_lock); + data->thyst = val; + i2c_smbus_write_byte_data(data->client, LM95234_REG_TCRIT_HYST, val); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_offset(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95234_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(data); + + if (ret) + return ret; + + return sprintf(buf, "%d", data->toffset[index] * 500); +} + +static ssize_t set_offset(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lm95234_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(attr)->index; + int ret = lm95234_update_device(data); + long val; + + if (ret) + return ret; + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + /* Accuracy is 1/2 degrees C */ + val = clamp_val(DIV_ROUND_CLOSEST(val, 500), -128, 127); + + mutex_lock(&data->update_lock); + data->toffset[index] = val; + i2c_smbus_write_byte_data(data->client, LM95234_REG_OFFSET(index), val); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_interval(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95234_data *data = dev_get_drvdata(dev); + int ret = lm95234_update_device(data); + + if (ret) + return ret; + + return sprintf(buf, "%lu\n", + DIV_ROUND_CLOSEST(data->interval * 1000, HZ)); +} + +static ssize_t set_interval(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lm95234_data *data = dev_get_drvdata(dev); + int ret = lm95234_update_device(data); + unsigned long val; + u8 regval; + + if (ret) + return ret; + + ret = kstrtoul(buf, 10, &val); + if (ret < 0) + return ret; + + for (regval = 0; regval < 3; regval++) { + if (val <= update_intervals[regval]) + break; + } + + mutex_lock(&data->update_lock); + data->interval = msecs_to_jiffies(update_intervals[regval]); + i2c_smbus_write_byte_data(data->client, LM95234_REG_CONVRATE, regval); + mutex_unlock(&data->update_lock); + + return count; +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4); + +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, + BIT(0) | BIT(1)); +static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, + BIT(2) | BIT(3)); +static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_alarm, NULL, + BIT(4) | BIT(5)); +static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_alarm, NULL, + BIT(6) | BIT(7)); + +static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, set_type, + BIT(1)); +static SENSOR_DEVICE_ATTR(temp3_type, S_IWUSR | S_IRUGO, show_type, set_type, + BIT(2)); +static SENSOR_DEVICE_ATTR(temp4_type, S_IWUSR | S_IRUGO, show_type, set_type, + BIT(3)); +static SENSOR_DEVICE_ATTR(temp5_type, S_IWUSR | S_IRUGO, show_type, set_type, + BIT(4)); + +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_tcrit1, + set_tcrit1, 0); +static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_tcrit2, + set_tcrit2, 0); +static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_tcrit2, + set_tcrit2, 1); +static SENSOR_DEVICE_ATTR(temp4_max, S_IWUSR | S_IRUGO, show_tcrit1, + set_tcrit1, 3); +static SENSOR_DEVICE_ATTR(temp5_max, S_IWUSR | S_IRUGO, show_tcrit1, + set_tcrit1, 4); + +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_tcrit1_hyst, + set_tcrit1_hyst, 0); +static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IRUGO, show_tcrit2_hyst, NULL, 0); +static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IRUGO, show_tcrit2_hyst, NULL, 1); +static SENSOR_DEVICE_ATTR(temp4_max_hyst, S_IRUGO, show_tcrit1_hyst, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_max_hyst, S_IRUGO, show_tcrit1_hyst, NULL, 4); + +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, + BIT(0 + 8)); +static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, + BIT(1 + 16)); +static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, + BIT(2 + 16)); +static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, + BIT(3 + 8)); +static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_alarm, NULL, + BIT(4 + 8)); + +static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_tcrit1, + set_tcrit1, 1); +static SENSOR_DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_tcrit1, + set_tcrit1, 2); + +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_tcrit1_hyst, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_tcrit1_hyst, NULL, 2); + +static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, + BIT(1 + 8)); +static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, + BIT(2 + 8)); + +static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_offset, + set_offset, 0); +static SENSOR_DEVICE_ATTR(temp3_offset, S_IWUSR | S_IRUGO, show_offset, + set_offset, 1); +static SENSOR_DEVICE_ATTR(temp4_offset, S_IWUSR | S_IRUGO, show_offset, + set_offset, 2); +static SENSOR_DEVICE_ATTR(temp5_offset, S_IWUSR | S_IRUGO, show_offset, + set_offset, 3); + +static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval, + set_interval); + +static struct attribute *lm95234_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, + &sensor_dev_attr_temp4_fault.dev_attr.attr, + &sensor_dev_attr_temp5_fault.dev_attr.attr, + &sensor_dev_attr_temp2_type.dev_attr.attr, + &sensor_dev_attr_temp3_type.dev_attr.attr, + &sensor_dev_attr_temp4_type.dev_attr.attr, + &sensor_dev_attr_temp5_type.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp4_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp5_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_offset.dev_attr.attr, + &sensor_dev_attr_temp3_offset.dev_attr.attr, + &sensor_dev_attr_temp4_offset.dev_attr.attr, + &sensor_dev_attr_temp5_offset.dev_attr.attr, + &dev_attr_update_interval.attr, + NULL +}; +ATTRIBUTE_GROUPS(lm95234); + +static int lm95234_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + int mfg_id, chip_id, val; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + mfg_id = i2c_smbus_read_byte_data(client, LM95234_REG_MAN_ID); + if (mfg_id != NATSEMI_MAN_ID) + return -ENODEV; + + chip_id = i2c_smbus_read_byte_data(client, LM95234_REG_CHIP_ID); + if (chip_id != LM95234_CHIP_ID) + return -ENODEV; + + val = i2c_smbus_read_byte_data(client, LM95234_REG_STATUS); + if (val & 0x30) + return -ENODEV; + + val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG); + if (val & 0xbc) + return -ENODEV; + + val = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE); + if (val & 0xfc) + return -ENODEV; + + val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL); + if (val & 0xe1) + return -ENODEV; + + val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS); + if (val & 0xe1) + return -ENODEV; + + strlcpy(info->type, "lm95234", I2C_NAME_SIZE); + return 0; +} + +static int lm95234_init_client(struct i2c_client *client) +{ + int val, model; + + /* start conversion if necessary */ + val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG); + if (val < 0) + return val; + if (val & 0x40) + i2c_smbus_write_byte_data(client, LM95234_REG_CONFIG, + val & ~0x40); + + /* If diode type status reports an error, try to fix it */ + val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS); + if (val < 0) + return val; + model = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL); + if (model < 0) + return model; + if (model & val) { + dev_notice(&client->dev, + "Fixing remote diode type misconfiguration (0x%x)\n", + val); + i2c_smbus_write_byte_data(client, LM95234_REG_REM_MODEL, + model & ~val); + } + return 0; +} + +static int lm95234_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct lm95234_data *data; + struct device *hwmon_dev; + int err; + + data = devm_kzalloc(dev, sizeof(struct lm95234_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + mutex_init(&data->update_lock); + + /* Initialize the LM95234 chip */ + err = lm95234_init_client(client); + if (err < 0) + return err; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + lm95234_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +/* Driver data (common to all clients) */ +static const struct i2c_device_id lm95234_id[] = { + { "lm95234", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm95234_id); + +static struct i2c_driver lm95234_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DRVNAME, + }, + .probe = lm95234_probe, + .id_table = lm95234_id, + .detect = lm95234_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(lm95234_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("LM95234 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index 8fc8eb8cba4..cdf19adaec7 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -1,13 +1,9 @@ /* - * lm95241.c - Part of lm_sensors, Linux kernel modules for hardware - * monitoring - * Copyright (C) 2008 Davide Rizzo <elpa-rizzo@gmail.com> + * Copyright (C) 2008, 2010 Davide Rizzo <elpa.rizzo@gmail.com> * - * Based on the max1619 driver. The LM95241 is a sensor chip made by National - * Semiconductors. - * It reports up to three temperatures (its own plus up to - * two external ones). Complete datasheet can be - * obtained from National's website at: + * The LM95241 is a sensor chip made by National Semiconductors. + * It reports up to three temperatures (its own plus up to two external ones). + * Complete datasheet can be obtained from National's website at: * http://www.national.com/ds.cgi/LM/LM95241.pdf * * This program is free software; you can redistribute it and/or modify @@ -36,8 +32,10 @@ #include <linux/mutex.h> #include <linux/sysfs.h> +#define DEVNAME "lm95241" + static const unsigned short normal_i2c[] = { - 0x19, 0x2a, 0x2b, I2C_CLIENT_END}; + 0x19, 0x2a, 0x2b, I2C_CLIENT_END }; /* LM95241 registers */ #define LM95241_REG_R_MAN_ID 0xFE @@ -46,7 +44,7 @@ static const unsigned short normal_i2c[] = { #define LM95241_REG_RW_CONFIG 0x03 #define LM95241_REG_RW_REM_FILTER 0x06 #define LM95241_REG_RW_TRUTHERM 0x07 -#define LM95241_REG_W_ONE_SHOT 0x0F +#define LM95241_REG_W_ONE_SHOT 0x0F #define LM95241_REG_R_LOCAL_TEMPH 0x10 #define LM95241_REG_R_REMOTE1_TEMPH 0x11 #define LM95241_REG_R_REMOTE2_TEMPH 0x12 @@ -76,255 +74,283 @@ static const unsigned short normal_i2c[] = { #define TT_OFF 0 #define TT_ON 1 #define TT_MASK 7 -#define MANUFACTURER_ID 0x01 -#define DEFAULT_REVISION 0xA4 - -/* Conversions and various macros */ -#define TEMP_FROM_REG(val_h, val_l) (((val_h) & 0x80 ? (val_h) - 0x100 : \ - (val_h)) * 1000 + (val_l) * 1000 / 256) - -/* Functions declaration */ -static void lm95241_init_client(struct i2c_client *client); -static struct lm95241_data *lm95241_update_device(struct device *dev); +#define NATSEMI_MAN_ID 0x01 +#define LM95231_CHIP_ID 0xA1 +#define LM95241_CHIP_ID 0xA4 + +static const u8 lm95241_reg_address[] = { + LM95241_REG_R_LOCAL_TEMPH, + LM95241_REG_R_LOCAL_TEMPL, + LM95241_REG_R_REMOTE1_TEMPH, + LM95241_REG_R_REMOTE1_TEMPL, + LM95241_REG_R_REMOTE2_TEMPH, + LM95241_REG_R_REMOTE2_TEMPL +}; /* Client data (each client gets its own) */ struct lm95241_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; - unsigned long last_updated, rate; /* in jiffies */ - char valid; /* zero until following fields are valid */ + unsigned long last_updated, interval; /* in jiffies */ + char valid; /* zero until following fields are valid */ /* registers values */ - u8 local_h, local_l; /* local */ - u8 remote1_h, remote1_l; /* remote1 */ - u8 remote2_h, remote2_l; /* remote2 */ + u8 temp[ARRAY_SIZE(lm95241_reg_address)]; u8 config, model, trutherm; }; +/* Conversions */ +static int temp_from_reg_signed(u8 val_h, u8 val_l) +{ + s16 val_hl = (val_h << 8) | val_l; + return val_hl * 1000 / 256; +} + +static int temp_from_reg_unsigned(u8 val_h, u8 val_l) +{ + u16 val_hl = (val_h << 8) | val_l; + return val_hl * 1000 / 256; +} + +static struct lm95241_data *lm95241_update_device(struct device *dev) +{ + struct lm95241_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + data->interval) || + !data->valid) { + int i; + + dev_dbg(dev, "Updating lm95241 data.\n"); + for (i = 0; i < ARRAY_SIZE(lm95241_reg_address); i++) + data->temp[i] + = i2c_smbus_read_byte_data(client, + lm95241_reg_address[i]); + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + /* Sysfs stuff */ -#define show_temp(value) \ -static ssize_t show_##value(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct lm95241_data *data = lm95241_update_device(dev); \ - snprintf(buf, PAGE_SIZE - 1, "%d\n", \ - TEMP_FROM_REG(data->value##_h, data->value##_l)); \ - return strlen(buf); \ +static ssize_t show_input(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95241_data *data = lm95241_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + + return snprintf(buf, PAGE_SIZE - 1, "%d\n", + index == 0 || (data->config & (1 << (index / 2))) ? + temp_from_reg_signed(data->temp[index], data->temp[index + 1]) : + temp_from_reg_unsigned(data->temp[index], + data->temp[index + 1])); } -show_temp(local); -show_temp(remote1); -show_temp(remote2); -static ssize_t show_rate(struct device *dev, struct device_attribute *attr, +static ssize_t show_type(struct device *dev, struct device_attribute *attr, char *buf) { - struct lm95241_data *data = lm95241_update_device(dev); + struct lm95241_data *data = dev_get_drvdata(dev); - snprintf(buf, PAGE_SIZE - 1, "%lu\n", 1000 * data->rate / HZ); - return strlen(buf); + return snprintf(buf, PAGE_SIZE - 1, + data->model & to_sensor_dev_attr(attr)->index ? "1\n" : "2\n"); } -static ssize_t set_rate(struct device *dev, struct device_attribute *attr, +static ssize_t set_type(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95241_data *data = i2c_get_clientdata(client); + struct lm95241_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + int shift; + u8 mask = to_sensor_dev_attr(attr)->index; - strict_strtol(buf, 10, &data->rate); - data->rate = data->rate * HZ / 1000; + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + if (val != 1 && val != 2) + return -EINVAL; + + shift = mask == R1MS_MASK ? TT1_SHIFT : TT2_SHIFT; + + mutex_lock(&data->update_lock); + + data->trutherm &= ~(TT_MASK << shift); + if (val == 1) { + data->model |= mask; + data->trutherm |= (TT_ON << shift); + } else { + data->model &= ~mask; + data->trutherm |= (TT_OFF << shift); + } + data->valid = 0; + + i2c_smbus_write_byte_data(client, LM95241_REG_RW_REMOTE_MODEL, + data->model); + i2c_smbus_write_byte_data(client, LM95241_REG_RW_TRUTHERM, + data->trutherm); + + mutex_unlock(&data->update_lock); return count; } -#define show_type(flag) \ -static ssize_t show_type##flag(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct lm95241_data *data = i2c_get_clientdata(client); \ -\ - snprintf(buf, PAGE_SIZE - 1, \ - data->model & R##flag##MS_MASK ? "1\n" : "2\n"); \ - return strlen(buf); \ +static ssize_t show_min(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95241_data *data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE - 1, + data->config & to_sensor_dev_attr(attr)->index ? + "-127000\n" : "0\n"); } -show_type(1); -show_type(2); - -#define show_min(flag) \ -static ssize_t show_min##flag(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct lm95241_data *data = i2c_get_clientdata(client); \ -\ - snprintf(buf, PAGE_SIZE - 1, \ - data->config & R##flag##DF_MASK ? \ - "-127000\n" : "0\n"); \ - return strlen(buf); \ + +static ssize_t set_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lm95241_data *data = dev_get_drvdata(dev); + long val; + + if (kstrtol(buf, 10, &val) < 0) + return -EINVAL; + if (val < -128000) + return -EINVAL; + + mutex_lock(&data->update_lock); + + if (val < 0) + data->config |= to_sensor_dev_attr(attr)->index; + else + data->config &= ~to_sensor_dev_attr(attr)->index; + data->valid = 0; + + i2c_smbus_write_byte_data(data->client, LM95241_REG_RW_CONFIG, + data->config); + + mutex_unlock(&data->update_lock); + + return count; } -show_min(1); -show_min(2); - -#define show_max(flag) \ -static ssize_t show_max##flag(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct lm95241_data *data = i2c_get_clientdata(client); \ -\ - snprintf(buf, PAGE_SIZE - 1, \ - data->config & R##flag##DF_MASK ? \ - "127000\n" : "255000\n"); \ - return strlen(buf); \ + +static ssize_t show_max(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95241_data *data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE - 1, + data->config & to_sensor_dev_attr(attr)->index ? + "127000\n" : "255000\n"); } -show_max(1); -show_max(2); - -#define set_type(flag) \ -static ssize_t set_type##flag(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct lm95241_data *data = i2c_get_clientdata(client); \ -\ - long val; \ - strict_strtol(buf, 10, &val); \ -\ - if ((val == 1) || (val == 2)) { \ -\ - mutex_lock(&data->update_lock); \ -\ - data->trutherm &= ~(TT_MASK << TT##flag##_SHIFT); \ - if (val == 1) { \ - data->model |= R##flag##MS_MASK; \ - data->trutherm |= (TT_ON << TT##flag##_SHIFT); \ - } \ - else { \ - data->model &= ~R##flag##MS_MASK; \ - data->trutherm |= (TT_OFF << TT##flag##_SHIFT); \ - } \ -\ - data->valid = 0; \ -\ - i2c_smbus_write_byte_data(client, LM95241_REG_RW_REMOTE_MODEL, \ - data->model); \ - i2c_smbus_write_byte_data(client, LM95241_REG_RW_TRUTHERM, \ - data->trutherm); \ -\ - mutex_unlock(&data->update_lock); \ -\ - } \ - return count; \ + +static ssize_t set_max(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lm95241_data *data = dev_get_drvdata(dev); + long val; + + if (kstrtol(buf, 10, &val) < 0) + return -EINVAL; + if (val >= 256000) + return -EINVAL; + + mutex_lock(&data->update_lock); + + if (val <= 127000) + data->config |= to_sensor_dev_attr(attr)->index; + else + data->config &= ~to_sensor_dev_attr(attr)->index; + data->valid = 0; + + i2c_smbus_write_byte_data(data->client, LM95241_REG_RW_CONFIG, + data->config); + + mutex_unlock(&data->update_lock); + + return count; } -set_type(1); -set_type(2); - -#define set_min(flag) \ -static ssize_t set_min##flag(struct device *dev, \ - struct device_attribute *devattr, const char *buf, size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct lm95241_data *data = i2c_get_clientdata(client); \ -\ - long val; \ - strict_strtol(buf, 10, &val); \ -\ - mutex_lock(&data->update_lock); \ -\ - if (val < 0) \ - data->config |= R##flag##DF_MASK; \ - else \ - data->config &= ~R##flag##DF_MASK; \ -\ - data->valid = 0; \ -\ - i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, \ - data->config); \ -\ - mutex_unlock(&data->update_lock); \ -\ - return count; \ + +static ssize_t show_interval(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95241_data *data = lm95241_update_device(dev); + + return snprintf(buf, PAGE_SIZE - 1, "%lu\n", 1000 * data->interval + / HZ); } -set_min(1); -set_min(2); - -#define set_max(flag) \ -static ssize_t set_max##flag(struct device *dev, \ - struct device_attribute *devattr, const char *buf, size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct lm95241_data *data = i2c_get_clientdata(client); \ -\ - long val; \ - strict_strtol(buf, 10, &val); \ -\ - mutex_lock(&data->update_lock); \ -\ - if (val <= 127000) \ - data->config |= R##flag##DF_MASK; \ - else \ - data->config &= ~R##flag##DF_MASK; \ -\ - data->valid = 0; \ -\ - i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, \ - data->config); \ -\ - mutex_unlock(&data->update_lock); \ -\ - return count; \ + +static ssize_t set_interval(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lm95241_data *data = dev_get_drvdata(dev); + unsigned long val; + + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + + data->interval = val * HZ / 1000; + + return count; } -set_max(1); -set_max(2); - -static DEVICE_ATTR(temp1_input, S_IRUGO, show_local, NULL); -static DEVICE_ATTR(temp2_input, S_IRUGO, show_remote1, NULL); -static DEVICE_ATTR(temp3_input, S_IRUGO, show_remote2, NULL); -static DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type1, set_type1); -static DEVICE_ATTR(temp3_type, S_IWUSR | S_IRUGO, show_type2, set_type2); -static DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_min1, set_min1); -static DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_min2, set_min2); -static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_max1, set_max1); -static DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_max2, set_max2); -static DEVICE_ATTR(rate, S_IWUSR | S_IRUGO, show_rate, set_rate); - -static struct attribute *lm95241_attributes[] = { - &dev_attr_temp1_input.attr, - &dev_attr_temp2_input.attr, - &dev_attr_temp3_input.attr, - &dev_attr_temp2_type.attr, - &dev_attr_temp3_type.attr, - &dev_attr_temp2_min.attr, - &dev_attr_temp3_min.attr, - &dev_attr_temp2_max.attr, - &dev_attr_temp3_max.attr, - &dev_attr_rate.attr, - NULL -}; -static const struct attribute_group lm95241_group = { - .attrs = lm95241_attributes, +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_input, NULL, 4); +static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, set_type, + R1MS_MASK); +static SENSOR_DEVICE_ATTR(temp3_type, S_IWUSR | S_IRUGO, show_type, set_type, + R2MS_MASK); +static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_min, set_min, + R1DF_MASK); +static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_min, set_min, + R2DF_MASK); +static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_max, set_max, + R1DF_MASK); +static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_max, set_max, + R2DF_MASK); +static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval, + set_interval); + +static struct attribute *lm95241_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp2_type.dev_attr.attr, + &sensor_dev_attr_temp3_type.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &dev_attr_update_interval.attr, + NULL }; +ATTRIBUTE_GROUPS(lm95241); /* Return 0 if detection is successful, -ENODEV otherwise */ static int lm95241_detect(struct i2c_client *new_client, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; - int address = new_client->addr; const char *name; + int mfg_id, chip_id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID) - == MANUFACTURER_ID) - && (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID) - >= DEFAULT_REVISION)) { + mfg_id = i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID); + if (mfg_id != NATSEMI_MAN_ID) + return -ENODEV; + + chip_id = i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID); + switch (chip_id) { + case LM95231_CHIP_ID: + name = "lm95231"; + break; + case LM95241_CHIP_ID: name = "lm95241"; - } else { - dev_dbg(&adapter->dev, "LM95241 detection failed at 0x%02x\n", - address); + break; + default: return -ENODEV; } @@ -333,57 +359,14 @@ static int lm95241_detect(struct i2c_client *new_client, return 0; } -static int lm95241_probe(struct i2c_client *new_client, - const struct i2c_device_id *id) -{ - struct lm95241_data *data; - int err; - - data = kzalloc(sizeof(struct lm95241_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } - - i2c_set_clientdata(new_client, data); - mutex_init(&data->update_lock); - - /* Initialize the LM95241 chip */ - lm95241_init_client(new_client); - - /* Register sysfs hooks */ - err = sysfs_create_group(&new_client->dev.kobj, &lm95241_group); - if (err) - goto exit_free; - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - -exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &lm95241_group); -exit_free: - kfree(data); -exit: - return err; -} - -static void lm95241_init_client(struct i2c_client *client) +static void lm95241_init_client(struct i2c_client *client, + struct lm95241_data *data) { - struct lm95241_data *data = i2c_get_clientdata(client); - - data->rate = HZ; /* 1 sec default */ - data->valid = 0; + data->interval = HZ; /* 1 sec default */ data->config = CFG_CR0076; - data->model = 0; data->trutherm = (TT_OFF << TT1_SHIFT) | (TT_OFF << TT2_SHIFT); - i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, - data->config); + i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, data->config); i2c_smbus_write_byte_data(client, LM95241_REG_RW_REM_FILTER, R1FE_MASK | R2FE_MASK); i2c_smbus_write_byte_data(client, LM95241_REG_RW_TRUTHERM, @@ -392,57 +375,32 @@ static void lm95241_init_client(struct i2c_client *client) data->model); } -static int lm95241_remove(struct i2c_client *client) -{ - struct lm95241_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &lm95241_group); - - i2c_set_clientdata(client, NULL); - kfree(data); - return 0; -} - -static struct lm95241_data *lm95241_update_device(struct device *dev) +static int lm95241_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95241_data *data = i2c_get_clientdata(client); + struct device *dev = &client->dev; + struct lm95241_data *data; + struct device *hwmon_dev; - mutex_lock(&data->update_lock); + data = devm_kzalloc(dev, sizeof(struct lm95241_data), GFP_KERNEL); + if (!data) + return -ENOMEM; - if (time_after(jiffies, data->last_updated + data->rate) || - !data->valid) { - dev_dbg(&client->dev, "Updating lm95241 data.\n"); - data->local_h = - i2c_smbus_read_byte_data(client, - LM95241_REG_R_LOCAL_TEMPH); - data->local_l = - i2c_smbus_read_byte_data(client, - LM95241_REG_R_LOCAL_TEMPL); - data->remote1_h = - i2c_smbus_read_byte_data(client, - LM95241_REG_R_REMOTE1_TEMPH); - data->remote1_l = - i2c_smbus_read_byte_data(client, - LM95241_REG_R_REMOTE1_TEMPL); - data->remote2_h = - i2c_smbus_read_byte_data(client, - LM95241_REG_R_REMOTE2_TEMPH); - data->remote2_l = - i2c_smbus_read_byte_data(client, - LM95241_REG_R_REMOTE2_TEMPL); - data->last_updated = jiffies; - data->valid = 1; - } + data->client = client; + mutex_init(&data->update_lock); - mutex_unlock(&data->update_lock); + /* Initialize the LM95241 chip */ + lm95241_init_client(client, data); - return data; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + lm95241_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } /* Driver data (common to all clients) */ static const struct i2c_device_id lm95241_id[] = { + { "lm95231", 0 }, { "lm95241", 0 }, { } }; @@ -451,28 +409,16 @@ MODULE_DEVICE_TABLE(i2c, lm95241_id); static struct i2c_driver lm95241_driver = { .class = I2C_CLASS_HWMON, .driver = { - .name = "lm95241", + .name = DEVNAME, }, .probe = lm95241_probe, - .remove = lm95241_remove, .id_table = lm95241_id, .detect = lm95241_detect, .address_list = normal_i2c, }; -static int __init sensors_lm95241_init(void) -{ - return i2c_add_driver(&lm95241_driver); -} - -static void __exit sensors_lm95241_exit(void) -{ - i2c_del_driver(&lm95241_driver); -} +module_i2c_driver(lm95241_driver); -MODULE_AUTHOR("Davide Rizzo <elpa-rizzo@gmail.com>"); +MODULE_AUTHOR("Davide Rizzo <elpa.rizzo@gmail.com>"); MODULE_DESCRIPTION("LM95241 sensor driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_lm95241_init); -module_exit(sensors_lm95241_exit); diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c new file mode 100644 index 00000000000..0ae0dfdafdf --- /dev/null +++ b/drivers/hwmon/lm95245.c @@ -0,0 +1,507 @@ +/* + * Copyright (C) 2011 Alexander Stein <alexander.stein@systec-electronic.com> + * + * The LM95245 is a sensor chip made by National Semiconductors. + * It reports up to two temperatures (its own plus an external one). + * Complete datasheet can be obtained from National's website at: + * http://www.national.com/ds.cgi/LM/LM95245.pdf + * + * This driver is based on lm95241.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/sysfs.h> + +#define DEVNAME "lm95245" + +static const unsigned short normal_i2c[] = { + 0x18, 0x19, 0x29, 0x4c, 0x4d, I2C_CLIENT_END }; + +/* LM95245 registers */ +/* general registers */ +#define LM95245_REG_RW_CONFIG1 0x03 +#define LM95245_REG_RW_CONVERS_RATE 0x04 +#define LM95245_REG_W_ONE_SHOT 0x0F + +/* diode configuration */ +#define LM95245_REG_RW_CONFIG2 0xBF +#define LM95245_REG_RW_REMOTE_OFFH 0x11 +#define LM95245_REG_RW_REMOTE_OFFL 0x12 + +/* status registers */ +#define LM95245_REG_R_STATUS1 0x02 +#define LM95245_REG_R_STATUS2 0x33 + +/* limit registers */ +#define LM95245_REG_RW_REMOTE_OS_LIMIT 0x07 +#define LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT 0x20 +#define LM95245_REG_RW_REMOTE_TCRIT_LIMIT 0x19 +#define LM95245_REG_RW_COMMON_HYSTERESIS 0x21 + +/* temperature signed */ +#define LM95245_REG_R_LOCAL_TEMPH_S 0x00 +#define LM95245_REG_R_LOCAL_TEMPL_S 0x30 +#define LM95245_REG_R_REMOTE_TEMPH_S 0x01 +#define LM95245_REG_R_REMOTE_TEMPL_S 0x10 +/* temperature unsigned */ +#define LM95245_REG_R_REMOTE_TEMPH_U 0x31 +#define LM95245_REG_R_REMOTE_TEMPL_U 0x32 + +/* id registers */ +#define LM95245_REG_R_MAN_ID 0xFE +#define LM95245_REG_R_CHIP_ID 0xFF + +/* LM95245 specific bitfields */ +#define CFG_STOP 0x40 +#define CFG_REMOTE_TCRIT_MASK 0x10 +#define CFG_REMOTE_OS_MASK 0x08 +#define CFG_LOCAL_TCRIT_MASK 0x04 +#define CFG_LOCAL_OS_MASK 0x02 + +#define CFG2_OS_A0 0x40 +#define CFG2_DIODE_FAULT_OS 0x20 +#define CFG2_DIODE_FAULT_TCRIT 0x10 +#define CFG2_REMOTE_TT 0x08 +#define CFG2_REMOTE_FILTER_DIS 0x00 +#define CFG2_REMOTE_FILTER_EN 0x06 + +/* conversation rate in ms */ +#define RATE_CR0063 0x00 +#define RATE_CR0364 0x01 +#define RATE_CR1000 0x02 +#define RATE_CR2500 0x03 + +#define STATUS1_DIODE_FAULT 0x04 +#define STATUS1_RTCRIT 0x02 +#define STATUS1_LOC 0x01 + +#define MANUFACTURER_ID 0x01 +#define DEFAULT_REVISION 0xB3 + +static const u8 lm95245_reg_address[] = { + LM95245_REG_R_LOCAL_TEMPH_S, + LM95245_REG_R_LOCAL_TEMPL_S, + LM95245_REG_R_REMOTE_TEMPH_S, + LM95245_REG_R_REMOTE_TEMPL_S, + LM95245_REG_R_REMOTE_TEMPH_U, + LM95245_REG_R_REMOTE_TEMPL_U, + LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT, + LM95245_REG_RW_REMOTE_TCRIT_LIMIT, + LM95245_REG_RW_COMMON_HYSTERESIS, + LM95245_REG_R_STATUS1, +}; + +/* Client data (each client gets its own) */ +struct lm95245_data { + struct i2c_client *client; + struct mutex update_lock; + unsigned long last_updated; /* in jiffies */ + unsigned long interval; /* in msecs */ + bool valid; /* zero until following fields are valid */ + /* registers values */ + u8 regs[ARRAY_SIZE(lm95245_reg_address)]; + u8 config1, config2; +}; + +/* Conversions */ +static int temp_from_reg_unsigned(u8 val_h, u8 val_l) +{ + return val_h * 1000 + val_l * 1000 / 256; +} + +static int temp_from_reg_signed(u8 val_h, u8 val_l) +{ + if (val_h & 0x80) + return (val_h - 0x100) * 1000; + return temp_from_reg_unsigned(val_h, val_l); +} + +static struct lm95245_data *lm95245_update_device(struct device *dev) +{ + struct lm95245_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + + msecs_to_jiffies(data->interval)) || !data->valid) { + int i; + + for (i = 0; i < ARRAY_SIZE(lm95245_reg_address); i++) + data->regs[i] + = i2c_smbus_read_byte_data(client, + lm95245_reg_address[i]); + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static unsigned long lm95245_read_conversion_rate(struct i2c_client *client) +{ + int rate; + unsigned long interval; + + rate = i2c_smbus_read_byte_data(client, LM95245_REG_RW_CONVERS_RATE); + + switch (rate) { + case RATE_CR0063: + interval = 63; + break; + case RATE_CR0364: + interval = 364; + break; + case RATE_CR1000: + interval = 1000; + break; + case RATE_CR2500: + default: + interval = 2500; + break; + } + + return interval; +} + +static unsigned long lm95245_set_conversion_rate(struct i2c_client *client, + unsigned long interval) +{ + int rate; + + if (interval <= 63) { + interval = 63; + rate = RATE_CR0063; + } else if (interval <= 364) { + interval = 364; + rate = RATE_CR0364; + } else if (interval <= 1000) { + interval = 1000; + rate = RATE_CR1000; + } else { + interval = 2500; + rate = RATE_CR2500; + } + + i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONVERS_RATE, rate); + + return interval; +} + +/* Sysfs stuff */ +static ssize_t show_input(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95245_data *data = lm95245_update_device(dev); + int temp; + int index = to_sensor_dev_attr(attr)->index; + + /* + * Index 0 (Local temp) is always signed + * Index 2 (Remote temp) has both signed and unsigned data + * use signed calculation for remote if signed bit is set + */ + if (index == 0 || data->regs[index] & 0x80) + temp = temp_from_reg_signed(data->regs[index], + data->regs[index + 1]); + else + temp = temp_from_reg_unsigned(data->regs[index + 2], + data->regs[index + 3]); + + return snprintf(buf, PAGE_SIZE - 1, "%d\n", temp); +} + +static ssize_t show_limit(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95245_data *data = lm95245_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + + return snprintf(buf, PAGE_SIZE - 1, "%d\n", + data->regs[index] * 1000); +} + +static ssize_t set_limit(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lm95245_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(attr)->index; + struct i2c_client *client = data->client; + unsigned long val; + + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + + val /= 1000; + + val = clamp_val(val, 0, (index == 6 ? 127 : 255)); + + mutex_lock(&data->update_lock); + + data->valid = 0; + + i2c_smbus_write_byte_data(client, lm95245_reg_address[index], val); + + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_crit_hyst(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95245_data *data = lm95245_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + int hyst = data->regs[index] - data->regs[8]; + + return snprintf(buf, PAGE_SIZE - 1, "%d\n", hyst * 1000); +} + +static ssize_t set_crit_hyst(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lm95245_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(attr)->index; + struct i2c_client *client = data->client; + unsigned long val; + int hyst, limit; + + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + + limit = i2c_smbus_read_byte_data(client, lm95245_reg_address[index]); + hyst = limit - val / 1000; + hyst = clamp_val(hyst, 0, 31); + data->regs[8] = hyst; + + /* shared crit hysteresis */ + i2c_smbus_write_byte_data(client, LM95245_REG_RW_COMMON_HYSTERESIS, + hyst); + + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_type(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95245_data *data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE - 1, + data->config2 & CFG2_REMOTE_TT ? "1\n" : "2\n"); +} + +static ssize_t set_type(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lm95245_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + if (val != 1 && val != 2) + return -EINVAL; + + mutex_lock(&data->update_lock); + + if (val == 1) + data->config2 |= CFG2_REMOTE_TT; + else + data->config2 &= ~CFG2_REMOTE_TT; + + data->valid = 0; + + i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG2, + data->config2); + + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95245_data *data = lm95245_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + + return snprintf(buf, PAGE_SIZE - 1, "%d\n", + !!(data->regs[9] & index)); +} + +static ssize_t show_interval(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95245_data *data = lm95245_update_device(dev); + + return snprintf(buf, PAGE_SIZE - 1, "%lu\n", data->interval); +} + +static ssize_t set_interval(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lm95245_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + + data->interval = lm95245_set_conversion_rate(client, val); + + mutex_unlock(&data->update_lock); + + return count; +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_limit, + set_limit, 6); +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_crit_hyst, + set_crit_hyst, 6); +static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, + STATUS1_LOC); + +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2); +static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_limit, + set_limit, 7); +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_crit_hyst, NULL, 7); +static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, + STATUS1_RTCRIT); +static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, + set_type, 0); +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, + STATUS1_DIODE_FAULT); + +static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval, + set_interval); + +static struct attribute *lm95245_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_type.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &dev_attr_update_interval.attr, + NULL +}; +ATTRIBUTE_GROUPS(lm95245); + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int lm95245_detect(struct i2c_client *new_client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = new_client->adapter; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + if (i2c_smbus_read_byte_data(new_client, LM95245_REG_R_MAN_ID) + != MANUFACTURER_ID + || i2c_smbus_read_byte_data(new_client, LM95245_REG_R_CHIP_ID) + != DEFAULT_REVISION) + return -ENODEV; + + strlcpy(info->type, DEVNAME, I2C_NAME_SIZE); + return 0; +} + +static void lm95245_init_client(struct i2c_client *client, + struct lm95245_data *data) +{ + data->interval = lm95245_read_conversion_rate(client); + + data->config1 = i2c_smbus_read_byte_data(client, + LM95245_REG_RW_CONFIG1); + data->config2 = i2c_smbus_read_byte_data(client, + LM95245_REG_RW_CONFIG2); + + if (data->config1 & CFG_STOP) { + /* Clear the standby bit */ + data->config1 &= ~CFG_STOP; + i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG1, + data->config1); + } +} + +static int lm95245_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct lm95245_data *data; + struct device *hwmon_dev; + + data = devm_kzalloc(dev, sizeof(struct lm95245_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + mutex_init(&data->update_lock); + + /* Initialize the LM95245 chip */ + lm95245_init_client(client, data); + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + lm95245_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +/* Driver data (common to all clients) */ +static const struct i2c_device_id lm95245_id[] = { + { DEVNAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm95245_id); + +static struct i2c_driver lm95245_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DEVNAME, + }, + .probe = lm95245_probe, + .id_table = lm95245_id, + .detect = lm95245_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(lm95245_driver); + +MODULE_AUTHOR("Alexander Stein <alexander.stein@systec-electronic.com>"); +MODULE_DESCRIPTION("LM95245 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ltc2945.c b/drivers/hwmon/ltc2945.c new file mode 100644 index 00000000000..3701b329b6a --- /dev/null +++ b/drivers/hwmon/ltc2945.c @@ -0,0 +1,519 @@ +/* + * Driver for Linear Technology LTC2945 I2C Power Monitor + * + * Copyright (c) 2014 Guenter Roeck + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/jiffies.h> +#include <linux/regmap.h> + +/* chip registers */ +#define LTC2945_CONTROL 0x00 +#define LTC2945_ALERT 0x01 +#define LTC2945_STATUS 0x02 +#define LTC2945_FAULT 0x03 +#define LTC2945_POWER_H 0x05 +#define LTC2945_MAX_POWER_H 0x08 +#define LTC2945_MIN_POWER_H 0x0b +#define LTC2945_MAX_POWER_THRES_H 0x0e +#define LTC2945_MIN_POWER_THRES_H 0x11 +#define LTC2945_SENSE_H 0x14 +#define LTC2945_MAX_SENSE_H 0x16 +#define LTC2945_MIN_SENSE_H 0x18 +#define LTC2945_MAX_SENSE_THRES_H 0x1a +#define LTC2945_MIN_SENSE_THRES_H 0x1c +#define LTC2945_VIN_H 0x1e +#define LTC2945_MAX_VIN_H 0x20 +#define LTC2945_MIN_VIN_H 0x22 +#define LTC2945_MAX_VIN_THRES_H 0x24 +#define LTC2945_MIN_VIN_THRES_H 0x26 +#define LTC2945_ADIN_H 0x28 +#define LTC2945_MAX_ADIN_H 0x2a +#define LTC2945_MIN_ADIN_H 0x2c +#define LTC2945_MAX_ADIN_THRES_H 0x2e +#define LTC2945_MIN_ADIN_THRES_H 0x30 +#define LTC2945_MIN_ADIN_THRES_L 0x31 + +/* Fault register bits */ + +#define FAULT_ADIN_UV (1 << 0) +#define FAULT_ADIN_OV (1 << 1) +#define FAULT_VIN_UV (1 << 2) +#define FAULT_VIN_OV (1 << 3) +#define FAULT_SENSE_UV (1 << 4) +#define FAULT_SENSE_OV (1 << 5) +#define FAULT_POWER_UV (1 << 6) +#define FAULT_POWER_OV (1 << 7) + +/* Control register bits */ + +#define CONTROL_MULT_SELECT (1 << 0) +#define CONTROL_TEST_MODE (1 << 4) + +static inline bool is_power_reg(u8 reg) +{ + return reg < LTC2945_SENSE_H; +} + +/* Return the value from the given register in uW, mV, or mA */ +static long long ltc2945_reg_to_val(struct device *dev, u8 reg) +{ + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int control; + u8 buf[3]; + long long val; + int ret; + + ret = regmap_bulk_read(regmap, reg, buf, + is_power_reg(reg) ? 3 : 2); + if (ret < 0) + return ret; + + if (is_power_reg(reg)) { + /* power */ + val = (buf[0] << 16) + (buf[1] << 8) + buf[2]; + } else { + /* current, voltage */ + val = (buf[0] << 4) + (buf[1] >> 4); + } + + switch (reg) { + case LTC2945_POWER_H: + case LTC2945_MAX_POWER_H: + case LTC2945_MIN_POWER_H: + case LTC2945_MAX_POWER_THRES_H: + case LTC2945_MIN_POWER_THRES_H: + /* + * Convert to uW by assuming current is measured with + * an 1mOhm sense resistor, similar to current + * measurements. + * Control register bit 0 selects if voltage at SENSE+/VDD + * or voltage at ADIN is used to measure power. + */ + ret = regmap_read(regmap, LTC2945_CONTROL, &control); + if (ret < 0) + return ret; + if (control & CONTROL_MULT_SELECT) { + /* 25 mV * 25 uV = 0.625 uV resolution. */ + val *= 625LL; + } else { + /* 0.5 mV * 25 uV = 0.0125 uV resolution. */ + val = (val * 25LL) >> 1; + } + break; + case LTC2945_VIN_H: + case LTC2945_MAX_VIN_H: + case LTC2945_MIN_VIN_H: + case LTC2945_MAX_VIN_THRES_H: + case LTC2945_MIN_VIN_THRES_H: + /* 25 mV resolution. Convert to mV. */ + val *= 25; + break; + case LTC2945_ADIN_H: + case LTC2945_MAX_ADIN_H: + case LTC2945_MIN_ADIN_THRES_H: + case LTC2945_MAX_ADIN_THRES_H: + case LTC2945_MIN_ADIN_H: + /* 0.5mV resolution. Convert to mV. */ + val = val >> 1; + break; + case LTC2945_SENSE_H: + case LTC2945_MAX_SENSE_H: + case LTC2945_MIN_SENSE_H: + case LTC2945_MAX_SENSE_THRES_H: + case LTC2945_MIN_SENSE_THRES_H: + /* + * 25 uV 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 *= 25; + break; + default: + return -EINVAL; + } + return val; +} + +static int ltc2945_val_to_reg(struct device *dev, u8 reg, + unsigned long val) +{ + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int control; + int ret; + + switch (reg) { + case LTC2945_POWER_H: + case LTC2945_MAX_POWER_H: + case LTC2945_MIN_POWER_H: + case LTC2945_MAX_POWER_THRES_H: + case LTC2945_MIN_POWER_THRES_H: + /* + * Convert to register value by assuming current is measured + * with an 1mOhm sense resistor, similar to current + * measurements. + * Control register bit 0 selects if voltage at SENSE+/VDD + * or voltage at ADIN is used to measure power, which in turn + * determines register calculations. + */ + ret = regmap_read(regmap, LTC2945_CONTROL, &control); + if (ret < 0) + return ret; + if (control & CONTROL_MULT_SELECT) { + /* 25 mV * 25 uV = 0.625 uV resolution. */ + val = DIV_ROUND_CLOSEST(val, 625); + } else { + /* + * 0.5 mV * 25 uV = 0.0125 uV resolution. + * Divide first to avoid overflow; + * accept loss of accuracy. + */ + val = DIV_ROUND_CLOSEST(val, 25) * 2; + } + break; + case LTC2945_VIN_H: + case LTC2945_MAX_VIN_H: + case LTC2945_MIN_VIN_H: + case LTC2945_MAX_VIN_THRES_H: + case LTC2945_MIN_VIN_THRES_H: + /* 25 mV resolution. */ + val /= 25; + break; + case LTC2945_ADIN_H: + case LTC2945_MAX_ADIN_H: + case LTC2945_MIN_ADIN_THRES_H: + case LTC2945_MAX_ADIN_THRES_H: + case LTC2945_MIN_ADIN_H: + /* 0.5mV resolution. */ + val *= 2; + break; + case LTC2945_SENSE_H: + case LTC2945_MAX_SENSE_H: + case LTC2945_MIN_SENSE_H: + case LTC2945_MAX_SENSE_THRES_H: + case LTC2945_MIN_SENSE_THRES_H: + /* + * 25 uV 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 = DIV_ROUND_CLOSEST(val, 25); + break; + default: + return -EINVAL; + } + return val; +} + +static ssize_t ltc2945_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + long long value; + + value = ltc2945_reg_to_val(dev, attr->index); + if (value < 0) + return value; + return snprintf(buf, PAGE_SIZE, "%lld\n", value); +} + +static ssize_t ltc2945_set_value(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct regmap *regmap = dev_get_drvdata(dev); + u8 reg = attr->index; + unsigned long val; + u8 regbuf[3]; + int num_regs; + int regval; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + /* convert to register value, then clamp and write result */ + regval = ltc2945_val_to_reg(dev, reg, val); + if (is_power_reg(reg)) { + regval = clamp_val(regval, 0, 0xffffff); + regbuf[0] = regval >> 16; + regbuf[1] = (regval >> 8) & 0xff; + regbuf[2] = regval; + num_regs = 3; + } else { + regval = clamp_val(regval, 0, 0xfff) << 4; + regbuf[0] = regval >> 8; + regbuf[1] = regval & 0xff; + num_regs = 2; + } + ret = regmap_bulk_write(regmap, reg, regbuf, num_regs); + return ret < 0 ? ret : count; +} + +static ssize_t ltc2945_reset_history(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct regmap *regmap = dev_get_drvdata(dev); + u8 reg = attr->index; + int num_regs = is_power_reg(reg) ? 3 : 2; + u8 buf_min[3] = { 0xff, 0xff, 0xff }; + u8 buf_max[3] = { 0, 0, 0 }; + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + if (val != 1) + return -EINVAL; + + ret = regmap_update_bits(regmap, LTC2945_CONTROL, CONTROL_TEST_MODE, + CONTROL_TEST_MODE); + + /* Reset minimum */ + ret = regmap_bulk_write(regmap, reg, buf_min, num_regs); + if (ret) + return ret; + + switch (reg) { + case LTC2945_MIN_POWER_H: + reg = LTC2945_MAX_POWER_H; + break; + case LTC2945_MIN_SENSE_H: + reg = LTC2945_MAX_SENSE_H; + break; + case LTC2945_MIN_VIN_H: + reg = LTC2945_MAX_VIN_H; + break; + case LTC2945_MIN_ADIN_H: + reg = LTC2945_MAX_ADIN_H; + break; + default: + WARN_ONCE(1, "Bad register: 0x%x\n", reg); + return -EINVAL; + } + /* Reset maximum */ + ret = regmap_bulk_write(regmap, reg, buf_max, num_regs); + + /* Try resetting test mode even if there was an error */ + regmap_update_bits(regmap, LTC2945_CONTROL, CONTROL_TEST_MODE, 0); + + return ret ? : count; +} + +static ssize_t ltc2945_show_bool(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int fault; + int ret; + + ret = regmap_read(regmap, LTC2945_FAULT, &fault); + if (ret < 0) + return ret; + + fault &= attr->index; + if (fault) /* Clear reported faults in chip register */ + regmap_update_bits(regmap, LTC2945_FAULT, attr->index, 0); + + return snprintf(buf, PAGE_SIZE, "%d\n", !!fault); +} + +/* Input voltages */ + +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_VIN_H); +static SENSOR_DEVICE_ATTR(in1_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_VIN_THRES_H); +static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_VIN_THRES_H); +static SENSOR_DEVICE_ATTR(in1_lowest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MIN_VIN_H); +static SENSOR_DEVICE_ATTR(in1_highest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MAX_VIN_H); +static SENSOR_DEVICE_ATTR(in1_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_VIN_H); + +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_ADIN_H); +static SENSOR_DEVICE_ATTR(in2_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_ADIN_THRES_H); +static SENSOR_DEVICE_ATTR(in2_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_ADIN_THRES_H); +static SENSOR_DEVICE_ATTR(in2_lowest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MIN_ADIN_H); +static SENSOR_DEVICE_ATTR(in2_highest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MAX_ADIN_H); +static SENSOR_DEVICE_ATTR(in2_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_ADIN_H); + +/* Voltage alarms */ + +static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_VIN_UV); +static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_VIN_OV); +static SENSOR_DEVICE_ATTR(in2_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_ADIN_UV); +static SENSOR_DEVICE_ATTR(in2_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_ADIN_OV); + +/* Currents (via sense resistor) */ + +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_SENSE_H); +static SENSOR_DEVICE_ATTR(curr1_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_SENSE_THRES_H); +static SENSOR_DEVICE_ATTR(curr1_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_SENSE_THRES_H); +static SENSOR_DEVICE_ATTR(curr1_lowest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MIN_SENSE_H); +static SENSOR_DEVICE_ATTR(curr1_highest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MAX_SENSE_H); +static SENSOR_DEVICE_ATTR(curr1_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_SENSE_H); + +/* Current alarms */ + +static SENSOR_DEVICE_ATTR(curr1_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_SENSE_UV); +static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_SENSE_OV); + +/* Power */ + +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_POWER_H); +static SENSOR_DEVICE_ATTR(power1_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_POWER_THRES_H); +static SENSOR_DEVICE_ATTR(power1_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_POWER_THRES_H); +static SENSOR_DEVICE_ATTR(power1_input_lowest, S_IRUGO, ltc2945_show_value, + NULL, LTC2945_MIN_POWER_H); +static SENSOR_DEVICE_ATTR(power1_input_highest, S_IRUGO, ltc2945_show_value, + NULL, LTC2945_MAX_POWER_H); +static SENSOR_DEVICE_ATTR(power1_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_POWER_H); + +/* Power alarms */ + +static SENSOR_DEVICE_ATTR(power1_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_POWER_UV); +static SENSOR_DEVICE_ATTR(power1_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_POWER_OV); + +static struct attribute *ltc2945_attrs[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in1_lowest.dev_attr.attr, + &sensor_dev_attr_in1_highest.dev_attr.attr, + &sensor_dev_attr_in1_reset_history.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.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in2_lowest.dev_attr.attr, + &sensor_dev_attr_in2_highest.dev_attr.attr, + &sensor_dev_attr_in2_reset_history.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_min.dev_attr.attr, + &sensor_dev_attr_curr1_max.dev_attr.attr, + &sensor_dev_attr_curr1_lowest.dev_attr.attr, + &sensor_dev_attr_curr1_highest.dev_attr.attr, + &sensor_dev_attr_curr1_reset_history.dev_attr.attr, + &sensor_dev_attr_curr1_min_alarm.dev_attr.attr, + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + + &sensor_dev_attr_power1_input.dev_attr.attr, + &sensor_dev_attr_power1_min.dev_attr.attr, + &sensor_dev_attr_power1_max.dev_attr.attr, + &sensor_dev_attr_power1_input_lowest.dev_attr.attr, + &sensor_dev_attr_power1_input_highest.dev_attr.attr, + &sensor_dev_attr_power1_reset_history.dev_attr.attr, + &sensor_dev_attr_power1_min_alarm.dev_attr.attr, + &sensor_dev_attr_power1_max_alarm.dev_attr.attr, + + NULL, +}; +ATTRIBUTE_GROUPS(ltc2945); + +static struct regmap_config ltc2945_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = LTC2945_MIN_ADIN_THRES_L, +}; + +static int ltc2945_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, <c2945_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(regmap); + } + + /* Clear faults */ + regmap_write(regmap, LTC2945_FAULT, 0x00); + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + regmap, + ltc2945_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id ltc2945_id[] = { + {"ltc2945", 0}, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ltc2945_id); + +static struct i2c_driver ltc2945_driver = { + .driver = { + .name = "ltc2945", + }, + .probe = ltc2945_probe, + .id_table = ltc2945_id, +}; + +module_i2c_driver(ltc2945_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("LTC2945 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ltc4151.c b/drivers/hwmon/ltc4151.c new file mode 100644 index 00000000000..c86a1840249 --- /dev/null +++ b/drivers/hwmon/ltc4151.c @@ -0,0 +1,215 @@ +/* + * Driver for Linear Technology LTC4151 High Voltage I2C Current + * and Voltage Monitor + * + * Copyright (C) 2011 AppearTV AS + * + * Derived from: + * + * Driver for Linear Technology LTC4261 I2C Negative Voltage Hot + * Swap Controller + * Copyright (C) 2010 Ericsson AB. + * + * Datasheet: http://www.linear.com/docs/Datasheet/4151fc.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> +#include <linux/jiffies.h> + +/* chip registers */ +#define LTC4151_SENSE_H 0x00 +#define LTC4151_SENSE_L 0x01 +#define LTC4151_VIN_H 0x02 +#define LTC4151_VIN_L 0x03 +#define LTC4151_ADIN_H 0x04 +#define LTC4151_ADIN_L 0x05 + +struct ltc4151_data { + struct i2c_client *client; + + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + + /* Registers */ + u8 regs[6]; +}; + +static struct ltc4151_data *ltc4151_update_device(struct device *dev) +{ + struct ltc4151_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + struct ltc4151_data *ret = data; + + mutex_lock(&data->update_lock); + + /* + * The chip's A/D updates 6 times per second + * (Conversion Rate 6 - 9 Hz) + */ + if (time_after(jiffies, data->last_updated + HZ / 6) || !data->valid) { + int i; + + dev_dbg(&client->dev, "Starting ltc4151 update\n"); + + /* Read all registers */ + 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\n", + 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 */ +static int ltc4151_get_value(struct ltc4151_data *data, u8 reg) +{ + u32 val; + + val = (data->regs[reg] << 4) + (data->regs[reg + 1] >> 4); + + switch (reg) { + case LTC4151_ADIN_H: + /* 500uV resolution. Convert to mV. */ + val = val * 500 / 1000; + break; + case LTC4151_SENSE_H: + /* + * 20uV resolution. Convert to current as measured with + * an 1 mOhm sense resistor, in mA. + */ + val = val * 20; + break; + case LTC4151_VIN_H: + /* 25 mV per increment */ + val = val * 25; + break; + default: + /* If we get here, the developer messed up */ + WARN_ON_ONCE(1); + val = 0; + break; + } + + return val; +} + +static ssize_t ltc4151_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ltc4151_data *data = ltc4151_update_device(dev); + int value; + + if (IS_ERR(data)) + return PTR_ERR(data); + + value = ltc4151_get_value(data, attr->index); + return snprintf(buf, PAGE_SIZE, "%d\n", value); +} + +/* + * Input voltages. + */ +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4151_show_value, NULL, + LTC4151_VIN_H); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4151_show_value, NULL, + LTC4151_ADIN_H); + +/* Currents (via sense resistor) */ +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4151_show_value, NULL, + LTC4151_SENSE_H); + +/* + * Finally, construct an array of pointers to members of the above objects, + * as required for sysfs_create_group() + */ +static struct attribute *ltc4151_attrs[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + + &sensor_dev_attr_curr1_input.dev_attr.attr, + + NULL, +}; +ATTRIBUTE_GROUPS(ltc4151); + +static int ltc4151_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; + struct ltc4151_data *data; + struct device *hwmon_dev; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + mutex_init(&data->update_lock); + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + ltc4151_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id ltc4151_id[] = { + { "ltc4151", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc4151_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver ltc4151_driver = { + .driver = { + .name = "ltc4151", + }, + .probe = ltc4151_probe, + .id_table = ltc4151_id, +}; + +module_i2c_driver(ltc4151_driver); + +MODULE_AUTHOR("Per Dalen <per.dalen@appeartv.com>"); +MODULE_DESCRIPTION("LTC4151 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ltc4215.c b/drivers/hwmon/ltc4215.c index 00d975eb5b8..c8a9bd9b050 100644 --- a/drivers/hwmon/ltc4215.c +++ b/drivers/hwmon/ltc4215.c @@ -19,6 +19,7 @@ #include <linux/i2c.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> +#include <linux/jiffies.h> /* Here are names of the chip's registers (a.k.a. commands) */ enum ltc4215_cmd { @@ -32,7 +33,7 @@ enum ltc4215_cmd { }; struct ltc4215_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; bool valid; @@ -44,8 +45,8 @@ struct ltc4215_data { static struct ltc4215_data *ltc4215_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct ltc4215_data *data = i2c_get_clientdata(client); + struct ltc4215_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; s32 val; int i; @@ -91,8 +92,10 @@ static int ltc4215_get_voltage(struct device *dev, u8 reg) voltage = regval * 605 / 10; break; case LTC4215_ADIN: - /* The ADIN input is divided by 12.5, and has 4.82 mV - * per increment, so we have the additional multiply */ + /* + * The ADIN input is divided by 12.5, and has 4.82 mV + * per increment, so we have the additional multiply + */ voltage = regval * 482 * 125 / 1000; break; default: @@ -109,7 +112,8 @@ static unsigned int ltc4215_get_current(struct device *dev) { struct ltc4215_data *data = ltc4215_update_device(dev); - /* The strange looking conversions that follow are fixed-point + /* + * The strange looking conversions that follow are fixed-point * math, since we cannot do floating point in the kernel. * * Step 1: convert sense register to microVolts @@ -168,129 +172,90 @@ static ssize_t ltc4215_show_alarm(struct device *dev, struct device_attribute *da, char *buf) { - struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da); + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct ltc4215_data *data = ltc4215_update_device(dev); - const u8 reg = data->regs[attr->index]; - const u32 mask = attr->nr; + const u8 reg = data->regs[LTC4215_STATUS]; + const u32 mask = attr->index; - return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0); + return snprintf(buf, PAGE_SIZE, "%u\n", !!(reg & mask)); } -/* These macros are used below in constructing device attribute objects +/* + * 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 LTC4215_VOLTAGE(name, ltc4215_cmd_idx) \ - static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ - ltc4215_show_voltage, NULL, ltc4215_cmd_idx) - -#define LTC4215_CURRENT(name) \ - static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ - ltc4215_show_current, NULL, 0); - -#define LTC4215_POWER(name) \ - static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ - ltc4215_show_power, NULL, 0); - -#define LTC4215_ALARM(name, mask, reg) \ - static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \ - ltc4215_show_alarm, NULL, (mask), reg) - /* Construct a sensor_device_attribute structure for each register */ /* Current */ -LTC4215_CURRENT(curr1_input); -LTC4215_ALARM(curr1_max_alarm, (1 << 2), LTC4215_STATUS); +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4215_show_current, NULL, 0); +static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc4215_show_alarm, NULL, + 1 << 2); /* Power (virtual) */ -LTC4215_POWER(power1_input); -LTC4215_ALARM(power1_alarm, (1 << 3), LTC4215_STATUS); +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ltc4215_show_power, NULL, 0); /* Input Voltage */ -LTC4215_VOLTAGE(in1_input, LTC4215_ADIN); -LTC4215_ALARM(in1_max_alarm, (1 << 0), LTC4215_STATUS); -LTC4215_ALARM(in1_min_alarm, (1 << 1), LTC4215_STATUS); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4215_show_voltage, NULL, + LTC4215_ADIN); +static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ltc4215_show_alarm, NULL, + 1 << 0); +static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ltc4215_show_alarm, NULL, + 1 << 1); /* Output Voltage */ -LTC4215_VOLTAGE(in2_input, LTC4215_SOURCE); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4215_show_voltage, NULL, + LTC4215_SOURCE); +static SENSOR_DEVICE_ATTR(in2_min_alarm, S_IRUGO, ltc4215_show_alarm, NULL, + 1 << 3); -/* Finally, construct an array of pointers to members of the above objects, +/* + * Finally, construct an array of pointers to members of the above objects, * as required for sysfs_create_group() */ -static struct attribute *ltc4215_attributes[] = { +static struct attribute *ltc4215_attrs[] = { &sensor_dev_attr_curr1_input.dev_attr.attr, &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, &sensor_dev_attr_power1_input.dev_attr.attr, - &sensor_dev_attr_power1_alarm.dev_attr.attr, &sensor_dev_attr_in1_input.dev_attr.attr, &sensor_dev_attr_in1_max_alarm.dev_attr.attr, &sensor_dev_attr_in1_min_alarm.dev_attr.attr, &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_min_alarm.dev_attr.attr, NULL, }; - -static const struct attribute_group ltc4215_group = { - .attrs = ltc4215_attributes, -}; +ATTRIBUTE_GROUPS(ltc4215); static int ltc4215_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; struct ltc4215_data *data; - int ret; + struct device *hwmon_dev; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - ret = -ENOMEM; - goto out_kzalloc; - } + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; - i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->update_lock); /* Initialize the LTC4215 chip */ i2c_smbus_write_byte_data(client, LTC4215_FAULT, 0x00); - /* Register sysfs hooks */ - ret = sysfs_create_group(&client->dev.kobj, <c4215_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, <c4215_group); -out_sysfs_create_group: - kfree(data); -out_kzalloc: - return ret; -} - -static int ltc4215_remove(struct i2c_client *client) -{ - struct ltc4215_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, <c4215_group); - - kfree(data); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + ltc4215_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct i2c_device_id ltc4215_id[] = { @@ -305,23 +270,11 @@ static struct i2c_driver ltc4215_driver = { .name = "ltc4215", }, .probe = ltc4215_probe, - .remove = ltc4215_remove, .id_table = ltc4215_id, }; -static int __init ltc4215_init(void) -{ - return i2c_add_driver(<c4215_driver); -} - -static void __exit ltc4215_exit(void) -{ - i2c_del_driver(<c4215_driver); -} +module_i2c_driver(ltc4215_driver); MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>"); MODULE_DESCRIPTION("LTC4215 driver"); MODULE_LICENSE("GPL"); - -module_init(ltc4215_init); -module_exit(ltc4215_exit); diff --git a/drivers/hwmon/ltc4222.c b/drivers/hwmon/ltc4222.c new file mode 100644 index 00000000000..07c25653659 --- /dev/null +++ b/drivers/hwmon/ltc4222.c @@ -0,0 +1,237 @@ +/* + * Driver for Linear Technology LTC4222 Dual Hot Swap controller + * + * Copyright (c) 2014 Guenter Roeck + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/bitops.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/jiffies.h> +#include <linux/regmap.h> + +/* chip registers */ + +#define LTC4222_CONTROL1 0xd0 +#define LTC4222_ALERT1 0xd1 +#define LTC4222_STATUS1 0xd2 +#define LTC4222_FAULT1 0xd3 +#define LTC4222_CONTROL2 0xd4 +#define LTC4222_ALERT2 0xd5 +#define LTC4222_STATUS2 0xd6 +#define LTC4222_FAULT2 0xd7 +#define LTC4222_SOURCE1 0xd8 +#define LTC4222_SOURCE2 0xda +#define LTC4222_ADIN1 0xdc +#define LTC4222_ADIN2 0xde +#define LTC4222_SENSE1 0xe0 +#define LTC4222_SENSE2 0xe2 +#define LTC4222_ADC_CONTROL 0xe4 + +/* + * Fault register bits + */ +#define FAULT_OV BIT(0) +#define FAULT_UV BIT(1) +#define FAULT_OC BIT(2) +#define FAULT_POWER_BAD BIT(3) +#define FAULT_FET_BAD BIT(5) + +/* Return the voltage from the given register in mV or mA */ +static int ltc4222_get_value(struct device *dev, u8 reg) +{ + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int val; + u8 buf[2]; + int ret; + + ret = regmap_bulk_read(regmap, reg, buf, 2); + if (ret < 0) + return ret; + + val = ((buf[0] << 8) + buf[1]) >> 6; + + switch (reg) { + case LTC4222_ADIN1: + case LTC4222_ADIN2: + /* 1.25 mV resolution. Convert to mV. */ + val = DIV_ROUND_CLOSEST(val * 5, 4); + break; + case LTC4222_SOURCE1: + case LTC4222_SOURCE2: + /* 31.25 mV resolution. Convert to mV. */ + val = DIV_ROUND_CLOSEST(val * 125, 4); + break; + case LTC4222_SENSE1: + case LTC4222_SENSE2: + /* + * 62.5 uV 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 = DIV_ROUND_CLOSEST(val * 125, 2); + break; + default: + return -EINVAL; + } + return val; +} + +static ssize_t ltc4222_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int value; + + value = ltc4222_get_value(dev, attr->index); + if (value < 0) + return value; + return snprintf(buf, PAGE_SIZE, "%d\n", value); +} + +static ssize_t ltc4222_show_bool(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da); + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int fault; + int ret; + + ret = regmap_read(regmap, attr->nr, &fault); + if (ret < 0) + return ret; + fault &= attr->index; + if (fault) /* Clear reported faults in chip register */ + regmap_update_bits(regmap, attr->nr, attr->index, 0); + + return snprintf(buf, PAGE_SIZE, "%d\n", !!fault); +} + +/* Voltages */ +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4222_show_value, NULL, + LTC4222_SOURCE1); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4222_show_value, NULL, + LTC4222_ADIN1); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc4222_show_value, NULL, + LTC4222_SOURCE2); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc4222_show_value, NULL, + LTC4222_ADIN2); + +/* + * Voltage alarms + * UV/OV faults are associated with the input voltage, and power bad and fet + * faults are associated with the output voltage. + */ +static SENSOR_DEVICE_ATTR_2(in1_min_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT1, FAULT_UV); +static SENSOR_DEVICE_ATTR_2(in1_max_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT1, FAULT_OV); +static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT1, FAULT_POWER_BAD | FAULT_FET_BAD); + +static SENSOR_DEVICE_ATTR_2(in3_min_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT2, FAULT_UV); +static SENSOR_DEVICE_ATTR_2(in3_max_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT2, FAULT_OV); +static SENSOR_DEVICE_ATTR_2(in4_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT2, FAULT_POWER_BAD | FAULT_FET_BAD); + +/* Current (via sense resistor) */ +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4222_show_value, NULL, + LTC4222_SENSE1); +static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc4222_show_value, NULL, + LTC4222_SENSE2); + +/* Overcurrent alarm */ +static SENSOR_DEVICE_ATTR_2(curr1_max_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT1, FAULT_OC); +static SENSOR_DEVICE_ATTR_2(curr2_max_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT2, FAULT_OC); + +static struct attribute *ltc4222_attrs[] = { + &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_alarm.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_min_alarm.dev_attr.attr, + &sensor_dev_attr_in3_max_alarm.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + &sensor_dev_attr_curr2_input.dev_attr.attr, + &sensor_dev_attr_curr2_max_alarm.dev_attr.attr, + + NULL, +}; +ATTRIBUTE_GROUPS(ltc4222); + +static struct regmap_config ltc4222_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = LTC4222_ADC_CONTROL, +}; + +static int ltc4222_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, <c4222_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(regmap); + } + + /* Clear faults */ + regmap_write(regmap, LTC4222_FAULT1, 0x00); + regmap_write(regmap, LTC4222_FAULT2, 0x00); + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + regmap, + ltc4222_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id ltc4222_id[] = { + {"ltc4222", 0}, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ltc4222_id); + +static struct i2c_driver ltc4222_driver = { + .driver = { + .name = "ltc4222", + }, + .probe = ltc4222_probe, + .id_table = ltc4222_id, +}; + +module_i2c_driver(ltc4222_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("LTC4222 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c index 65c232a9d0c..681b5b7b3c3 100644 --- a/drivers/hwmon/ltc4245.c +++ b/drivers/hwmon/ltc4245.c @@ -21,6 +21,8 @@ #include <linux/i2c.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> +#include <linux/jiffies.h> +#include <linux/i2c/ltc4245.h> /* Here are names of the chip's registers (a.k.a. commands) */ enum ltc4245_cmd { @@ -45,13 +47,13 @@ enum ltc4245_cmd { LTC4245_VEEIN = 0x19, LTC4245_VEESENSE = 0x1a, LTC4245_VEEOUT = 0x1b, - LTC4245_GPIOADC1 = 0x1c, - LTC4245_GPIOADC2 = 0x1d, - LTC4245_GPIOADC3 = 0x1e, + LTC4245_GPIOADC = 0x1c, }; struct ltc4245_data { - struct device *hwmon_dev; + struct i2c_client *client; + + const struct attribute_group *groups[3]; struct mutex update_lock; bool valid; @@ -61,13 +63,76 @@ struct ltc4245_data { u8 cregs[0x08]; /* Voltage registers */ - u8 vregs[0x0f]; + u8 vregs[0x0d]; + + /* GPIO ADC registers */ + bool use_extra_gpios; + int gpios[3]; }; +/* + * Update the readings from the GPIO pins. If the driver has been configured to + * sample all GPIO's as analog voltages, a round-robin sampling method is used. + * Otherwise, only the configured GPIO pin is sampled. + * + * LOCKING: must hold data->update_lock + */ +static void ltc4245_update_gpios(struct device *dev) +{ + struct ltc4245_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + u8 gpio_curr, gpio_next, gpio_reg; + int i; + + /* no extra gpio support, we're basically done */ + if (!data->use_extra_gpios) { + data->gpios[0] = data->vregs[LTC4245_GPIOADC - 0x10]; + return; + } + + /* + * If the last reading was too long ago, then we mark all old GPIO + * readings as stale by setting them to -EAGAIN + */ + if (time_after(jiffies, data->last_updated + 5 * HZ)) { + for (i = 0; i < ARRAY_SIZE(data->gpios); i++) + data->gpios[i] = -EAGAIN; + } + + /* + * Get the current GPIO pin + * + * The datasheet calls these GPIO[1-3], but we'll calculate the zero + * based array index instead, and call them GPIO[0-2]. This is much + * easier to think about. + */ + gpio_curr = (data->cregs[LTC4245_GPIO] & 0xc0) >> 6; + if (gpio_curr > 0) + gpio_curr -= 1; + + /* Read the GPIO voltage from the GPIOADC register */ + data->gpios[gpio_curr] = data->vregs[LTC4245_GPIOADC - 0x10]; + + /* Find the next GPIO pin to read */ + gpio_next = (gpio_curr + 1) % ARRAY_SIZE(data->gpios); + + /* + * Calculate the correct setting for the GPIO register so it will + * sample the next GPIO pin + */ + gpio_reg = (data->cregs[LTC4245_GPIO] & 0x3f) | ((gpio_next + 1) << 6); + + /* Update the GPIO register */ + i2c_smbus_write_byte_data(client, LTC4245_GPIO, gpio_reg); + + /* Update saved data */ + data->cregs[LTC4245_GPIO] = gpio_reg; +} + static struct ltc4245_data *ltc4245_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct ltc4245_data *data = i2c_get_clientdata(client); + struct ltc4245_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; s32 val; int i; @@ -75,8 +140,6 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev) if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - dev_dbg(&client->dev, "Starting ltc4245 update\n"); - /* Read control registers -- 0x00 to 0x07 */ for (i = 0; i < ARRAY_SIZE(data->cregs); i++) { val = i2c_smbus_read_byte_data(client, i); @@ -86,7 +149,7 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev) data->cregs[i] = val; } - /* Read voltage registers -- 0x10 to 0x1f */ + /* Read voltage registers -- 0x10 to 0x1c */ for (i = 0; i < ARRAY_SIZE(data->vregs); i++) { val = i2c_smbus_read_byte_data(client, i+0x10); if (unlikely(val < 0)) @@ -95,6 +158,9 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev) data->vregs[i] = val; } + /* Update GPIO readings */ + ltc4245_update_gpios(dev); + data->last_updated = jiffies; data->valid = 1; } @@ -128,9 +194,7 @@ static int ltc4245_get_voltage(struct device *dev, u8 reg) case LTC4245_VEEOUT: voltage = regval * -55; break; - case LTC4245_GPIOADC1: - case LTC4245_GPIOADC2: - case LTC4245_GPIOADC3: + case LTC4245_GPIOADC: voltage = regval * 10; break; default: @@ -150,7 +214,8 @@ static unsigned int ltc4245_get_current(struct device *dev, u8 reg) unsigned int voltage; unsigned int curr; - /* The strange looking conversions that follow are fixed-point + /* + * The strange looking conversions that follow are fixed-point * math, since we cannot do floating point in the kernel. * * Step 1: convert sense register to microVolts @@ -237,80 +302,104 @@ static ssize_t ltc4245_show_alarm(struct device *dev, return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 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 LTC4245_VOLTAGE(name, ltc4245_cmd_idx) \ - static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ - ltc4245_show_voltage, NULL, ltc4245_cmd_idx) - -#define LTC4245_CURRENT(name, ltc4245_cmd_idx) \ - static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ - ltc4245_show_current, NULL, ltc4245_cmd_idx) +static ssize_t ltc4245_show_gpio(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct ltc4245_data *data = ltc4245_update_device(dev); + int val = data->gpios[attr->index]; -#define LTC4245_POWER(name, ltc4245_cmd_idx) \ - static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ - ltc4245_show_power, NULL, ltc4245_cmd_idx) + /* handle stale GPIO's */ + if (val < 0) + return val; -#define LTC4245_ALARM(name, mask, reg) \ - static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \ - ltc4245_show_alarm, NULL, (mask), reg) + /* Convert to millivolts and print */ + return snprintf(buf, PAGE_SIZE, "%u\n", val * 10); +} /* Construct a sensor_device_attribute structure for each register */ /* Input voltages */ -LTC4245_VOLTAGE(in1_input, LTC4245_12VIN); -LTC4245_VOLTAGE(in2_input, LTC4245_5VIN); -LTC4245_VOLTAGE(in3_input, LTC4245_3VIN); -LTC4245_VOLTAGE(in4_input, LTC4245_VEEIN); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4245_show_voltage, NULL, + LTC4245_12VIN); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4245_show_voltage, NULL, + LTC4245_5VIN); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc4245_show_voltage, NULL, + LTC4245_3VIN); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc4245_show_voltage, NULL, + LTC4245_VEEIN); /* Input undervoltage alarms */ -LTC4245_ALARM(in1_min_alarm, (1 << 0), LTC4245_FAULT1); -LTC4245_ALARM(in2_min_alarm, (1 << 1), LTC4245_FAULT1); -LTC4245_ALARM(in3_min_alarm, (1 << 2), LTC4245_FAULT1); -LTC4245_ALARM(in4_min_alarm, (1 << 3), LTC4245_FAULT1); +static SENSOR_DEVICE_ATTR_2(in1_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 0, LTC4245_FAULT1); +static SENSOR_DEVICE_ATTR_2(in2_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 1, LTC4245_FAULT1); +static SENSOR_DEVICE_ATTR_2(in3_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 2, LTC4245_FAULT1); +static SENSOR_DEVICE_ATTR_2(in4_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 3, LTC4245_FAULT1); /* Currents (via sense resistor) */ -LTC4245_CURRENT(curr1_input, LTC4245_12VSENSE); -LTC4245_CURRENT(curr2_input, LTC4245_5VSENSE); -LTC4245_CURRENT(curr3_input, LTC4245_3VSENSE); -LTC4245_CURRENT(curr4_input, LTC4245_VEESENSE); +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4245_show_current, NULL, + LTC4245_12VSENSE); +static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc4245_show_current, NULL, + LTC4245_5VSENSE); +static SENSOR_DEVICE_ATTR(curr3_input, S_IRUGO, ltc4245_show_current, NULL, + LTC4245_3VSENSE); +static SENSOR_DEVICE_ATTR(curr4_input, S_IRUGO, ltc4245_show_current, NULL, + LTC4245_VEESENSE); /* Overcurrent alarms */ -LTC4245_ALARM(curr1_max_alarm, (1 << 4), LTC4245_FAULT1); -LTC4245_ALARM(curr2_max_alarm, (1 << 5), LTC4245_FAULT1); -LTC4245_ALARM(curr3_max_alarm, (1 << 6), LTC4245_FAULT1); -LTC4245_ALARM(curr4_max_alarm, (1 << 7), LTC4245_FAULT1); +static SENSOR_DEVICE_ATTR_2(curr1_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 4, LTC4245_FAULT1); +static SENSOR_DEVICE_ATTR_2(curr2_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 5, LTC4245_FAULT1); +static SENSOR_DEVICE_ATTR_2(curr3_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 6, LTC4245_FAULT1); +static SENSOR_DEVICE_ATTR_2(curr4_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 7, LTC4245_FAULT1); /* Output voltages */ -LTC4245_VOLTAGE(in5_input, LTC4245_12VOUT); -LTC4245_VOLTAGE(in6_input, LTC4245_5VOUT); -LTC4245_VOLTAGE(in7_input, LTC4245_3VOUT); -LTC4245_VOLTAGE(in8_input, LTC4245_VEEOUT); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, ltc4245_show_voltage, NULL, + LTC4245_12VOUT); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, ltc4245_show_voltage, NULL, + LTC4245_5VOUT); +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, ltc4245_show_voltage, NULL, + LTC4245_3VOUT); +static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, ltc4245_show_voltage, NULL, + LTC4245_VEEOUT); /* Power Bad alarms */ -LTC4245_ALARM(in5_min_alarm, (1 << 0), LTC4245_FAULT2); -LTC4245_ALARM(in6_min_alarm, (1 << 1), LTC4245_FAULT2); -LTC4245_ALARM(in7_min_alarm, (1 << 2), LTC4245_FAULT2); -LTC4245_ALARM(in8_min_alarm, (1 << 3), LTC4245_FAULT2); +static SENSOR_DEVICE_ATTR_2(in5_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 0, LTC4245_FAULT2); +static SENSOR_DEVICE_ATTR_2(in6_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 1, LTC4245_FAULT2); +static SENSOR_DEVICE_ATTR_2(in7_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 2, LTC4245_FAULT2); +static SENSOR_DEVICE_ATTR_2(in8_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL, + 1 << 3, LTC4245_FAULT2); /* GPIO voltages */ -LTC4245_VOLTAGE(in9_input, LTC4245_GPIOADC1); -LTC4245_VOLTAGE(in10_input, LTC4245_GPIOADC2); -LTC4245_VOLTAGE(in11_input, LTC4245_GPIOADC3); +static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, ltc4245_show_gpio, NULL, 0); +static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, ltc4245_show_gpio, NULL, 1); +static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, ltc4245_show_gpio, NULL, 2); /* Power Consumption (virtual) */ -LTC4245_POWER(power1_input, LTC4245_12VSENSE); -LTC4245_POWER(power2_input, LTC4245_5VSENSE); -LTC4245_POWER(power3_input, LTC4245_3VSENSE); -LTC4245_POWER(power4_input, LTC4245_VEESENSE); +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ltc4245_show_power, NULL, + LTC4245_12VSENSE); +static SENSOR_DEVICE_ATTR(power2_input, S_IRUGO, ltc4245_show_power, NULL, + LTC4245_5VSENSE); +static SENSOR_DEVICE_ATTR(power3_input, S_IRUGO, ltc4245_show_power, NULL, + LTC4245_3VSENSE); +static SENSOR_DEVICE_ATTR(power4_input, S_IRUGO, ltc4245_show_power, NULL, + LTC4245_VEESENSE); -/* Finally, construct an array of pointers to members of the above objects, +/* + * Finally, construct an array of pointers to members of the above objects, * as required for sysfs_create_group() */ -static struct attribute *ltc4245_attributes[] = { +static struct attribute *ltc4245_std_attributes[] = { &sensor_dev_attr_in1_input.dev_attr.attr, &sensor_dev_attr_in2_input.dev_attr.attr, &sensor_dev_attr_in3_input.dev_attr.attr, @@ -342,8 +431,6 @@ static struct attribute *ltc4245_attributes[] = { &sensor_dev_attr_in8_min_alarm.dev_attr.attr, &sensor_dev_attr_in9_input.dev_attr.attr, - &sensor_dev_attr_in10_input.dev_attr.attr, - &sensor_dev_attr_in11_input.dev_attr.attr, &sensor_dev_attr_power1_input.dev_attr.attr, &sensor_dev_attr_power2_input.dev_attr.attr, @@ -353,64 +440,75 @@ static struct attribute *ltc4245_attributes[] = { NULL, }; -static const struct attribute_group ltc4245_group = { - .attrs = ltc4245_attributes, +static struct attribute *ltc4245_gpio_attributes[] = { + &sensor_dev_attr_in10_input.dev_attr.attr, + &sensor_dev_attr_in11_input.dev_attr.attr, + NULL, }; +static const struct attribute_group ltc4245_std_group = { + .attrs = ltc4245_std_attributes, +}; + +static const struct attribute_group ltc4245_gpio_group = { + .attrs = ltc4245_gpio_attributes, +}; + +static void ltc4245_sysfs_add_groups(struct ltc4245_data *data) +{ + /* standard sysfs attributes */ + data->groups[0] = <c4245_std_group; + + /* if we're using the extra gpio support, register it's attributes */ + if (data->use_extra_gpios) + data->groups[1] = <c4245_gpio_group; +} + +static bool ltc4245_use_extra_gpios(struct i2c_client *client) +{ + struct ltc4245_platform_data *pdata = dev_get_platdata(&client->dev); + struct device_node *np = client->dev.of_node; + + /* prefer platform data */ + if (pdata) + return pdata->use_extra_gpios; + + /* fallback on OF */ + if (of_find_property(np, "ltc4245,use-extra-gpios", NULL)) + return true; + + return false; +} + static int ltc4245_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = client->adapter; struct ltc4245_data *data; - int ret; + struct device *hwmon_dev; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - ret = -ENOMEM; - goto out_kzalloc; - } + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; - i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->update_lock); + data->use_extra_gpios = ltc4245_use_extra_gpios(client); /* Initialize the LTC4245 chip */ i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00); i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00); - /* Register sysfs hooks */ - ret = sysfs_create_group(&client->dev.kobj, <c4245_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; + /* Add sysfs hooks */ + ltc4245_sysfs_add_groups(data); -out_hwmon_device_register: - sysfs_remove_group(&client->dev.kobj, <c4245_group); -out_sysfs_create_group: - kfree(data); -out_kzalloc: - return ret; -} - -static int ltc4245_remove(struct i2c_client *client) -{ - struct ltc4245_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, <c4245_group); - - kfree(data); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, + client->name, data, + data->groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct i2c_device_id ltc4245_id[] = { @@ -425,23 +523,11 @@ static struct i2c_driver ltc4245_driver = { .name = "ltc4245", }, .probe = ltc4245_probe, - .remove = ltc4245_remove, .id_table = ltc4245_id, }; -static int __init ltc4245_init(void) -{ - return i2c_add_driver(<c4245_driver); -} - -static void __exit ltc4245_exit(void) -{ - i2c_del_driver(<c4245_driver); -} +module_i2c_driver(ltc4245_driver); MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>"); MODULE_DESCRIPTION("LTC4245 driver"); MODULE_LICENSE("GPL"); - -module_init(ltc4245_init); -module_exit(ltc4245_exit); diff --git a/drivers/hwmon/ltc4260.c b/drivers/hwmon/ltc4260.c new file mode 100644 index 00000000000..453a250d9df --- /dev/null +++ b/drivers/hwmon/ltc4260.c @@ -0,0 +1,200 @@ +/* + * Driver for Linear Technology LTC4260 I2C Positive Voltage Hot Swap Controller + * + * Copyright (c) 2014 Guenter Roeck + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/jiffies.h> +#include <linux/regmap.h> + +/* chip registers */ +#define LTC4260_CONTROL 0x00 +#define LTC4260_ALERT 0x01 +#define LTC4260_STATUS 0x02 +#define LTC4260_FAULT 0x03 +#define LTC4260_SENSE 0x04 +#define LTC4260_SOURCE 0x05 +#define LTC4260_ADIN 0x06 + +/* + * Fault register bits + */ +#define FAULT_OV (1 << 0) +#define FAULT_UV (1 << 1) +#define FAULT_OC (1 << 2) +#define FAULT_POWER_BAD (1 << 3) +#define FAULT_FET_SHORT (1 << 5) + +/* Return the voltage from the given register in mV or mA */ +static int ltc4260_get_value(struct device *dev, u8 reg) +{ + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int val; + int ret; + + ret = regmap_read(regmap, reg, &val); + if (ret < 0) + return ret; + + switch (reg) { + case LTC4260_ADIN: + /* 10 mV resolution. Convert to mV. */ + val = val * 10; + break; + case LTC4260_SOURCE: + /* 400 mV resolution. Convert to mV. */ + val = val * 400; + break; + case LTC4260_SENSE: + /* + * 300 uV 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 * 300; + break; + default: + return -EINVAL; + } + + return val; +} + +static ssize_t ltc4260_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int value; + + value = ltc4260_get_value(dev, attr->index); + if (value < 0) + return value; + return snprintf(buf, PAGE_SIZE, "%d\n", value); +} + +static ssize_t ltc4260_show_bool(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int fault; + int ret; + + ret = regmap_read(regmap, LTC4260_FAULT, &fault); + if (ret < 0) + return ret; + + fault &= attr->index; + if (fault) /* Clear reported faults in chip register */ + regmap_update_bits(regmap, LTC4260_FAULT, attr->index, 0); + + return snprintf(buf, PAGE_SIZE, "%d\n", !!fault); +} + +/* Voltages */ +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4260_show_value, NULL, + LTC4260_SOURCE); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4260_show_value, NULL, + LTC4260_ADIN); + +/* + * Voltage alarms + * UV/OV faults are associated with the input voltage, and the POWER BAD and + * FET SHORT faults are associated with the output voltage. + */ +static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ltc4260_show_bool, NULL, + FAULT_UV); +static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ltc4260_show_bool, NULL, + FAULT_OV); +static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, ltc4260_show_bool, NULL, + FAULT_POWER_BAD | FAULT_FET_SHORT); + +/* Current (via sense resistor) */ +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4260_show_value, NULL, + LTC4260_SENSE); + +/* Overcurrent alarm */ +static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc4260_show_bool, NULL, + FAULT_OC); + +static struct attribute *ltc4260_attrs[] = { + &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_alarm.dev_attr.attr, + + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + + NULL, +}; +ATTRIBUTE_GROUPS(ltc4260); + +static struct regmap_config ltc4260_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = LTC4260_ADIN, +}; + +static int ltc4260_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, <c4260_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(regmap); + } + + /* Clear faults */ + regmap_write(regmap, LTC4260_FAULT, 0x00); + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + regmap, + ltc4260_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id ltc4260_id[] = { + {"ltc4260", 0}, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ltc4260_id); + +static struct i2c_driver ltc4260_driver = { + .driver = { + .name = "ltc4260", + }, + .probe = ltc4260_probe, + .id_table = ltc4260_id, +}; + +module_i2c_driver(ltc4260_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("LTC4260 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ltc4261.c b/drivers/hwmon/ltc4261.c new file mode 100644 index 00000000000..0becd69842b --- /dev/null +++ b/drivers/hwmon/ltc4261.c @@ -0,0 +1,266 @@ +/* + * 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> +#include <linux/jiffies.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 i2c_client *client; + + 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 ltc4261_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->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\n", + val); + ret = ERR_PTR(val); + data->valid = 0; + 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 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(data->client, LTC4261_FAULT, ~fault); + + return snprintf(buf, PAGE_SIZE, "%d\n", fault ? 1 : 0); +} + +/* + * Input voltages. + */ +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4261_show_value, NULL, + LTC4261_ADIN_H); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4261_show_value, NULL, + LTC4261_ADIN2_H); + +/* + * Voltage alarms. The chip has only one set of voltage alarm status bits, + * 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. + */ +static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ltc4261_show_bool, NULL, + FAULT_UV); +static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ltc4261_show_bool, NULL, + FAULT_OV); +static SENSOR_DEVICE_ATTR(in2_min_alarm, S_IRUGO, ltc4261_show_bool, NULL, + FAULT_UV); +static SENSOR_DEVICE_ATTR(in2_max_alarm, S_IRUGO, ltc4261_show_bool, NULL, + FAULT_OV); + +/* Currents (via sense resistor) */ +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4261_show_value, NULL, + LTC4261_SENSE_H); + +/* Overcurrent alarm */ +static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc4261_show_bool, NULL, + FAULT_OC); + +static struct attribute *ltc4261_attrs[] = { + &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, +}; +ATTRIBUTE_GROUPS(ltc4261); + +static int ltc4261_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; + struct ltc4261_data *data; + struct device *hwmon_dev; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + if (i2c_smbus_read_byte_data(client, LTC4261_STATUS) < 0) { + dev_err(dev, "Failed to read status register\n"); + return -ENODEV; + } + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + mutex_init(&data->update_lock); + + /* Clear faults */ + i2c_smbus_write_byte_data(client, LTC4261_FAULT, 0x00); + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + ltc4261_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +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, + .id_table = ltc4261_id, +}; + +module_i2c_driver(ltc4261_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("LTC4261 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/max1111.c b/drivers/hwmon/max1111.c index 9ac497271ad..f67d71ee838 100644 --- a/drivers/hwmon/max1111.c +++ b/drivers/hwmon/max1111.c @@ -6,7 +6,7 @@ * Copyright (C) 2004-2005 Richard Purdie * * Copyright (C) 2008 Marvell International Ltd. - * Eric Miao <eric.miao@marvell.com> + * Eric Miao <eric.miao@marvell.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -20,6 +20,9 @@ #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> #include <linux/spi/spi.h> +#include <linux/slab.h> + +enum chips { max1110, max1111, max1112, max1113 }; #define MAX1111_TX_BUF_SIZE 1 #define MAX1111_RX_BUF_SIZE 2 @@ -29,6 +32,7 @@ #define MAX1111_CTRL_PD1 (1u << 1) #define MAX1111_CTRL_SGL (1u << 2) #define MAX1111_CTRL_UNI (1u << 3) +#define MAX1110_CTRL_SEL_SH (4) #define MAX1111_CTRL_SEL_SH (5) /* NOTE: bit 4 is ignored */ #define MAX1111_CTRL_STR (1u << 7) @@ -37,8 +41,12 @@ struct max1111_data { struct device *hwmon_dev; struct spi_message msg; struct spi_transfer xfer[2]; - uint8_t *tx_buf; - uint8_t *rx_buf; + uint8_t tx_buf[MAX1111_TX_BUF_SIZE]; + uint8_t rx_buf[MAX1111_RX_BUF_SIZE]; + struct mutex drvdata_lock; + /* protect msg, xfer and buffers from multiple access */ + int sel_sh; + int lsb; }; static int max1111_read(struct device *dev, int channel) @@ -47,19 +55,25 @@ static int max1111_read(struct device *dev, int channel) uint8_t v1, v2; int err; - data->tx_buf[0] = (channel << MAX1111_CTRL_SEL_SH) | + /* writing to drvdata struct is not thread safe, wait on mutex */ + mutex_lock(&data->drvdata_lock); + + data->tx_buf[0] = (channel << data->sel_sh) | MAX1111_CTRL_PD0 | MAX1111_CTRL_PD1 | MAX1111_CTRL_SGL | MAX1111_CTRL_UNI | MAX1111_CTRL_STR; err = spi_sync(data->spi, &data->msg); if (err < 0) { dev_err(dev, "spi_sync failed with %d\n", err); + mutex_unlock(&data->drvdata_lock); return err; } v1 = data->rx_buf[0]; v2 = data->rx_buf[1]; + mutex_unlock(&data->drvdata_lock); + if ((v1 & 0xc0) || (v2 & 0x3f)) return -EINVAL; @@ -84,12 +98,13 @@ EXPORT_SYMBOL(max1111_read_channel); static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "max1111\n"); + return sprintf(buf, "%s\n", to_spi_device(dev)->modalias); } static ssize_t show_adc(struct device *dev, struct device_attribute *attr, char *buf) { + struct max1111_data *data = dev_get_drvdata(dev); int channel = to_sensor_dev_attr(attr)->index; int ret; @@ -97,24 +112,32 @@ static ssize_t show_adc(struct device *dev, if (ret < 0) return ret; - return sprintf(buf, "%d\n", ret); + /* + * Assume the reference voltage to be 2.048V or 4.096V, with an 8-bit + * sample. The LSB weight is 8mV or 16mV depending on the chip type. + */ + return sprintf(buf, "%d\n", ret * data->lsb); } #define MAX1111_ADC_ATTR(_id) \ - SENSOR_DEVICE_ATTR(adc##_id##_in, S_IRUGO, show_adc, NULL, _id) + SENSOR_DEVICE_ATTR(in##_id##_input, S_IRUGO, show_adc, NULL, _id) static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); static MAX1111_ADC_ATTR(0); static MAX1111_ADC_ATTR(1); static MAX1111_ADC_ATTR(2); static MAX1111_ADC_ATTR(3); +static MAX1111_ADC_ATTR(4); +static MAX1111_ADC_ATTR(5); +static MAX1111_ADC_ATTR(6); +static MAX1111_ADC_ATTR(7); static struct attribute *max1111_attributes[] = { &dev_attr_name.attr, - &sensor_dev_attr_adc0_in.dev_attr.attr, - &sensor_dev_attr_adc1_in.dev_attr.attr, - &sensor_dev_attr_adc2_in.dev_attr.attr, - &sensor_dev_attr_adc3_in.dev_attr.attr, + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, NULL, }; @@ -122,40 +145,43 @@ static const struct attribute_group max1111_attr_group = { .attrs = max1111_attributes, }; +static struct attribute *max1110_attributes[] = { + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + NULL, +}; + +static const struct attribute_group max1110_attr_group = { + .attrs = max1110_attributes, +}; + static int setup_transfer(struct max1111_data *data) { struct spi_message *m; struct spi_transfer *x; - data->tx_buf = kmalloc(MAX1111_TX_BUF_SIZE, GFP_KERNEL); - if (!data->tx_buf) - return -ENOMEM; - - data->rx_buf = kmalloc(MAX1111_RX_BUF_SIZE, GFP_KERNEL); - if (!data->rx_buf) { - kfree(data->tx_buf); - return -ENOMEM; - } - m = &data->msg; x = &data->xfer[0]; spi_message_init(m); x->tx_buf = &data->tx_buf[0]; - x->len = 1; + x->len = MAX1111_TX_BUF_SIZE; spi_message_add_tail(x, m); x++; x->rx_buf = &data->rx_buf[0]; - x->len = 2; + x->len = MAX1111_RX_BUF_SIZE; spi_message_add_tail(x, m); return 0; } -static int __devinit max1111_probe(struct spi_device *spi) +static int max1111_probe(struct spi_device *spi) { + enum chips chip = spi_get_device_id(spi)->driver_data; struct max1111_data *data; int err; @@ -165,15 +191,33 @@ static int __devinit max1111_probe(struct spi_device *spi) if (err < 0) return err; - data = kzalloc(sizeof(struct max1111_data), GFP_KERNEL); - if (data == NULL) { - dev_err(&spi->dev, "failed to allocate memory\n"); + data = devm_kzalloc(&spi->dev, sizeof(struct max1111_data), GFP_KERNEL); + if (data == NULL) return -ENOMEM; - } + switch (chip) { + case max1110: + data->lsb = 8; + data->sel_sh = MAX1110_CTRL_SEL_SH; + break; + case max1111: + data->lsb = 8; + data->sel_sh = MAX1111_CTRL_SEL_SH; + break; + case max1112: + data->lsb = 16; + data->sel_sh = MAX1110_CTRL_SEL_SH; + break; + case max1113: + data->lsb = 16; + data->sel_sh = MAX1111_CTRL_SEL_SH; + break; + } err = setup_transfer(data); if (err) - goto err_free_data; + return err; + + mutex_init(&data->drvdata_lock); data->spi = spi; spi_set_drvdata(spi, data); @@ -181,7 +225,15 @@ static int __devinit max1111_probe(struct spi_device *spi) err = sysfs_create_group(&spi->dev.kobj, &max1111_attr_group); if (err) { dev_err(&spi->dev, "failed to create attribute group\n"); - goto err_free_all; + return err; + } + if (chip == max1110 || chip == max1112) { + err = sysfs_create_group(&spi->dev.kobj, &max1110_attr_group); + if (err) { + dev_err(&spi->dev, + "failed to create extended attribute group\n"); + goto err_remove; + } } data->hwmon_dev = hwmon_device_register(&spi->dev); @@ -197,49 +249,43 @@ static int __devinit max1111_probe(struct spi_device *spi) return 0; err_remove: + sysfs_remove_group(&spi->dev.kobj, &max1110_attr_group); sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group); -err_free_all: - kfree(data->rx_buf); - kfree(data->tx_buf); -err_free_data: - kfree(data); return err; } -static int __devexit max1111_remove(struct spi_device *spi) +static int max1111_remove(struct spi_device *spi) { struct max1111_data *data = spi_get_drvdata(spi); hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&spi->dev.kobj, &max1110_attr_group); sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group); - kfree(data->rx_buf); - kfree(data->tx_buf); - kfree(data); + mutex_destroy(&data->drvdata_lock); return 0; } +static const struct spi_device_id max1111_ids[] = { + { "max1110", max1110 }, + { "max1111", max1111 }, + { "max1112", max1112 }, + { "max1113", max1113 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, max1111_ids); + static struct spi_driver max1111_driver = { .driver = { .name = "max1111", .owner = THIS_MODULE, }, + .id_table = max1111_ids, .probe = max1111_probe, - .remove = __devexit_p(max1111_remove), + .remove = max1111_remove, }; -static int __init max1111_init(void) -{ - return spi_register_driver(&max1111_driver); -} -module_init(max1111_init); - -static void __exit max1111_exit(void) -{ - spi_unregister_driver(&max1111_driver); -} -module_exit(max1111_exit); +module_spi_driver(max1111_driver); MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); -MODULE_DESCRIPTION("MAX1111 ADC Driver"); +MODULE_DESCRIPTION("MAX1110/MAX1111/MAX1112/MAX1113 ADC Driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("spi:max1111"); diff --git a/drivers/hwmon/max16065.c b/drivers/hwmon/max16065.c new file mode 100644 index 00000000000..d4efc79d7b9 --- /dev/null +++ b/drivers/hwmon/max16065.c @@ -0,0 +1,676 @@ +/* + * Driver for + * Maxim MAX16065/MAX16066 12-Channel/8-Channel, Flash-Configurable + * System Managers with Nonvolatile Fault Registers + * Maxim MAX16067/MAX16068 6-Channel, Flash-Configurable System Managers + * with Nonvolatile Fault Registers + * Maxim MAX16070/MAX16071 12-Channel/8-Channel, Flash-Configurable System + * Monitors with Nonvolatile Fault Registers + * + * Copyright (C) 2011 Ericsson AB. + * + * 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 of the License. + */ + +#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> +#include <linux/jiffies.h> + +enum chips { max16065, max16066, max16067, max16068, max16070, max16071 }; + +/* + * Registers + */ +#define MAX16065_ADC(x) ((x) * 2) + +#define MAX16065_CURR_SENSE 0x18 +#define MAX16065_CSP_ADC 0x19 +#define MAX16065_FAULT(x) (0x1b + (x)) +#define MAX16065_SCALE(x) (0x43 + (x)) +#define MAX16065_CURR_CONTROL 0x47 +#define MAX16065_LIMIT(l, x) (0x48 + (l) + (x) * 3) /* + * l: limit + * 0: min/max + * 1: crit + * 2: lcrit + * x: ADC index + */ + +#define MAX16065_SW_ENABLE 0x73 + +#define MAX16065_WARNING_OV (1 << 3) /* Set if secondary threshold is OV + warning */ + +#define MAX16065_CURR_ENABLE (1 << 0) + +#define MAX16065_NUM_LIMIT 3 +#define MAX16065_NUM_ADC 12 /* maximum number of ADC channels */ + +static const int max16065_num_adc[] = { + [max16065] = 12, + [max16066] = 8, + [max16067] = 6, + [max16068] = 6, + [max16070] = 12, + [max16071] = 8, +}; + +static const bool max16065_have_secondary[] = { + [max16065] = true, + [max16066] = true, + [max16067] = false, + [max16068] = false, + [max16070] = true, + [max16071] = true, +}; + +static const bool max16065_have_current[] = { + [max16065] = true, + [max16066] = true, + [max16067] = false, + [max16068] = false, + [max16070] = true, + [max16071] = true, +}; + +struct max16065_data { + enum chips type; + struct i2c_client *client; + const struct attribute_group *groups[4]; + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + int num_adc; + bool have_current; + int curr_gain; + /* limits are in mV */ + int limit[MAX16065_NUM_LIMIT][MAX16065_NUM_ADC]; + int range[MAX16065_NUM_ADC + 1];/* voltage range */ + int adc[MAX16065_NUM_ADC + 1]; /* adc values (raw) including csp_adc */ + int curr_sense; + int fault[2]; +}; + +static const int max16065_adc_range[] = { 5560, 2780, 1390, 0 }; +static const int max16065_csp_adc_range[] = { 7000, 14000 }; + +/* ADC registers have 10 bit resolution. */ +static inline int ADC_TO_MV(int adc, int range) +{ + return (adc * range) / 1024; +} + +/* + * Limit registers have 8 bit resolution and match upper 8 bits of ADC + * registers. + */ +static inline int LIMIT_TO_MV(int limit, int range) +{ + return limit * range / 256; +} + +static inline int MV_TO_LIMIT(int mv, int range) +{ + return clamp_val(DIV_ROUND_CLOSEST(mv * 256, range), 0, 255); +} + +static inline int ADC_TO_CURR(int adc, int gain) +{ + return adc * 1400000 / (gain * 255); +} + +/* + * max16065_read_adc() + * + * Read 16 bit value from <reg>, <reg+1>. + * Upper 8 bits are in <reg>, lower 2 bits are in bits 7:6 of <reg+1>. + */ +static int max16065_read_adc(struct i2c_client *client, int reg) +{ + int rv; + + rv = i2c_smbus_read_word_swapped(client, reg); + if (unlikely(rv < 0)) + return rv; + return rv >> 6; +} + +static struct max16065_data *max16065_update_device(struct device *dev) +{ + struct max16065_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + mutex_lock(&data->update_lock); + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + int i; + + for (i = 0; i < data->num_adc; i++) + data->adc[i] + = max16065_read_adc(client, MAX16065_ADC(i)); + + if (data->have_current) { + data->adc[MAX16065_NUM_ADC] + = max16065_read_adc(client, MAX16065_CSP_ADC); + data->curr_sense + = i2c_smbus_read_byte_data(client, + MAX16065_CURR_SENSE); + } + + for (i = 0; i < DIV_ROUND_UP(data->num_adc, 8); i++) + data->fault[i] + = i2c_smbus_read_byte_data(client, MAX16065_FAULT(i)); + + data->last_updated = jiffies; + data->valid = 1; + } + mutex_unlock(&data->update_lock); + return data; +} + +static ssize_t max16065_show_alarm(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(da); + struct max16065_data *data = max16065_update_device(dev); + int val = data->fault[attr2->nr]; + + if (val < 0) + return val; + + val &= (1 << attr2->index); + if (val) + i2c_smbus_write_byte_data(data->client, + MAX16065_FAULT(attr2->nr), val); + + return snprintf(buf, PAGE_SIZE, "%d\n", !!val); +} + +static ssize_t max16065_show_input(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct max16065_data *data = max16065_update_device(dev); + int adc = data->adc[attr->index]; + + if (unlikely(adc < 0)) + return adc; + + return snprintf(buf, PAGE_SIZE, "%d\n", + ADC_TO_MV(adc, data->range[attr->index])); +} + +static ssize_t max16065_show_current(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct max16065_data *data = max16065_update_device(dev); + + if (unlikely(data->curr_sense < 0)) + return data->curr_sense; + + return snprintf(buf, PAGE_SIZE, "%d\n", + ADC_TO_CURR(data->curr_sense, data->curr_gain)); +} + +static ssize_t max16065_set_limit(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(da); + struct max16065_data *data = dev_get_drvdata(dev); + unsigned long val; + int err; + int limit; + + err = kstrtoul(buf, 10, &val); + if (unlikely(err < 0)) + return err; + + limit = MV_TO_LIMIT(val, data->range[attr2->index]); + + mutex_lock(&data->update_lock); + data->limit[attr2->nr][attr2->index] + = LIMIT_TO_MV(limit, data->range[attr2->index]); + i2c_smbus_write_byte_data(data->client, + MAX16065_LIMIT(attr2->nr, attr2->index), + limit); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t max16065_show_limit(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(da); + struct max16065_data *data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", + data->limit[attr2->nr][attr2->index]); +} + +/* Construct a sensor_device_attribute structure for each register */ + +/* Input voltages */ +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, max16065_show_input, NULL, 0); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, max16065_show_input, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, max16065_show_input, NULL, 2); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, max16065_show_input, NULL, 3); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, max16065_show_input, NULL, 4); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, max16065_show_input, NULL, 5); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, max16065_show_input, NULL, 6); +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, max16065_show_input, NULL, 7); +static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, max16065_show_input, NULL, 8); +static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, max16065_show_input, NULL, 9); +static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, max16065_show_input, NULL, 10); +static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, max16065_show_input, NULL, 11); +static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, max16065_show_input, NULL, 12); + +/* Input voltages lcrit */ +static SENSOR_DEVICE_ATTR_2(in0_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 2, 0); +static SENSOR_DEVICE_ATTR_2(in1_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 2, 1); +static SENSOR_DEVICE_ATTR_2(in2_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 2, 2); +static SENSOR_DEVICE_ATTR_2(in3_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 2, 3); +static SENSOR_DEVICE_ATTR_2(in4_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 2, 4); +static SENSOR_DEVICE_ATTR_2(in5_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 2, 5); +static SENSOR_DEVICE_ATTR_2(in6_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 2, 6); +static SENSOR_DEVICE_ATTR_2(in7_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 2, 7); +static SENSOR_DEVICE_ATTR_2(in8_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 2, 8); +static SENSOR_DEVICE_ATTR_2(in9_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 2, 9); +static SENSOR_DEVICE_ATTR_2(in10_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 2, 10); +static SENSOR_DEVICE_ATTR_2(in11_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 2, 11); + +/* Input voltages crit */ +static SENSOR_DEVICE_ATTR_2(in0_crit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 1, 0); +static SENSOR_DEVICE_ATTR_2(in1_crit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 1, 1); +static SENSOR_DEVICE_ATTR_2(in2_crit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 1, 2); +static SENSOR_DEVICE_ATTR_2(in3_crit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 1, 3); +static SENSOR_DEVICE_ATTR_2(in4_crit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 1, 4); +static SENSOR_DEVICE_ATTR_2(in5_crit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 1, 5); +static SENSOR_DEVICE_ATTR_2(in6_crit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 1, 6); +static SENSOR_DEVICE_ATTR_2(in7_crit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 1, 7); +static SENSOR_DEVICE_ATTR_2(in8_crit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 1, 8); +static SENSOR_DEVICE_ATTR_2(in9_crit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 1, 9); +static SENSOR_DEVICE_ATTR_2(in10_crit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 1, 10); +static SENSOR_DEVICE_ATTR_2(in11_crit, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 1, 11); + +/* Input voltages min */ +static SENSOR_DEVICE_ATTR_2(in0_min, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 0); +static SENSOR_DEVICE_ATTR_2(in1_min, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 1); +static SENSOR_DEVICE_ATTR_2(in2_min, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 2); +static SENSOR_DEVICE_ATTR_2(in3_min, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 3); +static SENSOR_DEVICE_ATTR_2(in4_min, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 4); +static SENSOR_DEVICE_ATTR_2(in5_min, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 5); +static SENSOR_DEVICE_ATTR_2(in6_min, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 6); +static SENSOR_DEVICE_ATTR_2(in7_min, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 7); +static SENSOR_DEVICE_ATTR_2(in8_min, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 8); +static SENSOR_DEVICE_ATTR_2(in9_min, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 9); +static SENSOR_DEVICE_ATTR_2(in10_min, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 10); +static SENSOR_DEVICE_ATTR_2(in11_min, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 11); + +/* Input voltages max */ +static SENSOR_DEVICE_ATTR_2(in0_max, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 0); +static SENSOR_DEVICE_ATTR_2(in1_max, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 1); +static SENSOR_DEVICE_ATTR_2(in2_max, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 2); +static SENSOR_DEVICE_ATTR_2(in3_max, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 3); +static SENSOR_DEVICE_ATTR_2(in4_max, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 4); +static SENSOR_DEVICE_ATTR_2(in5_max, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 5); +static SENSOR_DEVICE_ATTR_2(in6_max, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 6); +static SENSOR_DEVICE_ATTR_2(in7_max, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 7); +static SENSOR_DEVICE_ATTR_2(in8_max, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 8); +static SENSOR_DEVICE_ATTR_2(in9_max, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 9); +static SENSOR_DEVICE_ATTR_2(in10_max, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 10); +static SENSOR_DEVICE_ATTR_2(in11_max, S_IWUSR | S_IRUGO, max16065_show_limit, + max16065_set_limit, 0, 11); + +/* alarms */ +static SENSOR_DEVICE_ATTR_2(in0_alarm, S_IRUGO, max16065_show_alarm, NULL, + 0, 0); +static SENSOR_DEVICE_ATTR_2(in1_alarm, S_IRUGO, max16065_show_alarm, NULL, + 0, 1); +static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, max16065_show_alarm, NULL, + 0, 2); +static SENSOR_DEVICE_ATTR_2(in3_alarm, S_IRUGO, max16065_show_alarm, NULL, + 0, 3); +static SENSOR_DEVICE_ATTR_2(in4_alarm, S_IRUGO, max16065_show_alarm, NULL, + 0, 4); +static SENSOR_DEVICE_ATTR_2(in5_alarm, S_IRUGO, max16065_show_alarm, NULL, + 0, 5); +static SENSOR_DEVICE_ATTR_2(in6_alarm, S_IRUGO, max16065_show_alarm, NULL, + 0, 6); +static SENSOR_DEVICE_ATTR_2(in7_alarm, S_IRUGO, max16065_show_alarm, NULL, + 0, 7); +static SENSOR_DEVICE_ATTR_2(in8_alarm, S_IRUGO, max16065_show_alarm, NULL, + 1, 0); +static SENSOR_DEVICE_ATTR_2(in9_alarm, S_IRUGO, max16065_show_alarm, NULL, + 1, 1); +static SENSOR_DEVICE_ATTR_2(in10_alarm, S_IRUGO, max16065_show_alarm, NULL, + 1, 2); +static SENSOR_DEVICE_ATTR_2(in11_alarm, S_IRUGO, max16065_show_alarm, NULL, + 1, 3); + +/* Current and alarm */ +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, max16065_show_current, NULL, 0); +static SENSOR_DEVICE_ATTR_2(curr1_alarm, S_IRUGO, max16065_show_alarm, NULL, + 1, 4); + +/* + * Finally, construct an array of pointers to members of the above objects, + * as required for sysfs_create_group() + */ +static struct attribute *max16065_basic_attributes[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_lcrit.dev_attr.attr, + &sensor_dev_attr_in0_crit.dev_attr.attr, + &sensor_dev_attr_in0_alarm.dev_attr.attr, + + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_lcrit.dev_attr.attr, + &sensor_dev_attr_in1_crit.dev_attr.attr, + &sensor_dev_attr_in1_alarm.dev_attr.attr, + + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_lcrit.dev_attr.attr, + &sensor_dev_attr_in2_crit.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_lcrit.dev_attr.attr, + &sensor_dev_attr_in3_crit.dev_attr.attr, + &sensor_dev_attr_in3_alarm.dev_attr.attr, + + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_lcrit.dev_attr.attr, + &sensor_dev_attr_in4_crit.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in5_lcrit.dev_attr.attr, + &sensor_dev_attr_in5_crit.dev_attr.attr, + &sensor_dev_attr_in5_alarm.dev_attr.attr, + + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in6_lcrit.dev_attr.attr, + &sensor_dev_attr_in6_crit.dev_attr.attr, + &sensor_dev_attr_in6_alarm.dev_attr.attr, + + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in7_lcrit.dev_attr.attr, + &sensor_dev_attr_in7_crit.dev_attr.attr, + &sensor_dev_attr_in7_alarm.dev_attr.attr, + + &sensor_dev_attr_in8_input.dev_attr.attr, + &sensor_dev_attr_in8_lcrit.dev_attr.attr, + &sensor_dev_attr_in8_crit.dev_attr.attr, + &sensor_dev_attr_in8_alarm.dev_attr.attr, + + &sensor_dev_attr_in9_input.dev_attr.attr, + &sensor_dev_attr_in9_lcrit.dev_attr.attr, + &sensor_dev_attr_in9_crit.dev_attr.attr, + &sensor_dev_attr_in9_alarm.dev_attr.attr, + + &sensor_dev_attr_in10_input.dev_attr.attr, + &sensor_dev_attr_in10_lcrit.dev_attr.attr, + &sensor_dev_attr_in10_crit.dev_attr.attr, + &sensor_dev_attr_in10_alarm.dev_attr.attr, + + &sensor_dev_attr_in11_input.dev_attr.attr, + &sensor_dev_attr_in11_lcrit.dev_attr.attr, + &sensor_dev_attr_in11_crit.dev_attr.attr, + &sensor_dev_attr_in11_alarm.dev_attr.attr, + + NULL +}; + +static struct attribute *max16065_current_attributes[] = { + &sensor_dev_attr_in12_input.dev_attr.attr, + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr1_alarm.dev_attr.attr, + NULL +}; + +static struct attribute *max16065_min_attributes[] = { + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in6_min.dev_attr.attr, + &sensor_dev_attr_in7_min.dev_attr.attr, + &sensor_dev_attr_in8_min.dev_attr.attr, + &sensor_dev_attr_in9_min.dev_attr.attr, + &sensor_dev_attr_in10_min.dev_attr.attr, + &sensor_dev_attr_in11_min.dev_attr.attr, + NULL +}; + +static struct attribute *max16065_max_attributes[] = { + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, + &sensor_dev_attr_in6_max.dev_attr.attr, + &sensor_dev_attr_in7_max.dev_attr.attr, + &sensor_dev_attr_in8_max.dev_attr.attr, + &sensor_dev_attr_in9_max.dev_attr.attr, + &sensor_dev_attr_in10_max.dev_attr.attr, + &sensor_dev_attr_in11_max.dev_attr.attr, + NULL +}; + +static umode_t max16065_basic_is_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct max16065_data *data = dev_get_drvdata(dev); + int index = n / 4; + + if (index >= data->num_adc || !data->range[index]) + return 0; + return a->mode; +} + +static umode_t max16065_secondary_is_visible(struct kobject *kobj, + struct attribute *a, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct max16065_data *data = dev_get_drvdata(dev); + + if (index >= data->num_adc) + return 0; + return a->mode; +} + +static const struct attribute_group max16065_basic_group = { + .attrs = max16065_basic_attributes, + .is_visible = max16065_basic_is_visible, +}; + +static const struct attribute_group max16065_current_group = { + .attrs = max16065_current_attributes, +}; + +static const struct attribute_group max16065_min_group = { + .attrs = max16065_min_attributes, + .is_visible = max16065_secondary_is_visible, +}; + +static const struct attribute_group max16065_max_group = { + .attrs = max16065_max_attributes, + .is_visible = max16065_secondary_is_visible, +}; + +static int max16065_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct max16065_data *data; + struct device *dev = &client->dev; + struct device *hwmon_dev; + int i, j, val; + bool have_secondary; /* true if chip has secondary limits */ + bool secondary_is_max = false; /* secondary limits reflect max */ + int groups = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA + | I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (unlikely(!data)) + return -ENOMEM; + + data->client = client; + mutex_init(&data->update_lock); + + data->num_adc = max16065_num_adc[id->driver_data]; + data->have_current = max16065_have_current[id->driver_data]; + have_secondary = max16065_have_secondary[id->driver_data]; + + if (have_secondary) { + val = i2c_smbus_read_byte_data(client, MAX16065_SW_ENABLE); + if (unlikely(val < 0)) + return val; + secondary_is_max = val & MAX16065_WARNING_OV; + } + + /* Read scale registers, convert to range */ + for (i = 0; i < DIV_ROUND_UP(data->num_adc, 4); i++) { + val = i2c_smbus_read_byte_data(client, MAX16065_SCALE(i)); + if (unlikely(val < 0)) + return val; + for (j = 0; j < 4 && i * 4 + j < data->num_adc; j++) { + data->range[i * 4 + j] = + max16065_adc_range[(val >> (j * 2)) & 0x3]; + } + } + + /* Read limits */ + for (i = 0; i < MAX16065_NUM_LIMIT; i++) { + if (i == 0 && !have_secondary) + continue; + + for (j = 0; j < data->num_adc; j++) { + val = i2c_smbus_read_byte_data(client, + MAX16065_LIMIT(i, j)); + if (unlikely(val < 0)) + return val; + data->limit[i][j] = LIMIT_TO_MV(val, data->range[j]); + } + } + + /* sysfs hooks */ + data->groups[groups++] = &max16065_basic_group; + if (have_secondary) + data->groups[groups++] = secondary_is_max ? + &max16065_max_group : &max16065_min_group; + + if (data->have_current) { + val = i2c_smbus_read_byte_data(client, MAX16065_CURR_CONTROL); + if (unlikely(val < 0)) + return val; + if (val & MAX16065_CURR_ENABLE) { + /* + * Current gain is 6, 12, 24, 48 based on values in + * bit 2,3. + */ + data->curr_gain = 6 << ((val >> 2) & 0x03); + data->range[MAX16065_NUM_ADC] + = max16065_csp_adc_range[(val >> 1) & 0x01]; + data->groups[groups++] = &max16065_current_group; + } else { + data->have_current = false; + } + } + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, data->groups); + if (unlikely(IS_ERR(hwmon_dev))) + return PTR_ERR(hwmon_dev); + + return 0; +} + +static const struct i2c_device_id max16065_id[] = { + { "max16065", max16065 }, + { "max16066", max16066 }, + { "max16067", max16067 }, + { "max16068", max16068 }, + { "max16070", max16070 }, + { "max16071", max16071 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, max16065_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver max16065_driver = { + .driver = { + .name = "max16065", + }, + .probe = max16065_probe, + .id_table = max16065_id, +}; + +module_i2c_driver(max16065_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("MAX16065 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index 022ded09810..eda9cf59968 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -1,8 +1,8 @@ /* * max1619.c - Part of lm_sensors, Linux kernel modules for hardware * monitoring - * Copyright (C) 2003-2004 Alexey Fisher <fishor@mail.ru> - * Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2003-2004 Oleksij Rempel <bug-track@fisher-privat.net> + * Jean Delvare <jdelvare@suse.de> * * Based on the lm90 driver. The MAX1619 is a sensor chip made by Maxim. * It reports up to two temperatures (its own plus up to @@ -19,13 +19,8 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> @@ -76,38 +71,14 @@ static int temp_to_reg(int val) return (val < 0 ? val+0x100*1000 : val) / 1000; } -/* - * Functions declaration - */ - -static int max1619_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int max1619_detect(struct i2c_client *client, - struct i2c_board_info *info); -static void max1619_init_client(struct i2c_client *client); -static int max1619_remove(struct i2c_client *client); -static struct max1619_data *max1619_update_device(struct device *dev); - -/* - * Driver data (common to all clients) - */ - -static const struct i2c_device_id max1619_id[] = { - { "max1619", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, max1619_id); - -static struct i2c_driver max1619_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "max1619", - }, - .probe = max1619_probe, - .remove = max1619_remove, - .id_table = max1619_id, - .detect = max1619_detect, - .address_list = normal_i2c, +enum temp_index { + t_input1 = 0, + t_input2, + t_low2, + t_high2, + t_crit2, + t_hyst2, + t_num_regs }; /* @@ -115,57 +86,95 @@ static struct i2c_driver max1619_driver = { */ struct max1619_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ /* registers values */ - u8 temp_input1; /* local */ - u8 temp_input2, temp_low2, temp_high2; /* remote */ - u8 temp_crit2; - u8 temp_hyst2; - u8 alarms; + u8 temp[t_num_regs]; /* index with enum temp_index */ + u8 alarms; +}; + +static const u8 regs_read[t_num_regs] = { + [t_input1] = MAX1619_REG_R_LOCAL_TEMP, + [t_input2] = MAX1619_REG_R_REMOTE_TEMP, + [t_low2] = MAX1619_REG_R_REMOTE_LOW, + [t_high2] = MAX1619_REG_R_REMOTE_HIGH, + [t_crit2] = MAX1619_REG_R_REMOTE_CRIT, + [t_hyst2] = MAX1619_REG_R_TCRIT_HYST, +}; + +static const u8 regs_write[t_num_regs] = { + [t_low2] = MAX1619_REG_W_REMOTE_LOW, + [t_high2] = MAX1619_REG_W_REMOTE_HIGH, + [t_crit2] = MAX1619_REG_W_REMOTE_CRIT, + [t_hyst2] = MAX1619_REG_W_TCRIT_HYST, }; +static struct max1619_data *max1619_update_device(struct device *dev) +{ + struct max1619_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int config, i; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { + dev_dbg(&client->dev, "Updating max1619 data.\n"); + for (i = 0; i < t_num_regs; i++) + data->temp[i] = i2c_smbus_read_byte_data(client, + regs_read[i]); + data->alarms = i2c_smbus_read_byte_data(client, + MAX1619_REG_R_STATUS); + /* If OVERT polarity is low, reverse alarm bit */ + config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG); + if (!(config & 0x20)) + data->alarms ^= 0x02; + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + /* * Sysfs stuff */ -#define show_temp(value) \ -static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct max1619_data *data = max1619_update_device(dev); \ - return sprintf(buf, "%d\n", temp_from_reg(data->value)); \ -} -show_temp(temp_input1); -show_temp(temp_input2); -show_temp(temp_low2); -show_temp(temp_high2); -show_temp(temp_crit2); -show_temp(temp_hyst2); - -#define set_temp2(value, reg) \ -static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct max1619_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - mutex_lock(&data->update_lock); \ - data->value = temp_to_reg(val); \ - i2c_smbus_write_byte_data(client, reg, data->value); \ - mutex_unlock(&data->update_lock); \ - return count; \ +static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max1619_data *data = max1619_update_device(dev); + + return sprintf(buf, "%d\n", temp_from_reg(data->temp[attr->index])); } -set_temp2(temp_low2, MAX1619_REG_W_REMOTE_LOW); -set_temp2(temp_high2, MAX1619_REG_W_REMOTE_HIGH); -set_temp2(temp_crit2, MAX1619_REG_W_REMOTE_CRIT); -set_temp2(temp_hyst2, MAX1619_REG_W_TCRIT_HYST); +static ssize_t set_temp(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max1619_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + long val; + int err = kstrtol(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + data->temp[attr->index] = temp_to_reg(val); + i2c_smbus_write_byte_data(client, regs_write[attr->index], + data->temp[attr->index]); + mutex_unlock(&data->update_lock); + return count; +} -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, + char *buf) { struct max1619_data *data = max1619_update_device(dev); return sprintf(buf, "%d\n", data->alarms); @@ -179,29 +188,30 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); } -static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL); -static DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input2, NULL); -static DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_low2, - set_temp_low2); -static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_high2, - set_temp_high2); -static DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp_crit2, - set_temp_crit2); -static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp_hyst2, - set_temp_hyst2); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, t_input1); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, t_input2); +static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp, set_temp, + t_low2); +static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp, set_temp, + t_high2); +static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp, set_temp, + t_crit2); +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp, + set_temp, t_hyst2); + static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1); static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 2); static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3); static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4); -static struct attribute *max1619_attributes[] = { - &dev_attr_temp1_input.attr, - &dev_attr_temp2_input.attr, - &dev_attr_temp2_min.attr, - &dev_attr_temp2_max.attr, - &dev_attr_temp2_crit.attr, - &dev_attr_temp2_crit_hyst.attr, +static struct attribute *max1619_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, &dev_attr_alarms.attr, &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, @@ -210,14 +220,7 @@ static struct attribute *max1619_attributes[] = { &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, NULL }; - -static const struct attribute_group max1619_group = { - .attrs = max1619_attributes, -}; - -/* - * Real code - */ +ATTRIBUTE_GROUPS(max1619); /* Return 0 if detection is successful, -ENODEV otherwise */ static int max1619_detect(struct i2c_client *client, @@ -255,45 +258,6 @@ static int max1619_detect(struct i2c_client *client, return 0; } -static int max1619_probe(struct i2c_client *new_client, - const struct i2c_device_id *id) -{ - struct max1619_data *data; - int err; - - data = kzalloc(sizeof(struct max1619_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } - - i2c_set_clientdata(new_client, data); - data->valid = 0; - mutex_init(&data->update_lock); - - /* Initialize the MAX1619 chip */ - max1619_init_client(new_client); - - /* Register sysfs hooks */ - if ((err = sysfs_create_group(&new_client->dev.kobj, &max1619_group))) - goto exit_free; - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - -exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &max1619_group); -exit_free: - kfree(data); -exit: - return err; -} - static void max1619_init_client(struct i2c_client *client) { u8 config; @@ -309,64 +273,49 @@ static void max1619_init_client(struct i2c_client *client) config & 0xBF); /* run */ } -static int max1619_remove(struct i2c_client *client) -{ - struct max1619_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &max1619_group); - - kfree(data); - return 0; -} - -static struct max1619_data *max1619_update_device(struct device *dev) +static int max1619_probe(struct i2c_client *new_client, + const struct i2c_device_id *id) { - struct i2c_client *client = to_i2c_client(dev); - struct max1619_data *data = i2c_get_clientdata(client); - - mutex_lock(&data->update_lock); + struct max1619_data *data; + struct device *hwmon_dev; - if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { - dev_dbg(&client->dev, "Updating max1619 data.\n"); - data->temp_input1 = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_LOCAL_TEMP); - data->temp_input2 = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_REMOTE_TEMP); - data->temp_high2 = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_REMOTE_HIGH); - data->temp_low2 = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_REMOTE_LOW); - data->temp_crit2 = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_REMOTE_CRIT); - data->temp_hyst2 = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_TCRIT_HYST); - data->alarms = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_STATUS); + data = devm_kzalloc(&new_client->dev, sizeof(struct max1619_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; - data->last_updated = jiffies; - data->valid = 1; - } + data->client = new_client; + mutex_init(&data->update_lock); - mutex_unlock(&data->update_lock); + /* Initialize the MAX1619 chip */ + max1619_init_client(new_client); - return data; + hwmon_dev = devm_hwmon_device_register_with_groups(&new_client->dev, + new_client->name, + data, + max1619_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } -static int __init sensors_max1619_init(void) -{ - return i2c_add_driver(&max1619_driver); -} +static const struct i2c_device_id max1619_id[] = { + { "max1619", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max1619_id); -static void __exit sensors_max1619_exit(void) -{ - i2c_del_driver(&max1619_driver); -} +static struct i2c_driver max1619_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "max1619", + }, + .probe = max1619_probe, + .id_table = max1619_id, + .detect = max1619_detect, + .address_list = normal_i2c, +}; -MODULE_AUTHOR("Alexey Fisher <fishor@mail.ru> and " - "Jean Delvare <khali@linux-fr.org>"); +module_i2c_driver(max1619_driver); + +MODULE_AUTHOR("Oleksij Rempel <bug-track@fisher-privat.net>, Jean Delvare <jdelvare@suse.de>"); MODULE_DESCRIPTION("MAX1619 sensor driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_max1619_init); -module_exit(sensors_max1619_exit); diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c new file mode 100644 index 00000000000..e3ed0a5b6d9 --- /dev/null +++ b/drivers/hwmon/max1668.c @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2011 David George <david.george@ska.ac.za> + * + * based on adm1021.c + * some credit to Christoph Scheurer, but largely a rewrite + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> + +/* Addresses to scan */ +static unsigned short max1668_addr_list[] = { + 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; + +/* max1668 registers */ + +#define MAX1668_REG_TEMP(nr) (nr) +#define MAX1668_REG_STAT1 0x05 +#define MAX1668_REG_STAT2 0x06 +#define MAX1668_REG_MAN_ID 0xfe +#define MAX1668_REG_DEV_ID 0xff + +/* limits */ + +/* write high limits */ +#define MAX1668_REG_LIMH_WR(nr) (0x13 + 2 * (nr)) +/* write low limits */ +#define MAX1668_REG_LIML_WR(nr) (0x14 + 2 * (nr)) +/* read high limits */ +#define MAX1668_REG_LIMH_RD(nr) (0x08 + 2 * (nr)) +/* read low limits */ +#define MAX1668_REG_LIML_RD(nr) (0x09 + 2 * (nr)) + +/* manufacturer and device ID Constants */ +#define MAN_ID_MAXIM 0x4d +#define DEV_ID_MAX1668 0x3 +#define DEV_ID_MAX1805 0x5 +#define DEV_ID_MAX1989 0xb + +/* read only mode module parameter */ +static bool read_only; +module_param(read_only, bool, 0); +MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); + +enum chips { max1668, max1805, max1989 }; + +struct max1668_data { + struct i2c_client *client; + const struct attribute_group *groups[3]; + enum chips type; + + struct mutex update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + /* 1x local and 4x remote */ + s8 temp_max[5]; + s8 temp_min[5]; + s8 temp[5]; + u16 alarms; +}; + +static struct max1668_data *max1668_update_device(struct device *dev) +{ + struct max1668_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + struct max1668_data *ret = data; + s32 val; + int i; + + mutex_lock(&data->update_lock); + + if (data->valid && !time_after(jiffies, + data->last_updated + HZ + HZ / 2)) + goto abort; + + for (i = 0; i < 5; i++) { + val = i2c_smbus_read_byte_data(client, MAX1668_REG_TEMP(i)); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp[i] = (s8) val; + + val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIMH_RD(i)); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp_max[i] = (s8) val; + + val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIML_RD(i)); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp_min[i] = (s8) val; + } + + val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT1); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->alarms = val << 8; + + val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT2); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->alarms |= val; + + data->last_updated = jiffies; + data->valid = 1; +abort: + mutex_unlock(&data->update_lock); + + return ret; +} + +static ssize_t show_temp(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct max1668_data *data = max1668_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", data->temp[index] * 1000); +} + +static ssize_t show_temp_max(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct max1668_data *data = max1668_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", data->temp_max[index] * 1000); +} + +static ssize_t show_temp_min(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct max1668_data *data = max1668_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", data->temp_min[index] * 1000); +} + +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int index = to_sensor_dev_attr(attr)->index; + struct max1668_data *data = max1668_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%u\n", (data->alarms >> index) & 0x1); +} + +static ssize_t show_fault(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct max1668_data *data = max1668_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%u\n", + (data->alarms & (1 << 12)) && data->temp[index] == 127); +} + +static ssize_t set_temp_max(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct max1668_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + long temp; + int ret; + + ret = kstrtol(buf, 10, &temp); + if (ret < 0) + return ret; + + mutex_lock(&data->update_lock); + data->temp_max[index] = clamp_val(temp/1000, -128, 127); + ret = i2c_smbus_write_byte_data(client, + MAX1668_REG_LIMH_WR(index), + data->temp_max[index]); + if (ret < 0) + count = ret; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t set_temp_min(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct max1668_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + long temp; + int ret; + + ret = kstrtol(buf, 10, &temp); + if (ret < 0) + return ret; + + mutex_lock(&data->update_lock); + data->temp_min[index] = clamp_val(temp/1000, -128, 127); + ret = i2c_smbus_write_byte_data(client, + MAX1668_REG_LIML_WR(index), + data->temp_min[index]); + if (ret < 0) + count = ret; + mutex_unlock(&data->update_lock); + + return count; +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, + set_temp_max, 0); +static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp_min, + set_temp_min, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp_max, + set_temp_max, 1); +static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO, show_temp_min, + set_temp_min, 1); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp_max, + set_temp_max, 2); +static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO, show_temp_min, + set_temp_min, 2); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO, show_temp_max, + set_temp_max, 3); +static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO, show_temp_min, + set_temp_min, 3); +static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp5_max, S_IRUGO, show_temp_max, + set_temp_max, 4); +static SENSOR_DEVICE_ATTR(temp5_min, S_IRUGO, show_temp_min, + set_temp_min, 4); + +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 14); +static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 13); +static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 7); +static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(temp5_min_alarm, S_IRUGO, show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_alarm, NULL, 0); + +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_fault, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_fault, NULL, 4); + +/* Attributes common to MAX1668, MAX1989 and MAX1805 */ +static struct attribute *max1668_attribute_common[] = { + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, + + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, + NULL +}; + +/* Attributes not present on MAX1805 */ +static struct attribute *max1668_attribute_unique[] = { + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp4_min.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + &sensor_dev_attr_temp5_min.dev_attr.attr, + &sensor_dev_attr_temp5_input.dev_attr.attr, + + &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_min_alarm.dev_attr.attr, + + &sensor_dev_attr_temp4_fault.dev_attr.attr, + &sensor_dev_attr_temp5_fault.dev_attr.attr, + NULL +}; + +static umode_t max1668_attribute_mode(struct kobject *kobj, + struct attribute *attr, int index) +{ + umode_t ret = S_IRUGO; + if (read_only) + return ret; + if (attr == &sensor_dev_attr_temp1_max.dev_attr.attr || + attr == &sensor_dev_attr_temp2_max.dev_attr.attr || + attr == &sensor_dev_attr_temp3_max.dev_attr.attr || + attr == &sensor_dev_attr_temp4_max.dev_attr.attr || + attr == &sensor_dev_attr_temp5_max.dev_attr.attr || + attr == &sensor_dev_attr_temp1_min.dev_attr.attr || + attr == &sensor_dev_attr_temp2_min.dev_attr.attr || + attr == &sensor_dev_attr_temp3_min.dev_attr.attr || + attr == &sensor_dev_attr_temp4_min.dev_attr.attr || + attr == &sensor_dev_attr_temp5_min.dev_attr.attr) + ret |= S_IWUSR; + return ret; +} + +static const struct attribute_group max1668_group_common = { + .attrs = max1668_attribute_common, + .is_visible = max1668_attribute_mode +}; + +static const struct attribute_group max1668_group_unique = { + .attrs = max1668_attribute_unique, + .is_visible = max1668_attribute_mode +}; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int max1668_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + const char *type_name; + int man_id, dev_id; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + /* Check for unsupported part */ + man_id = i2c_smbus_read_byte_data(client, MAX1668_REG_MAN_ID); + if (man_id != MAN_ID_MAXIM) + return -ENODEV; + + dev_id = i2c_smbus_read_byte_data(client, MAX1668_REG_DEV_ID); + if (dev_id < 0) + return -ENODEV; + + type_name = NULL; + if (dev_id == DEV_ID_MAX1668) + type_name = "max1668"; + else if (dev_id == DEV_ID_MAX1805) + type_name = "max1805"; + else if (dev_id == DEV_ID_MAX1989) + type_name = "max1989"; + + if (!type_name) + return -ENODEV; + + strlcpy(info->type, type_name, I2C_NAME_SIZE); + + return 0; +} + +static int max1668_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct max1668_data *data; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(struct max1668_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + data->type = id->driver_data; + mutex_init(&data->update_lock); + + /* sysfs hooks */ + data->groups[0] = &max1668_group_common; + if (data->type == max1668 || data->type == max1989) + data->groups[1] = &max1668_group_unique; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, data->groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id max1668_id[] = { + { "max1668", max1668 }, + { "max1805", max1805 }, + { "max1989", max1989 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max1668_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver max1668_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "max1668", + }, + .probe = max1668_probe, + .id_table = max1668_id, + .detect = max1668_detect, + .address_list = max1668_addr_list, +}; + +module_i2c_driver(max1668_driver); + +MODULE_AUTHOR("David George <david.george@ska.ac.za>"); +MODULE_DESCRIPTION("MAX1668 remote temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/max197.c b/drivers/hwmon/max197.c new file mode 100644 index 00000000000..82128ad79a9 --- /dev/null +++ b/drivers/hwmon/max197.c @@ -0,0 +1,347 @@ +/* + * Maxim MAX197 A/D Converter driver + * + * Copyright (c) 2012 Savoir-faire Linux Inc. + * Vivien Didelot <vivien.didelot@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * For further information, see the Documentation/hwmon/max197 file. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/sysfs.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/platform_device.h> +#include <linux/platform_data/max197.h> + +#define MAX199_LIMIT 4000 /* 4V */ +#define MAX197_LIMIT 10000 /* 10V */ + +#define MAX197_NUM_CH 8 /* 8 Analog Input Channels */ + +/* Control byte format */ +#define MAX197_BIP (1 << 3) /* Bipolarity */ +#define MAX197_RNG (1 << 4) /* Full range */ + +#define MAX197_SCALE 12207 /* Scale coefficient for raw data */ + +/* List of supported chips */ +enum max197_chips { max197, max199 }; + +/** + * struct max197_data - device instance specific data + * @pdata: Platform data. + * @hwmon_dev: The hwmon device. + * @lock: Read/Write mutex. + * @limit: Max range value (10V for MAX197, 4V for MAX199). + * @scale: Need to scale. + * @ctrl_bytes: Channels control byte. + */ +struct max197_data { + struct max197_platform_data *pdata; + struct device *hwmon_dev; + struct mutex lock; + int limit; + bool scale; + u8 ctrl_bytes[MAX197_NUM_CH]; +}; + +static inline void max197_set_unipolarity(struct max197_data *data, int channel) +{ + data->ctrl_bytes[channel] &= ~MAX197_BIP; +} + +static inline void max197_set_bipolarity(struct max197_data *data, int channel) +{ + data->ctrl_bytes[channel] |= MAX197_BIP; +} + +static inline void max197_set_half_range(struct max197_data *data, int channel) +{ + data->ctrl_bytes[channel] &= ~MAX197_RNG; +} + +static inline void max197_set_full_range(struct max197_data *data, int channel) +{ + data->ctrl_bytes[channel] |= MAX197_RNG; +} + +static inline bool max197_is_bipolar(struct max197_data *data, int channel) +{ + return data->ctrl_bytes[channel] & MAX197_BIP; +} + +static inline bool max197_is_full_range(struct max197_data *data, int channel) +{ + return data->ctrl_bytes[channel] & MAX197_RNG; +} + +/* Function called on read access on in{0,1,2,3,4,5,6,7}_{min,max} */ +static ssize_t max197_show_range(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct max197_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + int channel = attr->index; + bool is_min = attr->nr; + int range; + + if (mutex_lock_interruptible(&data->lock)) + return -ERESTARTSYS; + + range = max197_is_full_range(data, channel) ? + data->limit : data->limit / 2; + if (is_min) { + if (max197_is_bipolar(data, channel)) + range = -range; + else + range = 0; + } + + mutex_unlock(&data->lock); + + return sprintf(buf, "%d\n", range); +} + +/* Function called on write access on in{0,1,2,3,4,5,6,7}_{min,max} */ +static ssize_t max197_store_range(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct max197_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + int channel = attr->index; + bool is_min = attr->nr; + long value; + int half = data->limit / 2; + int full = data->limit; + + if (kstrtol(buf, 10, &value)) + return -EINVAL; + + if (is_min) { + if (value <= -full) + value = -full; + else if (value < 0) + value = -half; + else + value = 0; + } else { + if (value >= full) + value = full; + else + value = half; + } + + if (mutex_lock_interruptible(&data->lock)) + return -ERESTARTSYS; + + if (value == 0) { + /* We can deduce only the polarity */ + max197_set_unipolarity(data, channel); + } else if (value == -half) { + max197_set_bipolarity(data, channel); + max197_set_half_range(data, channel); + } else if (value == -full) { + max197_set_bipolarity(data, channel); + max197_set_full_range(data, channel); + } else if (value == half) { + /* We can deduce only the range */ + max197_set_half_range(data, channel); + } else if (value == full) { + /* We can deduce only the range */ + max197_set_full_range(data, channel); + } + + mutex_unlock(&data->lock); + + return count; +} + +/* Function called on read access on in{0,1,2,3,4,5,6,7}_input */ +static ssize_t max197_show_input(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct max197_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int channel = attr->index; + s32 value; + int ret; + + if (mutex_lock_interruptible(&data->lock)) + return -ERESTARTSYS; + + ret = data->pdata->convert(data->ctrl_bytes[channel]); + if (ret < 0) { + dev_err(dev, "conversion failed\n"); + goto unlock; + } + value = ret; + + /* + * Coefficient to apply on raw value. + * See Table 1. Full Scale and Zero Scale in the MAX197 datasheet. + */ + if (data->scale) { + value *= MAX197_SCALE; + if (max197_is_full_range(data, channel)) + value *= 2; + value /= 10000; + } + + ret = sprintf(buf, "%d\n", value); + +unlock: + mutex_unlock(&data->lock); + return ret; +} + +static ssize_t max197_show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + return sprintf(buf, "%s\n", pdev->name); +} + +#define MAX197_SENSOR_DEVICE_ATTR_CH(chan) \ + static SENSOR_DEVICE_ATTR(in##chan##_input, S_IRUGO, \ + max197_show_input, NULL, chan); \ + static SENSOR_DEVICE_ATTR_2(in##chan##_min, S_IRUGO | S_IWUSR, \ + max197_show_range, \ + max197_store_range, \ + true, chan); \ + static SENSOR_DEVICE_ATTR_2(in##chan##_max, S_IRUGO | S_IWUSR, \ + max197_show_range, \ + max197_store_range, \ + false, chan) + +#define MAX197_SENSOR_DEV_ATTR_IN(chan) \ + &sensor_dev_attr_in##chan##_input.dev_attr.attr, \ + &sensor_dev_attr_in##chan##_max.dev_attr.attr, \ + &sensor_dev_attr_in##chan##_min.dev_attr.attr + +static DEVICE_ATTR(name, S_IRUGO, max197_show_name, NULL); + +MAX197_SENSOR_DEVICE_ATTR_CH(0); +MAX197_SENSOR_DEVICE_ATTR_CH(1); +MAX197_SENSOR_DEVICE_ATTR_CH(2); +MAX197_SENSOR_DEVICE_ATTR_CH(3); +MAX197_SENSOR_DEVICE_ATTR_CH(4); +MAX197_SENSOR_DEVICE_ATTR_CH(5); +MAX197_SENSOR_DEVICE_ATTR_CH(6); +MAX197_SENSOR_DEVICE_ATTR_CH(7); + +static const struct attribute_group max197_sysfs_group = { + .attrs = (struct attribute *[]) { + &dev_attr_name.attr, + MAX197_SENSOR_DEV_ATTR_IN(0), + MAX197_SENSOR_DEV_ATTR_IN(1), + MAX197_SENSOR_DEV_ATTR_IN(2), + MAX197_SENSOR_DEV_ATTR_IN(3), + MAX197_SENSOR_DEV_ATTR_IN(4), + MAX197_SENSOR_DEV_ATTR_IN(5), + MAX197_SENSOR_DEV_ATTR_IN(6), + MAX197_SENSOR_DEV_ATTR_IN(7), + NULL + }, +}; + +static int max197_probe(struct platform_device *pdev) +{ + int ch, ret; + struct max197_data *data; + struct max197_platform_data *pdata = dev_get_platdata(&pdev->dev); + enum max197_chips chip = platform_get_device_id(pdev)->driver_data; + + if (pdata == NULL) { + dev_err(&pdev->dev, "no platform data supplied\n"); + return -EINVAL; + } + + if (pdata->convert == NULL) { + dev_err(&pdev->dev, "no convert function supplied\n"); + return -EINVAL; + } + + data = devm_kzalloc(&pdev->dev, sizeof(struct max197_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->pdata = pdata; + mutex_init(&data->lock); + + if (chip == max197) { + data->limit = MAX197_LIMIT; + data->scale = true; + } else { + data->limit = MAX199_LIMIT; + data->scale = false; + } + + for (ch = 0; ch < MAX197_NUM_CH; ch++) + data->ctrl_bytes[ch] = (u8) ch; + + platform_set_drvdata(pdev, data); + + ret = sysfs_create_group(&pdev->dev.kobj, &max197_sysfs_group); + if (ret) { + dev_err(&pdev->dev, "sysfs create group failed\n"); + return ret; + } + + data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + dev_err(&pdev->dev, "hwmon device register failed\n"); + goto error; + } + + return 0; + +error: + sysfs_remove_group(&pdev->dev.kobj, &max197_sysfs_group); + return ret; +} + +static int max197_remove(struct platform_device *pdev) +{ + struct max197_data *data = platform_get_drvdata(pdev); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&pdev->dev.kobj, &max197_sysfs_group); + + return 0; +} + +static struct platform_device_id max197_device_ids[] = { + { "max197", max197 }, + { "max199", max199 }, + { } +}; +MODULE_DEVICE_TABLE(platform, max197_device_ids); + +static struct platform_driver max197_driver = { + .driver = { + .name = "max197", + .owner = THIS_MODULE, + }, + .probe = max197_probe, + .remove = max197_remove, + .id_table = max197_device_ids, +}; +module_platform_driver(max197_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>"); +MODULE_DESCRIPTION("Maxim MAX197 A/D Converter driver"); diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c new file mode 100644 index 00000000000..70650de2cbd --- /dev/null +++ b/drivers/hwmon/max6639.c @@ -0,0 +1,613 @@ +/* + * max6639.c - Support for Maxim MAX6639 + * + * 2-Channel Temperature Monitor with Dual PWM Fan-Speed Controller + * + * Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de> + * + * based on the initial MAX6639 support from semptian.net + * by He Changqing <hechangqing@semptian.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/i2c/max6639.h> + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2c, 0x2e, 0x2f, I2C_CLIENT_END }; + +/* The MAX6639 registers, valid channel numbers: 0, 1 */ +#define MAX6639_REG_TEMP(ch) (0x00 + (ch)) +#define MAX6639_REG_STATUS 0x02 +#define MAX6639_REG_OUTPUT_MASK 0x03 +#define MAX6639_REG_GCONFIG 0x04 +#define MAX6639_REG_TEMP_EXT(ch) (0x05 + (ch)) +#define MAX6639_REG_ALERT_LIMIT(ch) (0x08 + (ch)) +#define MAX6639_REG_OT_LIMIT(ch) (0x0A + (ch)) +#define MAX6639_REG_THERM_LIMIT(ch) (0x0C + (ch)) +#define MAX6639_REG_FAN_CONFIG1(ch) (0x10 + (ch) * 4) +#define MAX6639_REG_FAN_CONFIG2a(ch) (0x11 + (ch) * 4) +#define MAX6639_REG_FAN_CONFIG2b(ch) (0x12 + (ch) * 4) +#define MAX6639_REG_FAN_CONFIG3(ch) (0x13 + (ch) * 4) +#define MAX6639_REG_FAN_CNT(ch) (0x20 + (ch)) +#define MAX6639_REG_TARGET_CNT(ch) (0x22 + (ch)) +#define MAX6639_REG_FAN_PPR(ch) (0x24 + (ch)) +#define MAX6639_REG_TARGTDUTY(ch) (0x26 + (ch)) +#define MAX6639_REG_FAN_START_TEMP(ch) (0x28 + (ch)) +#define MAX6639_REG_DEVID 0x3D +#define MAX6639_REG_MANUID 0x3E +#define MAX6639_REG_DEVREV 0x3F + +/* Register bits */ +#define MAX6639_GCONFIG_STANDBY 0x80 +#define MAX6639_GCONFIG_POR 0x40 +#define MAX6639_GCONFIG_DISABLE_TIMEOUT 0x20 +#define MAX6639_GCONFIG_CH2_LOCAL 0x10 +#define MAX6639_GCONFIG_PWM_FREQ_HI 0x08 + +#define MAX6639_FAN_CONFIG1_PWM 0x80 + +#define MAX6639_FAN_CONFIG3_THERM_FULL_SPEED 0x40 + +static const int rpm_ranges[] = { 2000, 4000, 8000, 16000 }; + +#define FAN_FROM_REG(val, rpm_range) ((val) == 0 || (val) == 255 ? \ + 0 : (rpm_ranges[rpm_range] * 30) / (val)) +#define TEMP_LIMIT_TO_REG(val) clamp_val((val) / 1000, 0, 255) + +/* + * Client data (each client gets its own) + */ +struct max6639_data { + struct i2c_client *client; + struct mutex update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + /* Register values sampled regularly */ + u16 temp[2]; /* Temperature, in 1/8 C, 0..255 C */ + bool temp_fault[2]; /* Detected temperature diode failure */ + u8 fan[2]; /* Register value: TACH count for fans >=30 */ + u8 status; /* Detected channel alarms and fan failures */ + + /* Register values only written to */ + u8 pwm[2]; /* Register value: Duty cycle 0..120 */ + u8 temp_therm[2]; /* THERM Temperature, 0..255 C (->_max) */ + u8 temp_alert[2]; /* ALERT Temperature, 0..255 C (->_crit) */ + u8 temp_ot[2]; /* OT Temperature, 0..255 C (->_emergency) */ + + /* Register values initialized only once */ + u8 ppr; /* Pulses per rotation 0..3 for 1..4 ppr */ + u8 rpm_range; /* Index in above rpm_ranges table */ +}; + +static struct max6639_data *max6639_update_device(struct device *dev) +{ + struct max6639_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + struct max6639_data *ret = data; + int i; + int status_reg; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) { + int res; + + dev_dbg(&client->dev, "Starting max6639 update\n"); + + status_reg = i2c_smbus_read_byte_data(client, + MAX6639_REG_STATUS); + if (status_reg < 0) { + ret = ERR_PTR(status_reg); + goto abort; + } + + data->status = status_reg; + + for (i = 0; i < 2; i++) { + res = i2c_smbus_read_byte_data(client, + MAX6639_REG_FAN_CNT(i)); + if (res < 0) { + ret = ERR_PTR(res); + goto abort; + } + data->fan[i] = res; + + res = i2c_smbus_read_byte_data(client, + MAX6639_REG_TEMP_EXT(i)); + if (res < 0) { + ret = ERR_PTR(res); + goto abort; + } + data->temp[i] = res >> 5; + data->temp_fault[i] = res & 0x01; + + res = i2c_smbus_read_byte_data(client, + MAX6639_REG_TEMP(i)); + if (res < 0) { + ret = ERR_PTR(res); + goto abort; + } + data->temp[i] |= res << 3; + } + + data->last_updated = jiffies; + data->valid = 1; + } +abort: + mutex_unlock(&data->update_lock); + + return ret; +} + +static ssize_t show_temp_input(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + long temp; + struct max6639_data *data = max6639_update_device(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + + if (IS_ERR(data)) + return PTR_ERR(data); + + temp = data->temp[attr->index] * 125; + return sprintf(buf, "%ld\n", temp); +} + +static ssize_t show_temp_fault(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + struct max6639_data *data = max6639_update_device(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", data->temp_fault[attr->index]); +} + +static ssize_t show_temp_max(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", (data->temp_therm[attr->index] * 1000)); +} + +static ssize_t set_temp_max(struct device *dev, + struct device_attribute *dev_attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + int res; + + res = kstrtoul(buf, 10, &val); + if (res) + return res; + + mutex_lock(&data->update_lock); + data->temp_therm[attr->index] = TEMP_LIMIT_TO_REG(val); + i2c_smbus_write_byte_data(client, + MAX6639_REG_THERM_LIMIT(attr->index), + data->temp_therm[attr->index]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t show_temp_crit(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", (data->temp_alert[attr->index] * 1000)); +} + +static ssize_t set_temp_crit(struct device *dev, + struct device_attribute *dev_attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + int res; + + res = kstrtoul(buf, 10, &val); + if (res) + return res; + + mutex_lock(&data->update_lock); + data->temp_alert[attr->index] = TEMP_LIMIT_TO_REG(val); + i2c_smbus_write_byte_data(client, + MAX6639_REG_ALERT_LIMIT(attr->index), + data->temp_alert[attr->index]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t show_temp_emergency(struct device *dev, + struct device_attribute *dev_attr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", (data->temp_ot[attr->index] * 1000)); +} + +static ssize_t set_temp_emergency(struct device *dev, + struct device_attribute *dev_attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + int res; + + res = kstrtoul(buf, 10, &val); + if (res) + return res; + + mutex_lock(&data->update_lock); + data->temp_ot[attr->index] = TEMP_LIMIT_TO_REG(val); + i2c_smbus_write_byte_data(client, + MAX6639_REG_OT_LIMIT(attr->index), + data->temp_ot[attr->index]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t show_pwm(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", data->pwm[attr->index] * 255 / 120); +} + +static ssize_t set_pwm(struct device *dev, + struct device_attribute *dev_attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + int res; + + res = kstrtoul(buf, 10, &val); + if (res) + return res; + + val = clamp_val(val, 0, 255); + + mutex_lock(&data->update_lock); + data->pwm[attr->index] = (u8)(val * 120 / 255); + i2c_smbus_write_byte_data(client, + MAX6639_REG_TARGTDUTY(attr->index), + data->pwm[attr->index]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t show_fan_input(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + struct max6639_data *data = max6639_update_device(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[attr->index], + data->rpm_range)); +} + +static ssize_t show_alarm(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + struct max6639_data *data = max6639_update_device(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", !!(data->status & (1 << attr->index))); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input, NULL, 1); +static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 0); +static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 1); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp_crit, + set_temp_crit, 0); +static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp_crit, + set_temp_crit, 1); +static SENSOR_DEVICE_ATTR(temp1_emergency, S_IWUSR | S_IRUGO, + show_temp_emergency, set_temp_emergency, 0); +static SENSOR_DEVICE_ATTR(temp2_emergency, S_IWUSR | S_IRUGO, + show_temp_emergency, set_temp_emergency, 1); +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0); +static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1); +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input, NULL, 1); +static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, show_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 7); +static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO, show_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(temp2_emergency_alarm, S_IRUGO, show_alarm, NULL, 4); + + +static struct attribute *max6639_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp1_fault.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp1_emergency.dev_attr.attr, + &sensor_dev_attr_temp2_emergency.dev_attr.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan1_fault.dev_attr.attr, + &sensor_dev_attr_fan2_fault.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_emergency_alarm.dev_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(max6639); + +/* + * returns respective index in rpm_ranges table + * 1 by default on invalid range + */ +static int rpm_range_to_reg(int range) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rpm_ranges); i++) { + if (rpm_ranges[i] == range) + return i; + } + + return 1; /* default: 4000 RPM */ +} + +static int max6639_init_client(struct i2c_client *client, + struct max6639_data *data) +{ + struct max6639_platform_data *max6639_info = + dev_get_platdata(&client->dev); + int i; + int rpm_range = 1; /* default: 4000 RPM */ + int err; + + /* Reset chip to default values, see below for GCONFIG setup */ + err = i2c_smbus_write_byte_data(client, MAX6639_REG_GCONFIG, + MAX6639_GCONFIG_POR); + if (err) + goto exit; + + /* Fans pulse per revolution is 2 by default */ + if (max6639_info && max6639_info->ppr > 0 && + max6639_info->ppr < 5) + data->ppr = max6639_info->ppr; + else + data->ppr = 2; + data->ppr -= 1; + + if (max6639_info) + rpm_range = rpm_range_to_reg(max6639_info->rpm_range); + data->rpm_range = rpm_range; + + for (i = 0; i < 2; i++) { + + /* Set Fan pulse per revolution */ + err = i2c_smbus_write_byte_data(client, + MAX6639_REG_FAN_PPR(i), + data->ppr << 6); + if (err) + goto exit; + + /* Fans config PWM, RPM */ + err = i2c_smbus_write_byte_data(client, + MAX6639_REG_FAN_CONFIG1(i), + MAX6639_FAN_CONFIG1_PWM | rpm_range); + if (err) + goto exit; + + /* Fans PWM polarity high by default */ + if (max6639_info && max6639_info->pwm_polarity == 0) + err = i2c_smbus_write_byte_data(client, + MAX6639_REG_FAN_CONFIG2a(i), 0x00); + else + err = i2c_smbus_write_byte_data(client, + MAX6639_REG_FAN_CONFIG2a(i), 0x02); + if (err) + goto exit; + + /* + * /THERM full speed enable, + * PWM frequency 25kHz, see also GCONFIG below + */ + err = i2c_smbus_write_byte_data(client, + MAX6639_REG_FAN_CONFIG3(i), + MAX6639_FAN_CONFIG3_THERM_FULL_SPEED | 0x03); + if (err) + goto exit; + + /* Max. temp. 80C/90C/100C */ + data->temp_therm[i] = 80; + data->temp_alert[i] = 90; + data->temp_ot[i] = 100; + err = i2c_smbus_write_byte_data(client, + MAX6639_REG_THERM_LIMIT(i), + data->temp_therm[i]); + if (err) + goto exit; + err = i2c_smbus_write_byte_data(client, + MAX6639_REG_ALERT_LIMIT(i), + data->temp_alert[i]); + if (err) + goto exit; + err = i2c_smbus_write_byte_data(client, + MAX6639_REG_OT_LIMIT(i), data->temp_ot[i]); + if (err) + goto exit; + + /* PWM 120/120 (i.e. 100%) */ + data->pwm[i] = 120; + err = i2c_smbus_write_byte_data(client, + MAX6639_REG_TARGTDUTY(i), data->pwm[i]); + if (err) + goto exit; + } + /* Start monitoring */ + err = i2c_smbus_write_byte_data(client, MAX6639_REG_GCONFIG, + MAX6639_GCONFIG_DISABLE_TIMEOUT | MAX6639_GCONFIG_CH2_LOCAL | + MAX6639_GCONFIG_PWM_FREQ_HI); +exit: + return err; +} + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int max6639_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + int dev_id, manu_id; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + /* Actual detection via device and manufacturer ID */ + dev_id = i2c_smbus_read_byte_data(client, MAX6639_REG_DEVID); + manu_id = i2c_smbus_read_byte_data(client, MAX6639_REG_MANUID); + if (dev_id != 0x58 || manu_id != 0x4D) + return -ENODEV; + + strlcpy(info->type, "max6639", I2C_NAME_SIZE); + + return 0; +} + +static int max6639_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct max6639_data *data; + struct device *hwmon_dev; + int err; + + data = devm_kzalloc(dev, sizeof(struct max6639_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + mutex_init(&data->update_lock); + + /* Initialize the max6639 chip */ + err = max6639_init_client(client, data); + if (err < 0) + return err; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + max6639_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +#ifdef CONFIG_PM_SLEEP +static int max6639_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + int data = i2c_smbus_read_byte_data(client, MAX6639_REG_GCONFIG); + if (data < 0) + return data; + + return i2c_smbus_write_byte_data(client, + MAX6639_REG_GCONFIG, data | MAX6639_GCONFIG_STANDBY); +} + +static int max6639_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + int data = i2c_smbus_read_byte_data(client, MAX6639_REG_GCONFIG); + if (data < 0) + return data; + + return i2c_smbus_write_byte_data(client, + MAX6639_REG_GCONFIG, data & ~MAX6639_GCONFIG_STANDBY); +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct i2c_device_id max6639_id[] = { + {"max6639", 0}, + { } +}; + +MODULE_DEVICE_TABLE(i2c, max6639_id); + +static SIMPLE_DEV_PM_OPS(max6639_pm_ops, max6639_suspend, max6639_resume); + +static struct i2c_driver max6639_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "max6639", + .pm = &max6639_pm_ops, + }, + .probe = max6639_probe, + .id_table = max6639_id, + .detect = max6639_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(max6639_driver); + +MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); +MODULE_DESCRIPTION("max6639 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/max6642.c b/drivers/hwmon/max6642.c new file mode 100644 index 00000000000..6520bc51d02 --- /dev/null +++ b/drivers/hwmon/max6642.c @@ -0,0 +1,327 @@ +/* + * Driver for +/-1 degree C, SMBus-Compatible Remote/Local Temperature Sensor + * with Overtemperature Alarm + * + * Copyright (C) 2011 AppearTV AS + * + * Derived from: + * + * Based on the max1619 driver. + * Copyright (C) 2003-2004 Oleksij Rempel <bug-track@fisher-privat.net> + * Jean Delvare <jdelvare@suse.de> + * + * The MAX6642 is a sensor chip made by Maxim. + * It reports up to two temperatures (its own plus up to + * one external one). Complete datasheet can be + * obtained from Maxim's website at: + * http://datasheets.maxim-ic.com/en/ds/MAX6642.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/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/sysfs.h> + +static const unsigned short normal_i2c[] = { + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; + +/* + * The MAX6642 registers + */ + +#define MAX6642_REG_R_MAN_ID 0xFE +#define MAX6642_REG_R_CONFIG 0x03 +#define MAX6642_REG_W_CONFIG 0x09 +#define MAX6642_REG_R_STATUS 0x02 +#define MAX6642_REG_R_LOCAL_TEMP 0x00 +#define MAX6642_REG_R_LOCAL_TEMPL 0x11 +#define MAX6642_REG_R_LOCAL_HIGH 0x05 +#define MAX6642_REG_W_LOCAL_HIGH 0x0B +#define MAX6642_REG_R_REMOTE_TEMP 0x01 +#define MAX6642_REG_R_REMOTE_TEMPL 0x10 +#define MAX6642_REG_R_REMOTE_HIGH 0x07 +#define MAX6642_REG_W_REMOTE_HIGH 0x0D + +/* + * Conversions + */ + +static int temp_from_reg10(int val) +{ + return val * 250; +} + +static int temp_from_reg(int val) +{ + return val * 1000; +} + +static int temp_to_reg(int val) +{ + return val / 1000; +} + +/* + * Client data (each client gets its own) + */ + +struct max6642_data { + struct i2c_client *client; + struct mutex update_lock; + bool valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* registers values */ + u16 temp_input[2]; /* local/remote */ + u16 temp_high[2]; /* local/remote */ + u8 alarms; +}; + +/* + * Real code + */ + +static void max6642_init_client(struct max6642_data *data, + struct i2c_client *client) +{ + u8 config; + + /* + * Start the conversions. + */ + config = i2c_smbus_read_byte_data(client, MAX6642_REG_R_CONFIG); + if (config & 0x40) + i2c_smbus_write_byte_data(client, MAX6642_REG_W_CONFIG, + config & 0xBF); /* run */ + + data->temp_high[0] = i2c_smbus_read_byte_data(client, + MAX6642_REG_R_LOCAL_HIGH); + data->temp_high[1] = i2c_smbus_read_byte_data(client, + MAX6642_REG_R_REMOTE_HIGH); +} + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int max6642_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + u8 reg_config, reg_status, man_id; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + /* identification */ + man_id = i2c_smbus_read_byte_data(client, MAX6642_REG_R_MAN_ID); + if (man_id != 0x4D) + return -ENODEV; + + /* sanity check */ + if (i2c_smbus_read_byte_data(client, 0x04) != 0x4D + || i2c_smbus_read_byte_data(client, 0x06) != 0x4D + || i2c_smbus_read_byte_data(client, 0xff) != 0x4D) + return -ENODEV; + + /* + * We read the config and status register, the 4 lower bits in the + * config register should be zero and bit 5, 3, 1 and 0 should be + * zero in the status register. + */ + reg_config = i2c_smbus_read_byte_data(client, MAX6642_REG_R_CONFIG); + if ((reg_config & 0x0f) != 0x00) + return -ENODEV; + + /* in between, another round of sanity checks */ + if (i2c_smbus_read_byte_data(client, 0x04) != reg_config + || i2c_smbus_read_byte_data(client, 0x06) != reg_config + || i2c_smbus_read_byte_data(client, 0xff) != reg_config) + return -ENODEV; + + reg_status = i2c_smbus_read_byte_data(client, MAX6642_REG_R_STATUS); + if ((reg_status & 0x2b) != 0x00) + return -ENODEV; + + strlcpy(info->type, "max6642", I2C_NAME_SIZE); + + return 0; +} + +static struct max6642_data *max6642_update_device(struct device *dev) +{ + struct max6642_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + u16 val, tmp; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + dev_dbg(dev, "Updating max6642 data.\n"); + val = i2c_smbus_read_byte_data(client, + MAX6642_REG_R_LOCAL_TEMPL); + tmp = (val >> 6) & 3; + val = i2c_smbus_read_byte_data(client, + MAX6642_REG_R_LOCAL_TEMP); + val = (val << 2) | tmp; + data->temp_input[0] = val; + val = i2c_smbus_read_byte_data(client, + MAX6642_REG_R_REMOTE_TEMPL); + tmp = (val >> 6) & 3; + val = i2c_smbus_read_byte_data(client, + MAX6642_REG_R_REMOTE_TEMP); + val = (val << 2) | tmp; + data->temp_input[1] = val; + data->alarms = i2c_smbus_read_byte_data(client, + MAX6642_REG_R_STATUS); + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +/* + * Sysfs stuff + */ + +static ssize_t show_temp_max10(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6642_data *data = max6642_update_device(dev); + + return sprintf(buf, "%d\n", + temp_from_reg10(data->temp_input[attr->index])); +} + +static ssize_t show_temp_max(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr); + struct max6642_data *data = max6642_update_device(dev); + + return sprintf(buf, "%d\n", temp_from_reg(data->temp_high[attr2->nr])); +} + +static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr); + struct max6642_data *data = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + mutex_lock(&data->update_lock); + data->temp_high[attr2->nr] = clamp_val(temp_to_reg(val), 0, 255); + i2c_smbus_write_byte_data(data->client, attr2->index, + data->temp_high[attr2->nr]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int bitnr = to_sensor_dev_attr(attr)->index; + struct max6642_data *data = max6642_update_device(dev); + return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_max10, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_max10, NULL, 1); +static SENSOR_DEVICE_ATTR_2(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 0, MAX6642_REG_W_LOCAL_HIGH); +static SENSOR_DEVICE_ATTR_2(temp2_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 1, MAX6642_REG_W_REMOTE_HIGH); +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4); + +static struct attribute *max6642_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(max6642); + +static int max6642_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct max6642_data *data; + struct device *hwmon_dev; + + data = devm_kzalloc(dev, sizeof(struct max6642_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + mutex_init(&data->update_lock); + + /* Initialize the MAX6642 chip */ + max6642_init_client(data, client); + + hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, + client->name, data, + max6642_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +/* + * Driver data (common to all clients) + */ + +static const struct i2c_device_id max6642_id[] = { + { "max6642", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max6642_id); + +static struct i2c_driver max6642_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "max6642", + }, + .probe = max6642_probe, + .id_table = max6642_id, + .detect = max6642_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(max6642_driver); + +MODULE_AUTHOR("Per Dalen <per.dalen@appeartv.com>"); +MODULE_DESCRIPTION("MAX6642 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index a0160ee5cae..162a520f4bd 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -2,7 +2,7 @@ * max6650.c - Part of lm_sensors, Linux kernel modules for hardware * monitoring. * - * (C) 2007 by Hans J. Koch <hjk@linutronix.de> + * (C) 2007 by Hans J. Koch <hjk@hansjkoch.de> * * based on code written by John Morris <john.morris@spirentcom.com> * Copyright (c) 2003 Spirent Communications @@ -41,13 +41,6 @@ #include <linux/err.h> /* - * Addresses to scan. There are four disjoint possibilities, by pin config. - */ - -static const unsigned short normal_i2c[] = {0x1b, 0x1f, 0x48, 0x4b, - I2C_CLIENT_END}; - -/* * Insmod parameters */ @@ -112,44 +105,15 @@ module_param(clock, int, S_IRUGO); #define DIV_FROM_REG(reg) (1 << (reg & 7)) -static int max6650_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int max6650_detect(struct i2c_client *client, - struct i2c_board_info *info); -static int max6650_init_client(struct i2c_client *client); -static int max6650_remove(struct i2c_client *client); -static struct max6650_data *max6650_update_device(struct device *dev); - -/* - * Driver data (common to all clients) - */ - -static const struct i2c_device_id max6650_id[] = { - { "max6650", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, max6650_id); - -static struct i2c_driver max6650_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "max6650", - }, - .probe = max6650_probe, - .remove = max6650_remove, - .id_table = max6650_id, - .detect = max6650_detect, - .address_list = normal_i2c, -}; - /* * Client data (each client gets its own) */ -struct max6650_data -{ - struct device *hwmon_dev; +struct max6650_data { + struct i2c_client *client; + const struct attribute_group *groups[3]; struct mutex update_lock; + int nr_fans; char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ @@ -162,6 +126,51 @@ struct max6650_data u8 alarm; }; +static const u8 tach_reg[] = { + MAX6650_REG_TACH0, + MAX6650_REG_TACH1, + MAX6650_REG_TACH2, + MAX6650_REG_TACH3, +}; + +static struct max6650_data *max6650_update_device(struct device *dev) +{ + struct max6650_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int i; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + data->speed = i2c_smbus_read_byte_data(client, + MAX6650_REG_SPEED); + data->config = i2c_smbus_read_byte_data(client, + MAX6650_REG_CONFIG); + for (i = 0; i < data->nr_fans; i++) { + data->tach[i] = i2c_smbus_read_byte_data(client, + tach_reg[i]); + } + data->count = i2c_smbus_read_byte_data(client, + MAX6650_REG_COUNT); + data->dac = i2c_smbus_read_byte_data(client, MAX6650_REG_DAC); + + /* + * Alarms are cleared on read in case the condition that + * caused the alarm is removed. Keep the value latched here + * for providing the register through different alarm files. + */ + data->alarm |= i2c_smbus_read_byte_data(client, + MAX6650_REG_ALARM); + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + static ssize_t get_fan(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -170,13 +179,13 @@ static ssize_t get_fan(struct device *dev, struct device_attribute *devattr, int rpm; /* - * Calculation details: - * - * Each tachometer counts over an interval given by the "count" - * register (0.25, 0.5, 1 or 2 seconds). This module assumes - * that the fans produce two pulses per revolution (this seems - * to be the most common). - */ + * Calculation details: + * + * Each tachometer counts over an interval given by the "count" + * register (0.25, 0.5, 1 or 2 seconds). This module assumes + * that the fans produce two pulses per revolution (this seems + * to be the most common). + */ rpm = ((data->tach[attr->index] * 120) / DIV_FROM_REG(data->count)); return sprintf(buf, "%d\n", rpm); @@ -230,12 +239,12 @@ static ssize_t get_target(struct device *dev, struct device_attribute *devattr, int kscale, ktach, rpm; /* - * Use the datasheet equation: - * - * FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)] - * - * then multiply by 60 to give rpm. - */ + * Use the datasheet equation: + * + * FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)] + * + * then multiply by 60 to give rpm. + */ kscale = DIV_FROM_REG(data->config); ktach = data->speed; @@ -246,19 +255,24 @@ static ssize_t get_target(struct device *dev, struct device_attribute *devattr, static ssize_t set_target(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6650_data *data = i2c_get_clientdata(client); - int rpm = simple_strtoul(buf, NULL, 10); + struct max6650_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; int kscale, ktach; + unsigned long rpm; + int err; + + err = kstrtoul(buf, 10, &rpm); + if (err) + return err; - rpm = SENSORS_LIMIT(rpm, FAN_RPM_MIN, FAN_RPM_MAX); + rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX); /* - * Divide the required speed by 60 to get from rpm to rps, then - * use the datasheet equation: - * - * KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1 - */ + * Divide the required speed by 60 to get from rpm to rps, then + * use the datasheet equation: + * + * KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1 + */ mutex_lock(&data->update_lock); @@ -292,8 +306,10 @@ static ssize_t get_pwm(struct device *dev, struct device_attribute *devattr, int pwm; struct max6650_data *data = max6650_update_device(dev); - /* Useful range for dac is 0-180 for 12V fans and 0-76 for 5V fans. - Lower DAC values mean higher speeds. */ + /* + * Useful range for dac is 0-180 for 12V fans and 0-76 for 5V fans. + * Lower DAC values mean higher speeds. + */ if (data->config & MAX6650_CFG_V12) pwm = 255 - (255 * (int)data->dac)/180; else @@ -308,11 +324,16 @@ static ssize_t get_pwm(struct device *dev, struct device_attribute *devattr, static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6650_data *data = i2c_get_clientdata(client); - int pwm = simple_strtoul(buf, NULL, 10); + struct max6650_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long pwm; + int err; + + err = kstrtoul(buf, 10, &pwm); + if (err) + return err; - pwm = SENSORS_LIMIT(pwm, 0, 255); + pwm = clamp_val(pwm, 0, 255); mutex_lock(&data->update_lock); @@ -349,16 +370,18 @@ static ssize_t get_enable(struct device *dev, struct device_attribute *devattr, static ssize_t set_enable(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6650_data *data = i2c_get_clientdata(client); - int mode = simple_strtoul(buf, NULL, 10); + struct max6650_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; int max6650_modes[3] = {0, 3, 2}; + unsigned long mode; + int err; - if ((mode < 0)||(mode > 2)) { - dev_err(&client->dev, - "illegal value for pwm1_enable (%d)\n", mode); + err = kstrtoul(buf, 10, &mode); + if (err) + return err; + + if (mode > 2) return -EINVAL; - } mutex_lock(&data->update_lock); @@ -397,9 +420,14 @@ static ssize_t get_div(struct device *dev, struct device_attribute *devattr, static ssize_t set_div(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6650_data *data = i2c_get_clientdata(client); - int div = simple_strtoul(buf, NULL, 10); + struct max6650_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long div; + int err; + + err = kstrtoul(buf, 10, &div); + if (err) + return err; mutex_lock(&data->update_lock); switch (div) { @@ -417,8 +445,6 @@ static ssize_t set_div(struct device *dev, struct device_attribute *devattr, break; default: mutex_unlock(&data->update_lock); - dev_err(&client->dev, - "illegal value for fan divider (%d)\n", div); return -EINVAL; } @@ -440,7 +466,7 @@ static ssize_t get_alarm(struct device *dev, struct device_attribute *devattr, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct max6650_data *data = max6650_update_device(dev); - struct i2c_client *client = to_i2c_client(dev); + struct i2c_client *client = data->client; int alarm = 0; if (data->alarm & attr->index) { @@ -474,11 +500,12 @@ static SENSOR_DEVICE_ATTR(gpio1_alarm, S_IRUGO, get_alarm, NULL, static SENSOR_DEVICE_ATTR(gpio2_alarm, S_IRUGO, get_alarm, NULL, MAX6650_ALRM_GPIO2); -static mode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a, +static umode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a, int n) { struct device *dev = container_of(kobj, struct device, kobj); - struct i2c_client *client = to_i2c_client(dev); + struct max6650_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; u8 alarm_en = i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM_EN); struct device_attribute *devattr; @@ -501,9 +528,6 @@ static mode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a, static struct attribute *max6650_attrs[] = { &sensor_dev_attr_fan1_input.dev_attr.attr, - &sensor_dev_attr_fan2_input.dev_attr.attr, - &sensor_dev_attr_fan3_input.dev_attr.attr, - &sensor_dev_attr_fan4_input.dev_attr.attr, &dev_attr_fan1_target.attr, &dev_attr_fan1_div.attr, &dev_attr_pwm1_enable.attr, @@ -516,173 +540,104 @@ static struct attribute *max6650_attrs[] = { NULL }; -static struct attribute_group max6650_attr_grp = { +static const struct attribute_group max6650_group = { .attrs = max6650_attrs, .is_visible = max6650_attrs_visible, }; +static struct attribute *max6651_attrs[] = { + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan4_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group max6651_group = { + .attrs = max6651_attrs, +}; + /* * Real code */ -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int max6650_detect(struct i2c_client *client, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = client->adapter; - int address = client->addr; - - dev_dbg(&adapter->dev, "max6650_detect called\n"); - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_dbg(&adapter->dev, "max6650: I2C bus doesn't support " - "byte read mode, skipping.\n"); - return -ENODEV; - } - - if (((i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG) & 0xC0) - ||(i2c_smbus_read_byte_data(client, MAX6650_REG_GPIO_STAT) & 0xE0) - ||(i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM_EN) & 0xE0) - ||(i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM) & 0xE0) - ||(i2c_smbus_read_byte_data(client, MAX6650_REG_COUNT) & 0xFC))) { - dev_dbg(&adapter->dev, - "max6650: detection failed at 0x%02x.\n", address); - return -ENODEV; - } - - dev_info(&adapter->dev, "max6650: chip found at 0x%02x.\n", address); - - strlcpy(info->type, "max6650", I2C_NAME_SIZE); - - return 0; -} - -static int max6650_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct max6650_data *data; - int err; - - if (!(data = kzalloc(sizeof(struct max6650_data), GFP_KERNEL))) { - dev_err(&client->dev, "out of memory.\n"); - return -ENOMEM; - } - - i2c_set_clientdata(client, data); - mutex_init(&data->update_lock); - - /* - * Initialize the max6650 chip - */ - err = max6650_init_client(client); - if (err) - goto err_free; - - err = sysfs_create_group(&client->dev.kobj, &max6650_attr_grp); - if (err) - goto err_free; - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (!IS_ERR(data->hwmon_dev)) - return 0; - - err = PTR_ERR(data->hwmon_dev); - dev_err(&client->dev, "error registering hwmon device.\n"); - sysfs_remove_group(&client->dev.kobj, &max6650_attr_grp); -err_free: - kfree(data); - return err; -} - -static int max6650_remove(struct i2c_client *client) +static int max6650_init_client(struct max6650_data *data, + struct i2c_client *client) { - struct max6650_data *data = i2c_get_clientdata(client); - - sysfs_remove_group(&client->dev.kobj, &max6650_attr_grp); - hwmon_device_unregister(data->hwmon_dev); - kfree(data); - return 0; -} - -static int max6650_init_client(struct i2c_client *client) -{ - struct max6650_data *data = i2c_get_clientdata(client); + struct device *dev = &client->dev; int config; int err = -EIO; config = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG); if (config < 0) { - dev_err(&client->dev, "Error reading config, aborting.\n"); + dev_err(dev, "Error reading config, aborting.\n"); return err; } switch (fan_voltage) { - case 0: - break; - case 5: - config &= ~MAX6650_CFG_V12; - break; - case 12: - config |= MAX6650_CFG_V12; - break; - default: - dev_err(&client->dev, - "illegal value for fan_voltage (%d)\n", - fan_voltage); + case 0: + break; + case 5: + config &= ~MAX6650_CFG_V12; + break; + case 12: + config |= MAX6650_CFG_V12; + break; + default: + dev_err(dev, "illegal value for fan_voltage (%d)\n", + fan_voltage); } - dev_info(&client->dev, "Fan voltage is set to %dV.\n", + dev_info(dev, "Fan voltage is set to %dV.\n", (config & MAX6650_CFG_V12) ? 12 : 5); switch (prescaler) { - case 0: - break; - case 1: - config &= ~MAX6650_CFG_PRESCALER_MASK; - break; - case 2: - config = (config & ~MAX6650_CFG_PRESCALER_MASK) - | MAX6650_CFG_PRESCALER_2; - break; - case 4: - config = (config & ~MAX6650_CFG_PRESCALER_MASK) - | MAX6650_CFG_PRESCALER_4; - break; - case 8: - config = (config & ~MAX6650_CFG_PRESCALER_MASK) - | MAX6650_CFG_PRESCALER_8; - break; - case 16: - config = (config & ~MAX6650_CFG_PRESCALER_MASK) - | MAX6650_CFG_PRESCALER_16; - break; - default: - dev_err(&client->dev, - "illegal value for prescaler (%d)\n", - prescaler); + case 0: + break; + case 1: + config &= ~MAX6650_CFG_PRESCALER_MASK; + break; + case 2: + config = (config & ~MAX6650_CFG_PRESCALER_MASK) + | MAX6650_CFG_PRESCALER_2; + break; + case 4: + config = (config & ~MAX6650_CFG_PRESCALER_MASK) + | MAX6650_CFG_PRESCALER_4; + break; + case 8: + config = (config & ~MAX6650_CFG_PRESCALER_MASK) + | MAX6650_CFG_PRESCALER_8; + break; + case 16: + config = (config & ~MAX6650_CFG_PRESCALER_MASK) + | MAX6650_CFG_PRESCALER_16; + break; + default: + dev_err(dev, "illegal value for prescaler (%d)\n", prescaler); } - dev_info(&client->dev, "Prescaler is set to %d.\n", + dev_info(dev, "Prescaler is set to %d.\n", 1 << (config & MAX6650_CFG_PRESCALER_MASK)); - /* If mode is set to "full off", we change it to "open loop" and + /* + * If mode is set to "full off", we change it to "open loop" and * set DAC to 255, which has the same effect. We do this because - * there's no "full off" mode defined in hwmon specifcations. + * there's no "full off" mode defined in hwmon specifications. */ if ((config & MAX6650_CFG_MODE_MASK) == MAX6650_CFG_MODE_OFF) { - dev_dbg(&client->dev, "Change mode to open loop, full off.\n"); + dev_dbg(dev, "Change mode to open loop, full off.\n"); config = (config & ~MAX6650_CFG_MODE_MASK) | MAX6650_CFG_MODE_OPEN_LOOP; if (i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, 255)) { - dev_err(&client->dev, "DAC write error, aborting.\n"); + dev_err(dev, "DAC write error, aborting.\n"); return err; } } if (i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, config)) { - dev_err(&client->dev, "Config write error, aborting.\n"); + dev_err(dev, "Config write error, aborting.\n"); return err; } @@ -692,62 +647,57 @@ static int max6650_init_client(struct i2c_client *client) return 0; } -static const u8 tach_reg[] = { - MAX6650_REG_TACH0, - MAX6650_REG_TACH1, - MAX6650_REG_TACH2, - MAX6650_REG_TACH3, -}; - -static struct max6650_data *max6650_update_device(struct device *dev) +static int max6650_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - int i; - struct i2c_client *client = to_i2c_client(dev); - struct max6650_data *data = i2c_get_clientdata(client); - - mutex_lock(&data->update_lock); + struct device *dev = &client->dev; + struct max6650_data *data; + struct device *hwmon_dev; + int err; - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - data->speed = i2c_smbus_read_byte_data(client, - MAX6650_REG_SPEED); - data->config = i2c_smbus_read_byte_data(client, - MAX6650_REG_CONFIG); - for (i = 0; i < 4; i++) { - data->tach[i] = i2c_smbus_read_byte_data(client, - tach_reg[i]); - } - data->count = i2c_smbus_read_byte_data(client, - MAX6650_REG_COUNT); - data->dac = i2c_smbus_read_byte_data(client, MAX6650_REG_DAC); + data = devm_kzalloc(dev, sizeof(struct max6650_data), GFP_KERNEL); + if (!data) + return -ENOMEM; - /* Alarms are cleared on read in case the condition that - * caused the alarm is removed. Keep the value latched here - * for providing the register through different alarm files. */ - data->alarm |= i2c_smbus_read_byte_data(client, - MAX6650_REG_ALARM); + data->client = client; + mutex_init(&data->update_lock); + data->nr_fans = id->driver_data; - data->last_updated = jiffies; - data->valid = 1; - } + /* + * Initialize the max6650 chip + */ + err = max6650_init_client(data, client); + if (err) + return err; - mutex_unlock(&data->update_lock); + data->groups[0] = &max6650_group; + /* 3 additional fan inputs for the MAX6651 */ + if (data->nr_fans == 4) + data->groups[1] = &max6651_group; - return data; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, + client->name, data, + data->groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } -static int __init sensors_max6650_init(void) -{ - return i2c_add_driver(&max6650_driver); -} +static const struct i2c_device_id max6650_id[] = { + { "max6650", 1 }, + { "max6651", 4 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max6650_id); -static void __exit sensors_max6650_exit(void) -{ - i2c_del_driver(&max6650_driver); -} +static struct i2c_driver max6650_driver = { + .driver = { + .name = "max6650", + }, + .probe = max6650_probe, + .id_table = max6650_id, +}; + +module_i2c_driver(max6650_driver); MODULE_AUTHOR("Hans J. Koch"); MODULE_DESCRIPTION("MAX6650 sensor driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_max6650_init); -module_exit(sensors_max6650_exit); diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c new file mode 100644 index 00000000000..7fd3eaf817f --- /dev/null +++ b/drivers/hwmon/max6697.c @@ -0,0 +1,680 @@ +/* + * Copyright (c) 2012 Guenter Roeck <linux@roeck-us.net> + * + * based on max1668.c + * Copyright (c) 2011 David George <david.george@ska.ac.za> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/of.h> + +#include <linux/platform_data/max6697.h> + +enum chips { max6581, max6602, max6622, max6636, max6689, max6693, max6694, + max6697, max6698, max6699 }; + +/* Report local sensor as temp1 */ + +static const u8 MAX6697_REG_TEMP[] = { + 0x07, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08 }; +static const u8 MAX6697_REG_TEMP_EXT[] = { + 0x57, 0x09, 0x52, 0x53, 0x54, 0x55, 0x56, 0 }; +static const u8 MAX6697_REG_MAX[] = { + 0x17, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x18 }; +static const u8 MAX6697_REG_CRIT[] = { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 }; + +/* + * Map device tree / platform data register bit map to chip bit map. + * Applies to alert register and over-temperature register. + */ +#define MAX6697_MAP_BITS(reg) ((((reg) & 0x7e) >> 1) | \ + (((reg) & 0x01) << 6) | ((reg) & 0x80)) + +#define MAX6697_REG_STAT(n) (0x44 + (n)) + +#define MAX6697_REG_CONFIG 0x41 +#define MAX6581_CONF_EXTENDED (1 << 1) +#define MAX6693_CONF_BETA (1 << 2) +#define MAX6697_CONF_RESISTANCE (1 << 3) +#define MAX6697_CONF_TIMEOUT (1 << 5) +#define MAX6697_REG_ALERT_MASK 0x42 +#define MAX6697_REG_OVERT_MASK 0x43 + +#define MAX6581_REG_RESISTANCE 0x4a +#define MAX6581_REG_IDEALITY 0x4b +#define MAX6581_REG_IDEALITY_SELECT 0x4c +#define MAX6581_REG_OFFSET 0x4d +#define MAX6581_REG_OFFSET_SELECT 0x4e + +#define MAX6697_CONV_TIME 156 /* ms per channel, worst case */ + +struct max6697_chip_data { + int channels; + u32 have_ext; + u32 have_crit; + u32 have_fault; + u8 valid_conf; + const u8 *alarm_map; +}; + +struct max6697_data { + struct i2c_client *client; + + enum chips type; + const struct max6697_chip_data *chip; + + int update_interval; /* in milli-seconds */ + int temp_offset; /* in degrees C */ + + struct mutex update_lock; + unsigned long last_updated; /* In jiffies */ + bool valid; /* true if following fields are valid */ + + /* 1x local and up to 7x remote */ + u8 temp[8][4]; /* [nr][0]=temp [1]=ext [2]=max [3]=crit */ +#define MAX6697_TEMP_INPUT 0 +#define MAX6697_TEMP_EXT 1 +#define MAX6697_TEMP_MAX 2 +#define MAX6697_TEMP_CRIT 3 + u32 alarms; +}; + +/* Diode fault status bits on MAX6581 are right shifted by one bit */ +static const u8 max6581_alarm_map[] = { + 0, 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23 }; + +static const struct max6697_chip_data max6697_chip_data[] = { + [max6581] = { + .channels = 8, + .have_crit = 0xff, + .have_ext = 0x7f, + .have_fault = 0xfe, + .valid_conf = MAX6581_CONF_EXTENDED | MAX6697_CONF_TIMEOUT, + .alarm_map = max6581_alarm_map, + }, + [max6602] = { + .channels = 5, + .have_crit = 0x12, + .have_ext = 0x02, + .have_fault = 0x1e, + .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT, + }, + [max6622] = { + .channels = 5, + .have_crit = 0x12, + .have_ext = 0x02, + .have_fault = 0x1e, + .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT, + }, + [max6636] = { + .channels = 7, + .have_crit = 0x72, + .have_ext = 0x02, + .have_fault = 0x7e, + .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT, + }, + [max6689] = { + .channels = 7, + .have_crit = 0x72, + .have_ext = 0x02, + .have_fault = 0x7e, + .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT, + }, + [max6693] = { + .channels = 7, + .have_crit = 0x72, + .have_ext = 0x02, + .have_fault = 0x7e, + .valid_conf = MAX6697_CONF_RESISTANCE | MAX6693_CONF_BETA | + MAX6697_CONF_TIMEOUT, + }, + [max6694] = { + .channels = 5, + .have_crit = 0x12, + .have_ext = 0x02, + .have_fault = 0x1e, + .valid_conf = MAX6697_CONF_RESISTANCE | MAX6693_CONF_BETA | + MAX6697_CONF_TIMEOUT, + }, + [max6697] = { + .channels = 7, + .have_crit = 0x72, + .have_ext = 0x02, + .have_fault = 0x7e, + .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT, + }, + [max6698] = { + .channels = 7, + .have_crit = 0x72, + .have_ext = 0x02, + .have_fault = 0x0e, + .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT, + }, + [max6699] = { + .channels = 5, + .have_crit = 0x12, + .have_ext = 0x02, + .have_fault = 0x1e, + .valid_conf = MAX6697_CONF_RESISTANCE | MAX6697_CONF_TIMEOUT, + }, +}; + +static struct max6697_data *max6697_update_device(struct device *dev) +{ + struct max6697_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + struct max6697_data *ret = data; + int val; + int i; + u32 alarms; + + mutex_lock(&data->update_lock); + + if (data->valid && + !time_after(jiffies, data->last_updated + + msecs_to_jiffies(data->update_interval))) + goto abort; + + for (i = 0; i < data->chip->channels; i++) { + if (data->chip->have_ext & (1 << i)) { + val = i2c_smbus_read_byte_data(client, + MAX6697_REG_TEMP_EXT[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp[i][MAX6697_TEMP_EXT] = val; + } + + val = i2c_smbus_read_byte_data(client, MAX6697_REG_TEMP[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp[i][MAX6697_TEMP_INPUT] = val; + + val = i2c_smbus_read_byte_data(client, MAX6697_REG_MAX[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp[i][MAX6697_TEMP_MAX] = val; + + if (data->chip->have_crit & (1 << i)) { + val = i2c_smbus_read_byte_data(client, + MAX6697_REG_CRIT[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp[i][MAX6697_TEMP_CRIT] = val; + } + } + + alarms = 0; + for (i = 0; i < 3; i++) { + val = i2c_smbus_read_byte_data(client, MAX6697_REG_STAT(i)); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + alarms = (alarms << 8) | val; + } + data->alarms = alarms; + data->last_updated = jiffies; + data->valid = true; +abort: + mutex_unlock(&data->update_lock); + + return ret; +} + +static ssize_t show_temp_input(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct max6697_data *data = max6697_update_device(dev); + int temp; + + if (IS_ERR(data)) + return PTR_ERR(data); + + temp = (data->temp[index][MAX6697_TEMP_INPUT] - data->temp_offset) << 3; + temp |= data->temp[index][MAX6697_TEMP_EXT] >> 5; + + return sprintf(buf, "%d\n", temp * 125); +} + +static ssize_t show_temp(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int nr = to_sensor_dev_attr_2(devattr)->nr; + int index = to_sensor_dev_attr_2(devattr)->index; + struct max6697_data *data = max6697_update_device(dev); + int temp; + + if (IS_ERR(data)) + return PTR_ERR(data); + + temp = data->temp[nr][index]; + temp -= data->temp_offset; + + return sprintf(buf, "%d\n", temp * 1000); +} + +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int index = to_sensor_dev_attr(attr)->index; + struct max6697_data *data = max6697_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + if (data->chip->alarm_map) + index = data->chip->alarm_map[index]; + + return sprintf(buf, "%u\n", (data->alarms >> index) & 0x1); +} + +static ssize_t set_temp(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + int nr = to_sensor_dev_attr_2(devattr)->nr; + int index = to_sensor_dev_attr_2(devattr)->index; + struct max6697_data *data = dev_get_drvdata(dev); + long temp; + int ret; + + ret = kstrtol(buf, 10, &temp); + if (ret < 0) + return ret; + + mutex_lock(&data->update_lock); + temp = DIV_ROUND_CLOSEST(temp, 1000) + data->temp_offset; + temp = clamp_val(temp, 0, data->type == max6581 ? 255 : 127); + data->temp[nr][index] = temp; + ret = i2c_smbus_write_byte_data(data->client, + index == 2 ? MAX6697_REG_MAX[nr] + : MAX6697_REG_CRIT[nr], + temp); + mutex_unlock(&data->update_lock); + + return ret < 0 ? ret : count; +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL, 0); +static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 0, MAX6697_TEMP_MAX); +static SENSOR_DEVICE_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp, set_temp, + 0, MAX6697_TEMP_CRIT); + +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input, NULL, 1); +static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 1, MAX6697_TEMP_MAX); +static SENSOR_DEVICE_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp, set_temp, + 1, MAX6697_TEMP_CRIT); + +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp_input, NULL, 2); +static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 2, MAX6697_TEMP_MAX); +static SENSOR_DEVICE_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR, show_temp, set_temp, + 2, MAX6697_TEMP_CRIT); + +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp_input, NULL, 3); +static SENSOR_DEVICE_ATTR_2(temp4_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 3, MAX6697_TEMP_MAX); +static SENSOR_DEVICE_ATTR_2(temp4_crit, S_IRUGO | S_IWUSR, show_temp, set_temp, + 3, MAX6697_TEMP_CRIT); + +static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp_input, NULL, 4); +static SENSOR_DEVICE_ATTR_2(temp5_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 4, MAX6697_TEMP_MAX); +static SENSOR_DEVICE_ATTR_2(temp5_crit, S_IRUGO | S_IWUSR, show_temp, set_temp, + 4, MAX6697_TEMP_CRIT); + +static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_temp_input, NULL, 5); +static SENSOR_DEVICE_ATTR_2(temp6_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 5, MAX6697_TEMP_MAX); +static SENSOR_DEVICE_ATTR_2(temp6_crit, S_IRUGO | S_IWUSR, show_temp, set_temp, + 5, MAX6697_TEMP_CRIT); + +static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, show_temp_input, NULL, 6); +static SENSOR_DEVICE_ATTR_2(temp7_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 6, MAX6697_TEMP_MAX); +static SENSOR_DEVICE_ATTR_2(temp7_crit, S_IRUGO | S_IWUSR, show_temp, set_temp, + 6, MAX6697_TEMP_CRIT); + +static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, show_temp_input, NULL, 7); +static SENSOR_DEVICE_ATTR_2(temp8_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + 7, MAX6697_TEMP_MAX); +static SENSOR_DEVICE_ATTR_2(temp8_crit, S_IRUGO | S_IWUSR, show_temp, set_temp, + 7, MAX6697_TEMP_CRIT); + +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 22); +static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 16); +static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 17); +static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, 18); +static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_alarm, NULL, 19); +static SENSOR_DEVICE_ATTR(temp6_max_alarm, S_IRUGO, show_alarm, NULL, 20); +static SENSOR_DEVICE_ATTR(temp7_max_alarm, S_IRUGO, show_alarm, NULL, 21); +static SENSOR_DEVICE_ATTR(temp8_max_alarm, S_IRUGO, show_alarm, NULL, 23); + +static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 14); +static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 8); +static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, 9); +static SENSOR_DEVICE_ATTR(temp4_crit_alarm, S_IRUGO, show_alarm, NULL, 10); +static SENSOR_DEVICE_ATTR(temp5_crit_alarm, S_IRUGO, show_alarm, NULL, 11); +static SENSOR_DEVICE_ATTR(temp6_crit_alarm, S_IRUGO, show_alarm, NULL, 12); +static SENSOR_DEVICE_ATTR(temp7_crit_alarm, S_IRUGO, show_alarm, NULL, 13); +static SENSOR_DEVICE_ATTR(temp8_crit_alarm, S_IRUGO, show_alarm, NULL, 15); + +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_fault, S_IRUGO, show_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(temp7_fault, S_IRUGO, show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(temp8_fault, S_IRUGO, show_alarm, NULL, 7); + +static DEVICE_ATTR(dummy, 0, NULL, NULL); + +static umode_t max6697_is_visible(struct kobject *kobj, struct attribute *attr, + int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct max6697_data *data = dev_get_drvdata(dev); + const struct max6697_chip_data *chip = data->chip; + int channel = index / 6; /* channel number */ + int nr = index % 6; /* attribute index within channel */ + + if (channel >= chip->channels) + return 0; + + if ((nr == 3 || nr == 4) && !(chip->have_crit & (1 << channel))) + return 0; + if (nr == 5 && !(chip->have_fault & (1 << channel))) + return 0; + + return attr->mode; +} + +/* + * max6697_is_visible uses the index into the following array to determine + * if attributes should be created or not. Any change in order or content + * must be matched in max6697_is_visible. + */ +static struct attribute *max6697_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + &dev_attr_dummy.attr, + + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, + + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_crit.dev_attr.attr, + &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_fault.dev_attr.attr, + + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_crit.dev_attr.attr, + &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_fault.dev_attr.attr, + + &sensor_dev_attr_temp6_input.dev_attr.attr, + &sensor_dev_attr_temp6_max.dev_attr.attr, + &sensor_dev_attr_temp6_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp6_crit.dev_attr.attr, + &sensor_dev_attr_temp6_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp6_fault.dev_attr.attr, + + &sensor_dev_attr_temp7_input.dev_attr.attr, + &sensor_dev_attr_temp7_max.dev_attr.attr, + &sensor_dev_attr_temp7_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp7_crit.dev_attr.attr, + &sensor_dev_attr_temp7_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp7_fault.dev_attr.attr, + + &sensor_dev_attr_temp8_input.dev_attr.attr, + &sensor_dev_attr_temp8_max.dev_attr.attr, + &sensor_dev_attr_temp8_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp8_crit.dev_attr.attr, + &sensor_dev_attr_temp8_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp8_fault.dev_attr.attr, + NULL +}; + +static const struct attribute_group max6697_group = { + .attrs = max6697_attributes, .is_visible = max6697_is_visible, +}; +__ATTRIBUTE_GROUPS(max6697); + +static void max6697_get_config_of(struct device_node *node, + struct max6697_platform_data *pdata) +{ + int len; + const __be32 *prop; + + prop = of_get_property(node, "smbus-timeout-disable", &len); + if (prop) + pdata->smbus_timeout_disable = true; + prop = of_get_property(node, "extended-range-enable", &len); + if (prop) + pdata->extended_range_enable = true; + prop = of_get_property(node, "beta-compensation-enable", &len); + if (prop) + pdata->beta_compensation = true; + prop = of_get_property(node, "alert-mask", &len); + if (prop && len == sizeof(u32)) + pdata->alert_mask = be32_to_cpu(prop[0]); + prop = of_get_property(node, "over-temperature-mask", &len); + if (prop && len == sizeof(u32)) + pdata->over_temperature_mask = be32_to_cpu(prop[0]); + prop = of_get_property(node, "resistance-cancellation", &len); + if (prop) { + if (len == sizeof(u32)) + pdata->resistance_cancellation = be32_to_cpu(prop[0]); + else + pdata->resistance_cancellation = 0xfe; + } + prop = of_get_property(node, "transistor-ideality", &len); + if (prop && len == 2 * sizeof(u32)) { + pdata->ideality_mask = be32_to_cpu(prop[0]); + pdata->ideality_value = be32_to_cpu(prop[1]); + } +} + +static int max6697_init_chip(struct max6697_data *data, + struct i2c_client *client) +{ + struct max6697_platform_data *pdata = dev_get_platdata(&client->dev); + struct max6697_platform_data p; + const struct max6697_chip_data *chip = data->chip; + int factor = chip->channels; + int ret, reg; + + /* + * Don't touch configuration if neither platform data nor OF + * configuration was specified. If that is the case, use the + * current chip configuration. + */ + if (!pdata && !client->dev.of_node) { + reg = i2c_smbus_read_byte_data(client, MAX6697_REG_CONFIG); + if (reg < 0) + return reg; + if (data->type == max6581) { + if (reg & MAX6581_CONF_EXTENDED) + data->temp_offset = 64; + reg = i2c_smbus_read_byte_data(client, + MAX6581_REG_RESISTANCE); + if (reg < 0) + return reg; + factor += hweight8(reg); + } else { + if (reg & MAX6697_CONF_RESISTANCE) + factor++; + } + goto done; + } + + if (client->dev.of_node) { + memset(&p, 0, sizeof(p)); + max6697_get_config_of(client->dev.of_node, &p); + pdata = &p; + } + + reg = 0; + if (pdata->smbus_timeout_disable && + (chip->valid_conf & MAX6697_CONF_TIMEOUT)) { + reg |= MAX6697_CONF_TIMEOUT; + } + if (pdata->extended_range_enable && + (chip->valid_conf & MAX6581_CONF_EXTENDED)) { + reg |= MAX6581_CONF_EXTENDED; + data->temp_offset = 64; + } + if (pdata->resistance_cancellation && + (chip->valid_conf & MAX6697_CONF_RESISTANCE)) { + reg |= MAX6697_CONF_RESISTANCE; + factor++; + } + if (pdata->beta_compensation && + (chip->valid_conf & MAX6693_CONF_BETA)) { + reg |= MAX6693_CONF_BETA; + } + + ret = i2c_smbus_write_byte_data(client, MAX6697_REG_CONFIG, reg); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, MAX6697_REG_ALERT_MASK, + MAX6697_MAP_BITS(pdata->alert_mask)); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, MAX6697_REG_OVERT_MASK, + MAX6697_MAP_BITS(pdata->over_temperature_mask)); + if (ret < 0) + return ret; + + if (data->type == max6581) { + factor += hweight8(pdata->resistance_cancellation >> 1); + ret = i2c_smbus_write_byte_data(client, MAX6581_REG_RESISTANCE, + pdata->resistance_cancellation >> 1); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(client, MAX6581_REG_IDEALITY, + pdata->ideality_value); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(client, + MAX6581_REG_IDEALITY_SELECT, + pdata->ideality_mask >> 1); + if (ret < 0) + return ret; + } +done: + data->update_interval = factor * MAX6697_CONV_TIME; + return 0; +} + +static int max6697_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; + struct max6697_data *data; + struct device *hwmon_dev; + int err; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(struct max6697_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->type = id->driver_data; + data->chip = &max6697_chip_data[data->type]; + data->client = client; + mutex_init(&data->update_lock); + + err = max6697_init_chip(data, client); + if (err) + return err; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + max6697_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id max6697_id[] = { + { "max6581", max6581 }, + { "max6602", max6602 }, + { "max6622", max6622 }, + { "max6636", max6636 }, + { "max6689", max6689 }, + { "max6693", max6693 }, + { "max6694", max6694 }, + { "max6697", max6697 }, + { "max6698", max6698 }, + { "max6699", max6699 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max6697_id); + +static struct i2c_driver max6697_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "max6697", + }, + .probe = max6697_probe, + .id_table = max6697_id, +}; + +module_i2c_driver(max6697_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("MAX6697 temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c index 883fa8197da..ae00e60d856 100644 --- a/drivers/hwmon/mc13783-adc.c +++ b/drivers/hwmon/mc13783-adc.c @@ -1,5 +1,5 @@ /* - * Driver for the Freescale Semiconductor MC13783 adc. + * Driver for the ADC on Freescale Semiconductor MC13783 and MC13892 PMICs. * * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright (C) 2009 Sascha Hauer, Pengutronix @@ -18,41 +18,48 @@ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <linux/mfd/mc13783-private.h> +#include <linux/mfd/mc13xxx.h> #include <linux/platform_device.h> #include <linux/hwmon-sysfs.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/hwmon.h> +#include <linux/slab.h> #include <linux/init.h> #include <linux/err.h> -#define MC13783_ADC_NAME "mc13783-adc" +#define DRIVER_NAME "mc13783-adc" + +/* platform device id driver data */ +#define MC13783_ADC_16CHANS 1 +#define MC13783_ADC_BPDIV2 2 struct mc13783_adc_priv { - struct mc13783 *mc13783; + struct mc13xxx *mc13xxx; struct device *hwmon_dev; + char name[PLATFORM_NAME_SIZE]; }; static ssize_t mc13783_adc_show_name(struct device *dev, struct device_attribute *devattr, char *buf) { - return sprintf(buf, "mc13783_adc\n"); + struct mc13783_adc_priv *priv = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", priv->name); } static int mc13783_adc_read(struct device *dev, struct device_attribute *devattr, unsigned int *val) { - struct platform_device *pdev = to_platform_device(dev); - struct mc13783_adc_priv *priv = platform_get_drvdata(pdev); + struct mc13783_adc_priv *priv = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); unsigned int channel = attr->index; unsigned int sample[4]; int ret; - ret = mc13783_adc_do_conversion(priv->mc13783, - MC13783_ADC_MODE_MULT_CHAN, - channel, sample); + ret = mc13xxx_adc_do_conversion(priv->mc13xxx, + MC13XXX_ADC_MODE_MULT_CHAN, + channel, 0, 0, sample); if (ret) return ret; @@ -67,16 +74,21 @@ static ssize_t mc13783_adc_read_bp(struct device *dev, struct device_attribute *devattr, char *buf) { unsigned val; + struct platform_device *pdev = to_platform_device(dev); + kernel_ulong_t driver_data = platform_get_device_id(pdev)->driver_data; int ret = mc13783_adc_read(dev, devattr, &val); if (ret) return ret; - /* - * BP (channel 2) reports with offset 2.4V to the actual value to fit - * the input range of the ADC. unit = 2.25mV = 9/4 mV. - */ - val = DIV_ROUND_CLOSEST(val * 9, 4) + 2400; + if (driver_data & MC13783_ADC_BPDIV2) + val = DIV_ROUND_CLOSEST(val * 9, 2); + else + /* + * BP (channel 2) reports with offset 2.4V to the actual value + * to fit the input range of the ADC. unit = 2.25mV = 9/4 mV. + */ + val = DIV_ROUND_CLOSEST(val * 9, 4) + 2400; return sprintf(buf, "%u\n", val); } @@ -113,12 +125,21 @@ static SENSOR_DEVICE_ATTR(in13_input, S_IRUGO, mc13783_adc_read_gp, NULL, 13); static SENSOR_DEVICE_ATTR(in14_input, S_IRUGO, mc13783_adc_read_gp, NULL, 14); static SENSOR_DEVICE_ATTR(in15_input, S_IRUGO, mc13783_adc_read_gp, NULL, 15); -static struct attribute *mc13783_attr[] = { +static struct attribute *mc13783_attr_base[] = { &dev_attr_name.attr, &sensor_dev_attr_in2_input.dev_attr.attr, &sensor_dev_attr_in5_input.dev_attr.attr, &sensor_dev_attr_in6_input.dev_attr.attr, &sensor_dev_attr_in7_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group mc13783_group_base = { + .attrs = mc13783_attr_base, +}; + +/* these are only used if MC13783_ADC_16CHANS is provided in driver data */ +static struct attribute *mc13783_attr_16chans[] = { &sensor_dev_attr_in8_input.dev_attr.attr, &sensor_dev_attr_in9_input.dev_attr.attr, &sensor_dev_attr_in10_input.dev_attr.attr, @@ -126,8 +147,8 @@ static struct attribute *mc13783_attr[] = { NULL }; -static const struct attribute_group mc13783_group = { - .attrs = mc13783_attr, +static const struct attribute_group mc13783_group_16chans = { + .attrs = mc13783_attr_16chans, }; /* last four channels may be occupied by the touchscreen */ @@ -143,28 +164,50 @@ static const struct attribute_group mc13783_group_ts = { .attrs = mc13783_attr_ts, }; +static int mc13783_adc_use_touchscreen(struct platform_device *pdev) +{ + struct mc13783_adc_priv *priv = platform_get_drvdata(pdev); + unsigned flags = mc13xxx_get_flags(priv->mc13xxx); + + return flags & MC13XXX_USE_TOUCHSCREEN; +} + static int __init mc13783_adc_probe(struct platform_device *pdev) { struct mc13783_adc_priv *priv; int ret; + const struct platform_device_id *id = platform_get_device_id(pdev); + char *dash; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - priv->mc13783 = dev_get_drvdata(pdev->dev.parent); + priv->mc13xxx = dev_get_drvdata(pdev->dev.parent); + snprintf(priv->name, ARRAY_SIZE(priv->name), "%s", id->name); + dash = strchr(priv->name, '-'); + if (dash) + *dash = '\0'; platform_set_drvdata(pdev, priv); /* Register sysfs hooks */ - ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group); + ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group_base); if (ret) - goto out_err_create1; + return ret; + + if (id->driver_data & MC13783_ADC_16CHANS) { + ret = sysfs_create_group(&pdev->dev.kobj, + &mc13783_group_16chans); + if (ret) + goto out_err_create_16chans; + } - if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN)) + if (!mc13783_adc_use_touchscreen(pdev)) { ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group_ts); if (ret) - goto out_err_create2; + goto out_err_create_ts; + } priv->hwmon_dev = hwmon_device_register(&pdev->dev); if (IS_ERR(priv->hwmon_dev)) { @@ -174,63 +217,64 @@ static int __init mc13783_adc_probe(struct platform_device *pdev) goto out_err_register; } - return 0; out_err_register: - if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN)) + if (!mc13783_adc_use_touchscreen(pdev)) sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts); -out_err_create2: +out_err_create_ts: - sysfs_remove_group(&pdev->dev.kobj, &mc13783_group); -out_err_create1: - - platform_set_drvdata(pdev, NULL); - kfree(priv); + if (id->driver_data & MC13783_ADC_16CHANS) + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_16chans); +out_err_create_16chans: + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_base); return ret; } -static int __devexit mc13783_adc_remove(struct platform_device *pdev) +static int mc13783_adc_remove(struct platform_device *pdev) { struct mc13783_adc_priv *priv = platform_get_drvdata(pdev); + kernel_ulong_t driver_data = platform_get_device_id(pdev)->driver_data; hwmon_device_unregister(priv->hwmon_dev); - if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN)) + if (!mc13783_adc_use_touchscreen(pdev)) sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts); - sysfs_remove_group(&pdev->dev.kobj, &mc13783_group); + if (driver_data & MC13783_ADC_16CHANS) + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_16chans); - platform_set_drvdata(pdev, NULL); - kfree(priv); + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_base); return 0; } +static const struct platform_device_id mc13783_adc_idtable[] = { + { + .name = "mc13783-adc", + .driver_data = MC13783_ADC_16CHANS, + }, { + .name = "mc13892-adc", + .driver_data = MC13783_ADC_BPDIV2, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, mc13783_adc_idtable); + static struct platform_driver mc13783_adc_driver = { - .remove = __devexit_p(mc13783_adc_remove), + .remove = mc13783_adc_remove, .driver = { .owner = THIS_MODULE, - .name = MC13783_ADC_NAME, + .name = DRIVER_NAME, }, + .id_table = mc13783_adc_idtable, }; -static int __init mc13783_adc_init(void) -{ - return platform_driver_probe(&mc13783_adc_driver, mc13783_adc_probe); -} - -static void __exit mc13783_adc_exit(void) -{ - platform_driver_unregister(&mc13783_adc_driver); -} - -module_init(mc13783_adc_init); -module_exit(mc13783_adc_exit); +module_platform_driver_probe(mc13783_adc_driver, mc13783_adc_probe); MODULE_DESCRIPTION("MC13783 ADC driver"); MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" MC13783_ADC_NAME); diff --git a/drivers/hwmon/mcp3021.c b/drivers/hwmon/mcp3021.c new file mode 100644 index 00000000000..d219c06a857 --- /dev/null +++ b/drivers/hwmon/mcp3021.c @@ -0,0 +1,201 @@ +/* + * mcp3021.c - driver for Microchip MCP3021 and MCP3221 + * + * Copyright (C) 2008-2009, 2012 Freescale Semiconductor, Inc. + * Author: Mingkai Hu <Mingkai.hu@freescale.com> + * Reworked by Sven Schuchmann <schuchmann@schleissheimer.de> + * + * This driver export the value of analog input voltage to sysfs, the + * voltage unit is mV. Through the sysfs interface, lm-sensors tool + * can also display the input voltage. + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/hwmon.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/device.h> + +/* Vdd info */ +#define MCP3021_VDD_MAX 5500 +#define MCP3021_VDD_MIN 2700 +#define MCP3021_VDD_REF 3300 + +/* output format */ +#define MCP3021_SAR_SHIFT 2 +#define MCP3021_SAR_MASK 0x3ff + +#define MCP3021_OUTPUT_RES 10 /* 10-bit resolution */ +#define MCP3021_OUTPUT_SCALE 4 + +#define MCP3221_SAR_SHIFT 0 +#define MCP3221_SAR_MASK 0xfff +#define MCP3221_OUTPUT_RES 12 /* 12-bit resolution */ +#define MCP3221_OUTPUT_SCALE 1 + +enum chips { + mcp3021, + mcp3221 +}; + +/* + * Client data (each client gets its own) + */ +struct mcp3021_data { + struct device *hwmon_dev; + u32 vdd; /* device power supply */ + u16 sar_shift; + u16 sar_mask; + u8 output_res; + u8 output_scale; +}; + +static int mcp3021_read16(struct i2c_client *client) +{ + struct mcp3021_data *data = i2c_get_clientdata(client); + int ret; + u16 reg; + __be16 buf; + + ret = i2c_master_recv(client, (char *)&buf, 2); + if (ret < 0) + return ret; + if (ret != 2) + return -EIO; + + /* The output code of the MCP3021 is transmitted with MSB first. */ + reg = be16_to_cpu(buf); + + /* + * The ten-bit output code is composed of the lower 4-bit of the + * first byte and the upper 6-bit of the second byte. + */ + reg = (reg >> data->sar_shift) & data->sar_mask; + + return reg; +} + +static inline u16 volts_from_reg(struct mcp3021_data *data, u16 val) +{ + if (val == 0) + return 0; + + val = val * data->output_scale - data->output_scale / 2; + + return val * DIV_ROUND_CLOSEST(data->vdd, + (1 << data->output_res) * data->output_scale); +} + +static ssize_t show_in_input(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mcp3021_data *data = i2c_get_clientdata(client); + int reg, in_input; + + reg = mcp3021_read16(client); + if (reg < 0) + return reg; + + in_input = volts_from_reg(data, reg); + + return sprintf(buf, "%d\n", in_input); +} + +static DEVICE_ATTR(in0_input, S_IRUGO, show_in_input, NULL); + +static int mcp3021_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err; + struct mcp3021_data *data = NULL; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + data = devm_kzalloc(&client->dev, sizeof(struct mcp3021_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + + switch (id->driver_data) { + case mcp3021: + data->sar_shift = MCP3021_SAR_SHIFT; + data->sar_mask = MCP3021_SAR_MASK; + data->output_res = MCP3021_OUTPUT_RES; + data->output_scale = MCP3021_OUTPUT_SCALE; + break; + + case mcp3221: + data->sar_shift = MCP3221_SAR_SHIFT; + data->sar_mask = MCP3221_SAR_MASK; + data->output_res = MCP3221_OUTPUT_RES; + data->output_scale = MCP3221_OUTPUT_SCALE; + break; + } + + if (dev_get_platdata(&client->dev)) { + data->vdd = *(u32 *)dev_get_platdata(&client->dev); + if (data->vdd > MCP3021_VDD_MAX || data->vdd < MCP3021_VDD_MIN) + return -EINVAL; + } else { + data->vdd = MCP3021_VDD_REF; + } + + err = sysfs_create_file(&client->dev.kobj, &dev_attr_in0_input.attr); + if (err) + return err; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + return 0; + +exit_remove: + sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr); + return err; +} + +static int mcp3021_remove(struct i2c_client *client) +{ + struct mcp3021_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr); + + return 0; +} + +static const struct i2c_device_id mcp3021_id[] = { + { "mcp3021", mcp3021 }, + { "mcp3221", mcp3221 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcp3021_id); + +static struct i2c_driver mcp3021_driver = { + .driver = { + .name = "mcp3021", + }, + .probe = mcp3021_probe, + .remove = mcp3021_remove, + .id_table = mcp3021_id, +}; + +module_i2c_driver(mcp3021_driver); + +MODULE_AUTHOR("Mingkai Hu <Mingkai.hu@freescale.com>"); +MODULE_DESCRIPTION("Microchip MCP3021/MCP3221 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c new file mode 100644 index 00000000000..7710f4694ba --- /dev/null +++ b/drivers/hwmon/nct6683.c @@ -0,0 +1,1457 @@ +/* + * nct6683 - Driver for the hardware monitoring functionality of + * Nuvoton NCT6683D eSIO + * + * Copyright (C) 2013 Guenter Roeck <linux@roeck-us.net> + * + * Derived from nct6775 driver + * Copyright (C) 2012, 2013 Guenter Roeck <linux@roeck-us.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (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. + * + * Supports the following chips: + * + * Chip #vin #fan #pwm #temp chip ID + * nct6683d 21(1) 16 8 32(1) 0xc730 + * + * Notes: + * (1) Total number of vin and temp inputs is 32. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/acpi.h> +#include <linux/dmi.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/jiffies.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +enum kinds { nct6683 }; + +static bool force; +module_param(force, bool, 0); +MODULE_PARM_DESC(force, "Set to one to enable detection on non-Intel boards"); + +static const char * const nct6683_device_names[] = { + "nct6683", +}; + +static const char * const nct6683_chip_names[] = { + "NCT6683D", +}; + +#define DRVNAME "nct6683" + +/* + * Super-I/O constants and functions + */ + +#define NCT6683_LD_ACPI 0x0a +#define NCT6683_LD_HWM 0x0b +#define NCT6683_LD_VID 0x0d + +#define SIO_REG_LDSEL 0x07 /* Logical device select */ +#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ +#define SIO_REG_ENABLE 0x30 /* Logical device enable */ +#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ + +#define SIO_NCT6681_ID 0xb270 /* for later */ +#define SIO_NCT6683_ID 0xc730 +#define SIO_ID_MASK 0xFFF0 + +static inline void +superio_outb(int ioreg, int reg, int val) +{ + outb(reg, ioreg); + outb(val, ioreg + 1); +} + +static inline int +superio_inb(int ioreg, int reg) +{ + outb(reg, ioreg); + return inb(ioreg + 1); +} + +static inline void +superio_select(int ioreg, int ld) +{ + outb(SIO_REG_LDSEL, ioreg); + outb(ld, ioreg + 1); +} + +static inline int +superio_enter(int ioreg) +{ + /* + * Try to reserve <ioreg> and <ioreg + 1> for exclusive access. + */ + if (!request_muxed_region(ioreg, 2, DRVNAME)) + return -EBUSY; + + outb(0x87, ioreg); + outb(0x87, ioreg); + + return 0; +} + +static inline void +superio_exit(int ioreg) +{ + outb(0xaa, ioreg); + outb(0x02, ioreg); + outb(0x02, ioreg + 1); + release_region(ioreg, 2); +} + +/* + * ISA constants + */ + +#define IOREGION_ALIGNMENT (~7) +#define IOREGION_OFFSET 4 /* Use EC port 1 */ +#define IOREGION_LENGTH 4 + +#define EC_PAGE_REG 0 +#define EC_INDEX_REG 1 +#define EC_DATA_REG 2 +#define EC_EVENT_REG 3 + +/* Common and NCT6683 specific data */ + +#define NCT6683_NUM_REG_MON 32 +#define NCT6683_NUM_REG_FAN 16 +#define NCT6683_NUM_REG_PWM 8 + +#define NCT6683_REG_MON(x) (0x100 + (x) * 2) +#define NCT6683_REG_FAN_RPM(x) (0x140 + (x) * 2) +#define NCT6683_REG_PWM(x) (0x160 + (x)) + +#define NCT6683_REG_MON_STS(x) (0x174 + (x)) +#define NCT6683_REG_IDLE(x) (0x178 + (x)) + +#define NCT6683_REG_FAN_STS(x) (0x17c + (x)) +#define NCT6683_REG_FAN_ERRSTS 0x17e +#define NCT6683_REG_FAN_INITSTS 0x17f + +#define NCT6683_HWM_CFG 0x180 + +#define NCT6683_REG_MON_CFG(x) (0x1a0 + (x)) +#define NCT6683_REG_FANIN_CFG(x) (0x1c0 + (x)) +#define NCT6683_REG_FANOUT_CFG(x) (0x1d0 + (x)) + +#define NCT6683_REG_INTEL_TEMP_MAX(x) (0x901 + (x) * 16) +#define NCT6683_REG_INTEL_TEMP_CRIT(x) (0x90d + (x) * 16) + +#define NCT6683_REG_TEMP_HYST(x) (0x330 + (x)) /* 8 bit */ +#define NCT6683_REG_TEMP_MAX(x) (0x350 + (x)) /* 8 bit */ +#define NCT6683_REG_MON_HIGH(x) (0x370 + (x) * 2) /* 8 bit */ +#define NCT6683_REG_MON_LOW(x) (0x371 + (x) * 2) /* 8 bit */ + +#define NCT6683_REG_FAN_MIN(x) (0x3b8 + (x) * 2) /* 16 bit */ + +#define NCT6683_REG_CUSTOMER_ID 0x602 +#define NCT6683_CUSTOMER_ID_INTEL 0x805 + +#define NCT6683_REG_BUILD_YEAR 0x604 +#define NCT6683_REG_BUILD_MONTH 0x605 +#define NCT6683_REG_BUILD_DAY 0x606 +#define NCT6683_REG_SERIAL 0x607 +#define NCT6683_REG_VERSION_HI 0x608 +#define NCT6683_REG_VERSION_LO 0x609 + +#define NCT6683_REG_CR_CASEOPEN 0xe8 +#define NCT6683_CR_CASEOPEN_MASK (1 << 7) + +#define NCT6683_REG_CR_BEEP 0xe0 +#define NCT6683_CR_BEEP_MASK (1 << 6) + +static const char *const nct6683_mon_label[] = { + NULL, /* disabled */ + "Local", + "Diode 0 (curr)", + "Diode 1 (curr)", + "Diode 2 (curr)", + "Diode 0 (volt)", + "Diode 1 (volt)", + "Diode 2 (volt)", + "Thermistor 14", + "Thermistor 15", + "Thermistor 16", + "Thermistor 0", + "Thermistor 1", + "Thermistor 2", + "Thermistor 3", + "Thermistor 4", + "Thermistor 5", /* 0x10 */ + "Thermistor 6", + "Thermistor 7", + "Thermistor 8", + "Thermistor 9", + "Thermistor 10", + "Thermistor 11", + "Thermistor 12", + "Thermistor 13", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "PECI 0.0", /* 0x20 */ + "PECI 1.0", + "PECI 2.0", + "PECI 3.0", + "PECI 0.1", + "PECI 1.1", + "PECI 2.1", + "PECI 3.1", + "PECI DIMM 0", + "PECI DIMM 1", + "PECI DIMM 2", + "PECI DIMM 3", + NULL, NULL, NULL, NULL, + "PCH CPU", /* 0x30 */ + "PCH CHIP", + "PCH CHIP CPU MAX", + "PCH MCH", + "PCH DIMM 0", + "PCH DIMM 1", + "PCH DIMM 2", + "PCH DIMM 3", + "SMBus 0", + "SMBus 1", + "SMBus 2", + "SMBus 3", + "SMBus 4", + "SMBus 5", + "DIMM 0", + "DIMM 1", + "DIMM 2", /* 0x40 */ + "DIMM 3", + "AMD TSI Addr 90h", + "AMD TSI Addr 92h", + "AMD TSI Addr 94h", + "AMD TSI Addr 96h", + "AMD TSI Addr 98h", + "AMD TSI Addr 9ah", + "AMD TSI Addr 9ch", + "AMD TSI Addr 9dh", + NULL, NULL, NULL, NULL, NULL, NULL, + "Virtual 0", /* 0x50 */ + "Virtual 1", + "Virtual 2", + "Virtual 3", + "Virtual 4", + "Virtual 5", + "Virtual 6", + "Virtual 7", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "VCC", /* 0x60 voltage sensors */ + "VSB", + "AVSB", + "VTT", + "VBAT", + "VREF", + "VIN0", + "VIN1", + "VIN2", + "VIN3", + "VIN4", + "VIN5", + "VIN6", + "VIN7", + "VIN8", + "VIN9", + "VIN10", + "VIN11", + "VIN12", + "VIN13", + "VIN14", + "VIN15", + "VIN16", +}; + +#define NUM_MON_LABELS ARRAY_SIZE(nct6683_mon_label) +#define MON_VOLTAGE_START 0x60 + +/* ------------------------------------------------------- */ + +struct nct6683_data { + int addr; /* IO base of EC space */ + int sioreg; /* SIO register */ + enum kinds kind; + u16 customer_id; + + struct device *hwmon_dev; + const struct attribute_group *groups[6]; + + int temp_num; /* number of temperature attributes */ + u8 temp_index[NCT6683_NUM_REG_MON]; + u8 temp_src[NCT6683_NUM_REG_MON]; + + u8 in_num; /* number of voltage attributes */ + u8 in_index[NCT6683_NUM_REG_MON]; + u8 in_src[NCT6683_NUM_REG_MON]; + + struct mutex update_lock; /* used to protect sensor updates */ + bool valid; /* true if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + /* Voltage attribute values */ + u8 in[3][NCT6683_NUM_REG_MON]; /* [0]=in, [1]=in_max, [2]=in_min */ + + /* Temperature attribute values */ + s16 temp_in[NCT6683_NUM_REG_MON]; + s8 temp[4][NCT6683_NUM_REG_MON];/* [0]=min, [1]=max, [2]=hyst, + * [3]=crit + */ + + /* Fan attribute values */ + unsigned int rpm[NCT6683_NUM_REG_FAN]; + u16 fan_min[NCT6683_NUM_REG_FAN]; + u8 fanin_cfg[NCT6683_NUM_REG_FAN]; + u8 fanout_cfg[NCT6683_NUM_REG_FAN]; + u16 have_fan; /* some fan inputs can be disabled */ + + u8 have_pwm; + u8 pwm[NCT6683_NUM_REG_PWM]; + +#ifdef CONFIG_PM + /* Remember extra register values over suspend/resume */ + u8 hwm_cfg; +#endif +}; + +struct nct6683_sio_data { + int sioreg; + enum kinds kind; +}; + +struct sensor_device_template { + struct device_attribute dev_attr; + union { + struct { + u8 nr; + u8 index; + } s; + int index; + } u; + bool s2; /* true if both index and nr are used */ +}; + +struct sensor_device_attr_u { + union { + struct sensor_device_attribute a1; + struct sensor_device_attribute_2 a2; + } u; + char name[32]; +}; + +#define __TEMPLATE_ATTR(_template, _mode, _show, _store) { \ + .attr = {.name = _template, .mode = _mode }, \ + .show = _show, \ + .store = _store, \ +} + +#define SENSOR_DEVICE_TEMPLATE(_template, _mode, _show, _store, _index) \ + { .dev_attr = __TEMPLATE_ATTR(_template, _mode, _show, _store), \ + .u.index = _index, \ + .s2 = false } + +#define SENSOR_DEVICE_TEMPLATE_2(_template, _mode, _show, _store, \ + _nr, _index) \ + { .dev_attr = __TEMPLATE_ATTR(_template, _mode, _show, _store), \ + .u.s.index = _index, \ + .u.s.nr = _nr, \ + .s2 = true } + +#define SENSOR_TEMPLATE(_name, _template, _mode, _show, _store, _index) \ +static struct sensor_device_template sensor_dev_template_##_name \ + = SENSOR_DEVICE_TEMPLATE(_template, _mode, _show, _store, \ + _index) + +#define SENSOR_TEMPLATE_2(_name, _template, _mode, _show, _store, \ + _nr, _index) \ +static struct sensor_device_template sensor_dev_template_##_name \ + = SENSOR_DEVICE_TEMPLATE_2(_template, _mode, _show, _store, \ + _nr, _index) + +struct sensor_template_group { + struct sensor_device_template **templates; + umode_t (*is_visible)(struct kobject *, struct attribute *, int); + int base; +}; + +static struct attribute_group * +nct6683_create_attr_group(struct device *dev, struct sensor_template_group *tg, + int repeat) +{ + struct sensor_device_attribute_2 *a2; + struct sensor_device_attribute *a; + struct sensor_device_template **t; + struct sensor_device_attr_u *su; + struct attribute_group *group; + struct attribute **attrs; + int i, j, count; + + if (repeat <= 0) + return ERR_PTR(-EINVAL); + + t = tg->templates; + for (count = 0; *t; t++, count++) + ; + + if (count == 0) + return ERR_PTR(-EINVAL); + + group = devm_kzalloc(dev, sizeof(*group), GFP_KERNEL); + if (group == NULL) + return ERR_PTR(-ENOMEM); + + attrs = devm_kzalloc(dev, sizeof(*attrs) * (repeat * count + 1), + GFP_KERNEL); + if (attrs == NULL) + return ERR_PTR(-ENOMEM); + + su = devm_kzalloc(dev, sizeof(*su) * repeat * count, + GFP_KERNEL); + if (su == NULL) + return ERR_PTR(-ENOMEM); + + group->attrs = attrs; + group->is_visible = tg->is_visible; + + for (i = 0; i < repeat; i++) { + t = tg->templates; + for (j = 0; *t != NULL; j++) { + snprintf(su->name, sizeof(su->name), + (*t)->dev_attr.attr.name, tg->base + i); + if ((*t)->s2) { + a2 = &su->u.a2; + a2->dev_attr.attr.name = su->name; + a2->nr = (*t)->u.s.nr + i; + a2->index = (*t)->u.s.index; + a2->dev_attr.attr.mode = + (*t)->dev_attr.attr.mode; + a2->dev_attr.show = (*t)->dev_attr.show; + a2->dev_attr.store = (*t)->dev_attr.store; + *attrs = &a2->dev_attr.attr; + } else { + a = &su->u.a1; + a->dev_attr.attr.name = su->name; + a->index = (*t)->u.index + i; + a->dev_attr.attr.mode = + (*t)->dev_attr.attr.mode; + a->dev_attr.show = (*t)->dev_attr.show; + a->dev_attr.store = (*t)->dev_attr.store; + *attrs = &a->dev_attr.attr; + } + attrs++; + su++; + t++; + } + } + + return group; +} + +/* LSB is 16 mV, except for the following sources, where it is 32 mV */ +#define MON_SRC_VCC 0x60 +#define MON_SRC_VSB 0x61 +#define MON_SRC_AVSB 0x62 +#define MON_SRC_VBAT 0x64 + +static inline long in_from_reg(u16 reg, u8 src) +{ + int scale = 16; + + if (src == MON_SRC_VCC || src == MON_SRC_VSB || src == MON_SRC_AVSB || + src == MON_SRC_VBAT) + scale <<= 1; + return reg * scale; +} + +static inline u16 in_to_reg(u32 val, u8 src) +{ + int scale = 16; + + if (src == MON_SRC_VCC || src == MON_SRC_VSB || src == MON_SRC_AVSB || + src == MON_SRC_VBAT) + scale <<= 1; + + return clamp_val(DIV_ROUND_CLOSEST(val, scale), 0, 127); +} + +static u16 nct6683_read(struct nct6683_data *data, u16 reg) +{ + int res; + + outb_p(0xff, data->addr + EC_PAGE_REG); /* unlock */ + outb_p(reg >> 8, data->addr + EC_PAGE_REG); + outb_p(reg & 0xff, data->addr + EC_INDEX_REG); + res = inb_p(data->addr + EC_DATA_REG); + return res; +} + +static u16 nct6683_read16(struct nct6683_data *data, u16 reg) +{ + return (nct6683_read(data, reg) << 8) | nct6683_read(data, reg + 1); +} + +static void nct6683_write(struct nct6683_data *data, u16 reg, u16 value) +{ + outb_p(0xff, data->addr + EC_PAGE_REG); /* unlock */ + outb_p(reg >> 8, data->addr + EC_PAGE_REG); + outb_p(reg & 0xff, data->addr + EC_INDEX_REG); + outb_p(value & 0xff, data->addr + EC_DATA_REG); +} + +static int get_in_reg(struct nct6683_data *data, int nr, int index) +{ + int ch = data->in_index[index]; + int reg = -EINVAL; + + switch (nr) { + case 0: + reg = NCT6683_REG_MON(ch); + break; + case 1: + if (data->customer_id != NCT6683_CUSTOMER_ID_INTEL) + reg = NCT6683_REG_MON_LOW(ch); + break; + case 2: + if (data->customer_id != NCT6683_CUSTOMER_ID_INTEL) + reg = NCT6683_REG_MON_HIGH(ch); + break; + default: + break; + } + return reg; +} + +static int get_temp_reg(struct nct6683_data *data, int nr, int index) +{ + int ch = data->temp_index[index]; + int reg = -EINVAL; + + switch (data->customer_id) { + case NCT6683_CUSTOMER_ID_INTEL: + switch (nr) { + default: + case 1: /* max */ + reg = NCT6683_REG_INTEL_TEMP_MAX(ch); + break; + case 3: /* crit */ + reg = NCT6683_REG_INTEL_TEMP_CRIT(ch); + break; + } + break; + default: + switch (nr) { + default: + case 0: /* min */ + reg = NCT6683_REG_MON_LOW(ch); + break; + case 1: /* max */ + reg = NCT6683_REG_TEMP_MAX(ch); + break; + case 2: /* hyst */ + reg = NCT6683_REG_TEMP_HYST(ch); + break; + case 3: /* crit */ + reg = NCT6683_REG_MON_HIGH(ch); + break; + } + break; + } + return reg; +} + +static void nct6683_update_pwm(struct device *dev) +{ + struct nct6683_data *data = dev_get_drvdata(dev); + int i; + + for (i = 0; i < NCT6683_NUM_REG_PWM; i++) { + if (!(data->have_pwm & (1 << i))) + continue; + data->pwm[i] = nct6683_read(data, NCT6683_REG_PWM(i)); + } +} + +static struct nct6683_data *nct6683_update_device(struct device *dev) +{ + struct nct6683_data *data = dev_get_drvdata(dev); + int i, j; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + /* Measured voltages and limits */ + for (i = 0; i < data->in_num; i++) { + for (j = 0; j < 3; j++) { + int reg = get_in_reg(data, j, i); + + if (reg >= 0) + data->in[j][i] = + nct6683_read(data, reg); + } + } + + /* Measured temperatures and limits */ + for (i = 0; i < data->temp_num; i++) { + u8 ch = data->temp_index[i]; + + data->temp_in[i] = nct6683_read16(data, + NCT6683_REG_MON(ch)); + for (j = 0; j < 4; j++) { + int reg = get_temp_reg(data, j, i); + + if (reg >= 0) + data->temp[j][i] = + nct6683_read(data, reg); + } + } + + /* Measured fan speeds and limits */ + for (i = 0; i < ARRAY_SIZE(data->rpm); i++) { + if (!(data->have_fan & (1 << i))) + continue; + + data->rpm[i] = nct6683_read16(data, + NCT6683_REG_FAN_RPM(i)); + data->fan_min[i] = nct6683_read16(data, + NCT6683_REG_FAN_MIN(i)); + } + + nct6683_update_pwm(dev); + + data->last_updated = jiffies; + data->valid = true; + } + + mutex_unlock(&data->update_lock); + return data; +} + +/* + * Sysfs callback functions + */ +static ssize_t +show_in_label(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct nct6683_data *data = nct6683_update_device(dev); + int nr = sattr->index; + + return sprintf(buf, "%s\n", nct6683_mon_label[data->in_src[nr]]); +} + +static ssize_t +show_in_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct6683_data *data = nct6683_update_device(dev); + int index = sattr->index; + int nr = sattr->nr; + + return sprintf(buf, "%ld\n", + in_from_reg(data->in[index][nr], data->in_index[index])); +} + +static umode_t nct6683_in_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct6683_data *data = dev_get_drvdata(dev); + int nr = index % 4; /* attribute */ + + /* + * Voltage limits exist for Intel boards, + * but register location and encoding is unknown + */ + if ((nr == 2 || nr == 3) && + data->customer_id == NCT6683_CUSTOMER_ID_INTEL) + return 0; + + return attr->mode; +} + +SENSOR_TEMPLATE(in_label, "in%d_label", S_IRUGO, show_in_label, NULL, 0); +SENSOR_TEMPLATE_2(in_input, "in%d_input", S_IRUGO, show_in_reg, NULL, 0, 0); +SENSOR_TEMPLATE_2(in_min, "in%d_min", S_IRUGO, show_in_reg, NULL, 0, 1); +SENSOR_TEMPLATE_2(in_max, "in%d_max", S_IRUGO, show_in_reg, NULL, 0, 2); + +static struct sensor_device_template *nct6683_attributes_in_template[] = { + &sensor_dev_template_in_label, + &sensor_dev_template_in_input, + &sensor_dev_template_in_min, + &sensor_dev_template_in_max, + NULL +}; + +static struct sensor_template_group nct6683_in_template_group = { + .templates = nct6683_attributes_in_template, + .is_visible = nct6683_in_is_visible, +}; + +static ssize_t +show_fan(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct nct6683_data *data = nct6683_update_device(dev); + + return sprintf(buf, "%d\n", data->rpm[sattr->index]); +} + +static ssize_t +show_fan_min(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6683_data *data = nct6683_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + + return sprintf(buf, "%d\n", data->fan_min[nr]); +} + +static ssize_t +show_fan_pulses(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct nct6683_data *data = nct6683_update_device(dev); + + return sprintf(buf, "%d\n", + ((data->fanin_cfg[sattr->index] >> 5) & 0x03) + 1); +} + +static umode_t nct6683_fan_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct6683_data *data = dev_get_drvdata(dev); + int fan = index / 3; /* fan index */ + int nr = index % 3; /* attribute index */ + + if (!(data->have_fan & (1 << fan))) + return 0; + + /* + * Intel may have minimum fan speed limits, + * but register location and encoding are unknown. + */ + if (nr == 2 && data->customer_id == NCT6683_CUSTOMER_ID_INTEL) + return 0; + + return attr->mode; +} + +SENSOR_TEMPLATE(fan_input, "fan%d_input", S_IRUGO, show_fan, NULL, 0); +SENSOR_TEMPLATE(fan_pulses, "fan%d_pulses", S_IRUGO, show_fan_pulses, NULL, 0); +SENSOR_TEMPLATE(fan_min, "fan%d_min", S_IRUGO, show_fan_min, NULL, 0); + +/* + * nct6683_fan_is_visible uses the index into the following array + * to determine if attributes should be created or not. + * Any change in order or content must be matched. + */ +static struct sensor_device_template *nct6683_attributes_fan_template[] = { + &sensor_dev_template_fan_input, + &sensor_dev_template_fan_pulses, + &sensor_dev_template_fan_min, + NULL +}; + +static struct sensor_template_group nct6683_fan_template_group = { + .templates = nct6683_attributes_fan_template, + .is_visible = nct6683_fan_is_visible, + .base = 1, +}; + +static ssize_t +show_temp_label(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct nct6683_data *data = nct6683_update_device(dev); + int nr = sattr->index; + + return sprintf(buf, "%s\n", nct6683_mon_label[data->temp_src[nr]]); +} + +static ssize_t +show_temp8(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct6683_data *data = nct6683_update_device(dev); + int index = sattr->index; + int nr = sattr->nr; + + return sprintf(buf, "%d\n", data->temp[index][nr] * 1000); +} + +static ssize_t +show_temp_hyst(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct nct6683_data *data = nct6683_update_device(dev); + int nr = sattr->index; + int temp = data->temp[1][nr] - data->temp[2][nr]; + + return sprintf(buf, "%d\n", temp * 1000); +} + +static ssize_t +show_temp16(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct nct6683_data *data = nct6683_update_device(dev); + int index = sattr->index; + + return sprintf(buf, "%d\n", (data->temp_in[index] / 128) * 500); +} + +/* + * Temperature sensor type is determined by temperature source + * and can not be modified. + * 0x02..0x07: Thermal diode + * 0x08..0x18: Thermistor + * 0x20..0x2b: Intel PECI + * 0x42..0x49: AMD TSI + * Others are unspecified (not visible) + */ + +static int get_temp_type(u8 src) +{ + if (src >= 0x02 && src <= 0x07) + return 3; /* thermal diode */ + else if (src >= 0x08 && src <= 0x18) + return 4; /* thermistor */ + else if (src >= 0x20 && src <= 0x2b) + return 6; /* PECI */ + else if (src >= 0x42 && src <= 0x49) + return 5; + + return 0; +} + +static ssize_t +show_temp_type(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6683_data *data = nct6683_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + return sprintf(buf, "%d\n", get_temp_type(data->temp_src[nr])); +} + +static umode_t nct6683_temp_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct6683_data *data = dev_get_drvdata(dev); + int temp = index / 7; /* temp index */ + int nr = index % 7; /* attribute index */ + + /* + * Intel does not have low temperature limits or temperature hysteresis + * registers, or at least register location and encoding is unknown. + */ + if ((nr == 2 || nr == 4) && + data->customer_id == NCT6683_CUSTOMER_ID_INTEL) + return 0; + + if (nr == 6 && get_temp_type(data->temp_src[temp]) == 0) + return 0; /* type */ + + return attr->mode; +} + +SENSOR_TEMPLATE(temp_input, "temp%d_input", S_IRUGO, show_temp16, NULL, 0); +SENSOR_TEMPLATE(temp_label, "temp%d_label", S_IRUGO, show_temp_label, NULL, 0); +SENSOR_TEMPLATE_2(temp_min, "temp%d_min", S_IRUGO, show_temp8, NULL, 0, 0); +SENSOR_TEMPLATE_2(temp_max, "temp%d_max", S_IRUGO, show_temp8, NULL, 0, 1); +SENSOR_TEMPLATE(temp_max_hyst, "temp%d_max_hyst", S_IRUGO, show_temp_hyst, NULL, + 0); +SENSOR_TEMPLATE_2(temp_crit, "temp%d_crit", S_IRUGO, show_temp8, NULL, 0, 3); +SENSOR_TEMPLATE(temp_type, "temp%d_type", S_IRUGO, show_temp_type, NULL, 0); + +/* + * nct6683_temp_is_visible uses the index into the following array + * to determine if attributes should be created or not. + * Any change in order or content must be matched. + */ +static struct sensor_device_template *nct6683_attributes_temp_template[] = { + &sensor_dev_template_temp_input, + &sensor_dev_template_temp_label, + &sensor_dev_template_temp_min, /* 2 */ + &sensor_dev_template_temp_max, /* 3 */ + &sensor_dev_template_temp_max_hyst, /* 4 */ + &sensor_dev_template_temp_crit, /* 5 */ + &sensor_dev_template_temp_type, /* 6 */ + NULL +}; + +static struct sensor_template_group nct6683_temp_template_group = { + .templates = nct6683_attributes_temp_template, + .is_visible = nct6683_temp_is_visible, + .base = 1, +}; + +static ssize_t +show_pwm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6683_data *data = nct6683_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int index = sattr->index; + + return sprintf(buf, "%d\n", data->pwm[index]); +} + +SENSOR_TEMPLATE(pwm, "pwm%d", S_IRUGO, show_pwm, NULL, 0); + +static umode_t nct6683_pwm_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct6683_data *data = dev_get_drvdata(dev); + int pwm = index; /* pwm index */ + + if (!(data->have_pwm & (1 << pwm))) + return 0; + + return attr->mode; +} + +static struct sensor_device_template *nct6683_attributes_pwm_template[] = { + &sensor_dev_template_pwm, + NULL +}; + +static struct sensor_template_group nct6683_pwm_template_group = { + .templates = nct6683_attributes_pwm_template, + .is_visible = nct6683_pwm_is_visible, + .base = 1, +}; + +static ssize_t +show_global_beep(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6683_data *data = dev_get_drvdata(dev); + int ret; + u8 reg; + + mutex_lock(&data->update_lock); + + ret = superio_enter(data->sioreg); + if (ret) + goto error; + superio_select(data->sioreg, NCT6683_LD_HWM); + reg = superio_inb(data->sioreg, NCT6683_REG_CR_BEEP); + superio_exit(data->sioreg); + + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%u\n", !!(reg & NCT6683_CR_BEEP_MASK)); + +error: + mutex_unlock(&data->update_lock); + return ret; +} + +static ssize_t +store_global_beep(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6683_data *data = dev_get_drvdata(dev); + unsigned long val; + u8 reg; + int ret; + + if (kstrtoul(buf, 10, &val) || (val != 0 && val != 1)) + return -EINVAL; + + mutex_lock(&data->update_lock); + + ret = superio_enter(data->sioreg); + if (ret) { + count = ret; + goto error; + } + + superio_select(data->sioreg, NCT6683_LD_HWM); + reg = superio_inb(data->sioreg, NCT6683_REG_CR_BEEP); + if (val) + reg |= NCT6683_CR_BEEP_MASK; + else + reg &= ~NCT6683_CR_BEEP_MASK; + superio_outb(data->sioreg, NCT6683_REG_CR_BEEP, reg); + superio_exit(data->sioreg); +error: + mutex_unlock(&data->update_lock); + return count; +} + +/* Case open detection */ + +static ssize_t +show_caseopen(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6683_data *data = dev_get_drvdata(dev); + int ret; + u8 reg; + + mutex_lock(&data->update_lock); + + ret = superio_enter(data->sioreg); + if (ret) + goto error; + superio_select(data->sioreg, NCT6683_LD_ACPI); + reg = superio_inb(data->sioreg, NCT6683_REG_CR_CASEOPEN); + superio_exit(data->sioreg); + + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%u\n", !(reg & NCT6683_CR_CASEOPEN_MASK)); + +error: + mutex_unlock(&data->update_lock); + return ret; +} + +static ssize_t +clear_caseopen(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6683_data *data = dev_get_drvdata(dev); + unsigned long val; + u8 reg; + int ret; + + if (kstrtoul(buf, 10, &val) || val != 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + + /* + * Use CR registers to clear caseopen status. + * Caseopen is activ low, clear by writing 1 into the register. + */ + + ret = superio_enter(data->sioreg); + if (ret) { + count = ret; + goto error; + } + + superio_select(data->sioreg, NCT6683_LD_ACPI); + reg = superio_inb(data->sioreg, NCT6683_REG_CR_CASEOPEN); + reg |= NCT6683_CR_CASEOPEN_MASK; + superio_outb(data->sioreg, NCT6683_REG_CR_CASEOPEN, reg); + reg &= ~NCT6683_CR_CASEOPEN_MASK; + superio_outb(data->sioreg, NCT6683_REG_CR_CASEOPEN, reg); + superio_exit(data->sioreg); + + data->valid = false; /* Force cache refresh */ +error: + mutex_unlock(&data->update_lock); + return count; +} + +static DEVICE_ATTR(intrusion0_alarm, S_IWUSR | S_IRUGO, show_caseopen, + clear_caseopen); +static DEVICE_ATTR(beep_enable, S_IWUSR | S_IRUGO, show_global_beep, + store_global_beep); + +static struct attribute *nct6683_attributes_other[] = { + &dev_attr_intrusion0_alarm.attr, + &dev_attr_beep_enable.attr, + NULL +}; + +static const struct attribute_group nct6683_group_other = { + .attrs = nct6683_attributes_other, +}; + +/* Get the monitoring functions started */ +static inline void nct6683_init_device(struct nct6683_data *data) +{ + u8 tmp; + + /* Start hardware monitoring if needed */ + tmp = nct6683_read(data, NCT6683_HWM_CFG); + if (!(tmp & 0x80)) + nct6683_write(data, NCT6683_HWM_CFG, tmp | 0x80); +} + +/* + * There are a total of 24 fan inputs. Each can be configured as input + * or as output. A maximum of 16 inputs and 8 outputs is configurable. + */ +static void +nct6683_setup_fans(struct nct6683_data *data) +{ + int i; + u8 reg; + + for (i = 0; i < NCT6683_NUM_REG_FAN; i++) { + reg = nct6683_read(data, NCT6683_REG_FANIN_CFG(i)); + if (reg & 0x80) + data->have_fan |= 1 << i; + data->fanin_cfg[i] = reg; + } + for (i = 0; i < NCT6683_NUM_REG_PWM; i++) { + reg = nct6683_read(data, NCT6683_REG_FANOUT_CFG(i)); + if (reg & 0x80) + data->have_pwm |= 1 << i; + data->fanout_cfg[i] = reg; + } +} + +/* + * Translation from monitoring register to temperature and voltage attributes + * ========================================================================== + * + * There are a total of 32 monitoring registers. Each can be assigned to either + * a temperature or voltage monitoring source. + * NCT6683_REG_MON_CFG(x) defines assignment for each monitoring source. + * + * Temperature and voltage attribute mapping is determined by walking through + * the NCT6683_REG_MON_CFG registers. If the assigned source is + * a temperature, temp_index[n] is set to the monitor register index, and + * temp_src[n] is set to the temperature source. If the assigned source is + * a voltage, the respective values are stored in in_index[] and in_src[], + * respectively. + */ + +static void nct6683_setup_sensors(struct nct6683_data *data) +{ + u8 reg; + int i; + + data->temp_num = 0; + data->in_num = 0; + for (i = 0; i < NCT6683_NUM_REG_MON; i++) { + reg = nct6683_read(data, NCT6683_REG_MON_CFG(i)) & 0x7f; + /* Ignore invalid assignments */ + if (reg >= NUM_MON_LABELS) + continue; + /* Skip if disabled or reserved */ + if (nct6683_mon_label[reg] == NULL) + continue; + if (reg < MON_VOLTAGE_START) { + data->temp_index[data->temp_num] = i; + data->temp_src[data->temp_num] = reg; + data->temp_num++; + } else { + data->in_index[data->in_num] = i; + data->in_src[data->in_num] = reg; + data->in_num++; + } + } +} + +static int nct6683_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct nct6683_sio_data *sio_data = dev->platform_data; + struct attribute_group *group; + struct nct6683_data *data; + struct device *hwmon_dev; + struct resource *res; + int groups = 0; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!devm_request_region(dev, res->start, IOREGION_LENGTH, DRVNAME)) + return -EBUSY; + + data = devm_kzalloc(dev, sizeof(struct nct6683_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->kind = sio_data->kind; + data->sioreg = sio_data->sioreg; + data->addr = res->start; + mutex_init(&data->update_lock); + platform_set_drvdata(pdev, data); + + data->customer_id = nct6683_read16(data, NCT6683_REG_CUSTOMER_ID); + + nct6683_init_device(data); + nct6683_setup_fans(data); + nct6683_setup_sensors(data); + + /* Register sysfs hooks */ + + if (data->have_pwm) { + group = nct6683_create_attr_group(dev, + &nct6683_pwm_template_group, + fls(data->have_pwm)); + if (IS_ERR(group)) + return PTR_ERR(group); + data->groups[groups++] = group; + } + + if (data->in_num) { + group = nct6683_create_attr_group(dev, + &nct6683_in_template_group, + data->in_num); + if (IS_ERR(group)) + return PTR_ERR(group); + data->groups[groups++] = group; + } + + if (data->have_fan) { + group = nct6683_create_attr_group(dev, + &nct6683_fan_template_group, + fls(data->have_fan)); + if (IS_ERR(group)) + return PTR_ERR(group); + data->groups[groups++] = group; + } + + if (data->temp_num) { + group = nct6683_create_attr_group(dev, + &nct6683_temp_template_group, + data->temp_num); + if (IS_ERR(group)) + return PTR_ERR(group); + data->groups[groups++] = group; + } + data->groups[groups++] = &nct6683_group_other; + + dev_info(dev, "%s EC firmware version %d.%d build %02x/%02x/%02x\n", + nct6683_chip_names[data->kind], + nct6683_read(data, NCT6683_REG_VERSION_HI), + nct6683_read(data, NCT6683_REG_VERSION_LO), + nct6683_read(data, NCT6683_REG_BUILD_MONTH), + nct6683_read(data, NCT6683_REG_BUILD_DAY), + nct6683_read(data, NCT6683_REG_BUILD_YEAR)); + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, + nct6683_device_names[data->kind], data, data->groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +#ifdef CONFIG_PM +static int nct6683_suspend(struct device *dev) +{ + struct nct6683_data *data = nct6683_update_device(dev); + + mutex_lock(&data->update_lock); + data->hwm_cfg = nct6683_read(data, NCT6683_HWM_CFG); + mutex_unlock(&data->update_lock); + + return 0; +} + +static int nct6683_resume(struct device *dev) +{ + struct nct6683_data *data = dev_get_drvdata(dev); + + mutex_lock(&data->update_lock); + + nct6683_write(data, NCT6683_HWM_CFG, data->hwm_cfg); + + /* Force re-reading all values */ + data->valid = false; + mutex_unlock(&data->update_lock); + + return 0; +} + +static const struct dev_pm_ops nct6683_dev_pm_ops = { + .suspend = nct6683_suspend, + .resume = nct6683_resume, + .freeze = nct6683_suspend, + .restore = nct6683_resume, +}; + +#define NCT6683_DEV_PM_OPS (&nct6683_dev_pm_ops) +#else +#define NCT6683_DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + +static struct platform_driver nct6683_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, + .pm = NCT6683_DEV_PM_OPS, + }, + .probe = nct6683_probe, +}; + +static int __init nct6683_find(int sioaddr, struct nct6683_sio_data *sio_data) +{ + const char *board_vendor; + int addr; + u16 val; + int err; + + /* + * Only run on Intel boards unless the 'force' module parameter is set + */ + if (!force) { + board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); + if (!board_vendor || strcmp(board_vendor, "Intel Corporation")) + return -ENODEV; + } + + err = superio_enter(sioaddr); + if (err) + return err; + + val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8) + | superio_inb(sioaddr, SIO_REG_DEVID + 1); + + switch (val & SIO_ID_MASK) { + case SIO_NCT6683_ID: + sio_data->kind = nct6683; + break; + default: + if (val != 0xffff) + pr_debug("unsupported chip ID: 0x%04x\n", val); + goto fail; + } + + /* We have a known chip, find the HWM I/O address */ + superio_select(sioaddr, NCT6683_LD_HWM); + val = (superio_inb(sioaddr, SIO_REG_ADDR) << 8) + | superio_inb(sioaddr, SIO_REG_ADDR + 1); + addr = val & IOREGION_ALIGNMENT; + if (addr == 0) { + pr_err("EC base I/O port unconfigured\n"); + goto fail; + } + + /* Activate logical device if needed */ + val = superio_inb(sioaddr, SIO_REG_ENABLE); + if (!(val & 0x01)) { + pr_err("EC is disabled\n"); + goto fail; + } + + superio_exit(sioaddr); + pr_info("Found %s or compatible chip at %#x:%#x\n", + nct6683_chip_names[sio_data->kind], sioaddr, addr); + sio_data->sioreg = sioaddr; + + return addr; + +fail: + superio_exit(sioaddr); + return -ENODEV; +} + +/* + * when Super-I/O functions move to a separate file, the Super-I/O + * bus will manage the lifetime of the device and this module will only keep + * track of the nct6683 driver. But since we use platform_device_alloc(), we + * must keep track of the device + */ +static struct platform_device *pdev[2]; + +static int __init sensors_nct6683_init(void) +{ + struct nct6683_sio_data sio_data; + int sioaddr[2] = { 0x2e, 0x4e }; + struct resource res; + bool found = false; + int address; + int i, err; + + err = platform_driver_register(&nct6683_driver); + if (err) + return err; + + /* + * initialize sio_data->kind and sio_data->sioreg. + * + * when Super-I/O functions move to a separate file, the Super-I/O + * driver will probe 0x2e and 0x4e and auto-detect the presence of a + * nct6683 hardware monitor, and call probe() + */ + for (i = 0; i < ARRAY_SIZE(pdev); i++) { + address = nct6683_find(sioaddr[i], &sio_data); + if (address <= 0) + continue; + + found = true; + + pdev[i] = platform_device_alloc(DRVNAME, address); + if (!pdev[i]) { + err = -ENOMEM; + goto exit_device_unregister; + } + + err = platform_device_add_data(pdev[i], &sio_data, + sizeof(struct nct6683_sio_data)); + if (err) + goto exit_device_put; + + memset(&res, 0, sizeof(res)); + res.name = DRVNAME; + res.start = address + IOREGION_OFFSET; + res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1; + res.flags = IORESOURCE_IO; + + err = acpi_check_resource_conflict(&res); + if (err) { + platform_device_put(pdev[i]); + pdev[i] = NULL; + continue; + } + + err = platform_device_add_resources(pdev[i], &res, 1); + if (err) + goto exit_device_put; + + /* platform_device_add calls probe() */ + err = platform_device_add(pdev[i]); + if (err) + goto exit_device_put; + } + if (!found) { + err = -ENODEV; + goto exit_unregister; + } + + return 0; + +exit_device_put: + platform_device_put(pdev[i]); +exit_device_unregister: + while (--i >= 0) { + if (pdev[i]) + platform_device_unregister(pdev[i]); + } +exit_unregister: + platform_driver_unregister(&nct6683_driver); + return err; +} + +static void __exit sensors_nct6683_exit(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pdev); i++) { + if (pdev[i]) + platform_device_unregister(pdev[i]); + } + platform_driver_unregister(&nct6683_driver); +} + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("NCT6683D driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_nct6683_init); +module_exit(sensors_nct6683_exit); diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c new file mode 100644 index 00000000000..59d9a3fc96b --- /dev/null +++ b/drivers/hwmon/nct6775.c @@ -0,0 +1,4228 @@ +/* + * nct6775 - Driver for the hardware monitoring functionality of + * Nuvoton NCT677x Super-I/O chips + * + * Copyright (C) 2012 Guenter Roeck <linux@roeck-us.net> + * + * Derived from w83627ehf driver + * Copyright (C) 2005-2012 Jean Delvare <jdelvare@suse.de> + * Copyright (C) 2006 Yuan Mu (Winbond), + * Rudolf Marek <r.marek@assembler.cz> + * David Hubbard <david.c.hubbard@gmail.com> + * Daniel J Blueman <daniel.blueman@gmail.com> + * Copyright (C) 2010 Sheng-Yuan Huang (Nuvoton) (PS00) + * + * Shamelessly ripped from the w83627hf driver + * Copyright (C) 2003 Mark Studebaker + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * Supports the following chips: + * + * Chip #vin #fan #pwm #temp chip IDs man ID + * nct6106d 9 3 3 6+3 0xc450 0xc1 0x5ca3 + * nct6775f 9 4 3 6+3 0xb470 0xc1 0x5ca3 + * nct6776f 9 5 3 6+3 0xc330 0xc1 0x5ca3 + * nct6779d 15 5 5 2+6 0xc560 0xc1 0x5ca3 + * nct6791d 15 6 6 2+6 0xc800 0xc1 0x5ca3 + * + * #temp lists the number of monitored temperature sources (first value) plus + * the number of directly connectable temperature sensors (second value). + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/platform_device.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/hwmon-vid.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/acpi.h> +#include <linux/io.h> +#include "lm75.h" + +#define USE_ALTERNATE + +enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791 }; + +/* used to set data->name = nct6775_device_names[data->sio_kind] */ +static const char * const nct6775_device_names[] = { + "nct6106", + "nct6775", + "nct6776", + "nct6779", + "nct6791", +}; + +static unsigned short force_id; +module_param(force_id, ushort, 0); +MODULE_PARM_DESC(force_id, "Override the detected device ID"); + +static unsigned short fan_debounce; +module_param(fan_debounce, ushort, 0); +MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); + +#define DRVNAME "nct6775" + +/* + * Super-I/O constants and functions + */ + +#define NCT6775_LD_ACPI 0x0a +#define NCT6775_LD_HWM 0x0b +#define NCT6775_LD_VID 0x0d + +#define SIO_REG_LDSEL 0x07 /* Logical device select */ +#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ +#define SIO_REG_ENABLE 0x30 /* Logical device enable */ +#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ + +#define SIO_NCT6106_ID 0xc450 +#define SIO_NCT6775_ID 0xb470 +#define SIO_NCT6776_ID 0xc330 +#define SIO_NCT6779_ID 0xc560 +#define SIO_NCT6791_ID 0xc800 +#define SIO_ID_MASK 0xFFF0 + +enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 }; + +static inline void +superio_outb(int ioreg, int reg, int val) +{ + outb(reg, ioreg); + outb(val, ioreg + 1); +} + +static inline int +superio_inb(int ioreg, int reg) +{ + outb(reg, ioreg); + return inb(ioreg + 1); +} + +static inline void +superio_select(int ioreg, int ld) +{ + outb(SIO_REG_LDSEL, ioreg); + outb(ld, ioreg + 1); +} + +static inline int +superio_enter(int ioreg) +{ + /* + * Try to reserve <ioreg> and <ioreg + 1> for exclusive access. + */ + if (!request_muxed_region(ioreg, 2, DRVNAME)) + return -EBUSY; + + outb(0x87, ioreg); + outb(0x87, ioreg); + + return 0; +} + +static inline void +superio_exit(int ioreg) +{ + outb(0xaa, ioreg); + outb(0x02, ioreg); + outb(0x02, ioreg + 1); + release_region(ioreg, 2); +} + +/* + * ISA constants + */ + +#define IOREGION_ALIGNMENT (~7) +#define IOREGION_OFFSET 5 +#define IOREGION_LENGTH 2 +#define ADDR_REG_OFFSET 0 +#define DATA_REG_OFFSET 1 + +#define NCT6775_REG_BANK 0x4E +#define NCT6775_REG_CONFIG 0x40 + +/* + * Not currently used: + * REG_MAN_ID has the value 0x5ca3 for all supported chips. + * REG_CHIP_ID == 0x88/0xa1/0xc1 depending on chip model. + * REG_MAN_ID is at port 0x4f + * REG_CHIP_ID is at port 0x58 + */ + +#define NUM_TEMP 10 /* Max number of temp attribute sets w/ limits*/ +#define NUM_TEMP_FIXED 6 /* Max number of fixed temp attribute sets */ + +#define NUM_REG_ALARM 7 /* Max number of alarm registers */ +#define NUM_REG_BEEP 5 /* Max number of beep registers */ + +#define NUM_FAN 6 + +/* Common and NCT6775 specific data */ + +/* Voltage min/max registers for nr=7..14 are in bank 5 */ + +static const u16 NCT6775_REG_IN_MAX[] = { + 0x2b, 0x2d, 0x2f, 0x31, 0x33, 0x35, 0x37, 0x554, 0x556, 0x558, 0x55a, + 0x55c, 0x55e, 0x560, 0x562 }; +static const u16 NCT6775_REG_IN_MIN[] = { + 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x555, 0x557, 0x559, 0x55b, + 0x55d, 0x55f, 0x561, 0x563 }; +static const u16 NCT6775_REG_IN[] = { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x550, 0x551, 0x552 +}; + +#define NCT6775_REG_VBAT 0x5D +#define NCT6775_REG_DIODE 0x5E +#define NCT6775_DIODE_MASK 0x02 + +#define NCT6775_REG_FANDIV1 0x506 +#define NCT6775_REG_FANDIV2 0x507 + +#define NCT6775_REG_CR_FAN_DEBOUNCE 0xf0 + +static const u16 NCT6775_REG_ALARM[NUM_REG_ALARM] = { 0x459, 0x45A, 0x45B }; + +/* 0..15 voltages, 16..23 fans, 24..29 temperatures, 30..31 intrusion */ + +static const s8 NCT6775_ALARM_BITS[] = { + 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */ + 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */ + -1, /* unused */ + 6, 7, 11, -1, -1, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ + 12, -1 }; /* intrusion0, intrusion1 */ + +#define FAN_ALARM_BASE 16 +#define TEMP_ALARM_BASE 24 +#define INTRUSION_ALARM_BASE 30 + +static const u16 NCT6775_REG_BEEP[NUM_REG_BEEP] = { 0x56, 0x57, 0x453, 0x4e }; + +/* + * 0..14 voltages, 15 global beep enable, 16..23 fans, 24..29 temperatures, + * 30..31 intrusion + */ +static const s8 NCT6775_BEEP_BITS[] = { + 0, 1, 2, 3, 8, 9, 10, 16, /* in0.. in7 */ + 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */ + 21, /* global beep enable */ + 6, 7, 11, 28, -1, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ + 12, -1 }; /* intrusion0, intrusion1 */ + +#define BEEP_ENABLE_BASE 15 + +static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee }; +static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 }; + +/* DC or PWM output fan configuration */ +static const u8 NCT6775_REG_PWM_MODE[] = { 0x04, 0x04, 0x12 }; +static const u8 NCT6775_PWM_MODE_MASK[] = { 0x01, 0x02, 0x01 }; + +/* Advanced Fan control, some values are common for all fans */ + +static const u16 NCT6775_REG_TARGET[] = { + 0x101, 0x201, 0x301, 0x801, 0x901, 0xa01 }; +static const u16 NCT6775_REG_FAN_MODE[] = { + 0x102, 0x202, 0x302, 0x802, 0x902, 0xa02 }; +static const u16 NCT6775_REG_FAN_STEP_DOWN_TIME[] = { + 0x103, 0x203, 0x303, 0x803, 0x903, 0xa03 }; +static const u16 NCT6775_REG_FAN_STEP_UP_TIME[] = { + 0x104, 0x204, 0x304, 0x804, 0x904, 0xa04 }; +static const u16 NCT6775_REG_FAN_STOP_OUTPUT[] = { + 0x105, 0x205, 0x305, 0x805, 0x905, 0xa05 }; +static const u16 NCT6775_REG_FAN_START_OUTPUT[] = { + 0x106, 0x206, 0x306, 0x806, 0x906, 0xa06 }; +static const u16 NCT6775_REG_FAN_MAX_OUTPUT[] = { 0x10a, 0x20a, 0x30a }; +static const u16 NCT6775_REG_FAN_STEP_OUTPUT[] = { 0x10b, 0x20b, 0x30b }; + +static const u16 NCT6775_REG_FAN_STOP_TIME[] = { + 0x107, 0x207, 0x307, 0x807, 0x907, 0xa07 }; +static const u16 NCT6775_REG_PWM[] = { + 0x109, 0x209, 0x309, 0x809, 0x909, 0xa09 }; +static const u16 NCT6775_REG_PWM_READ[] = { + 0x01, 0x03, 0x11, 0x13, 0x15, 0xa09 }; + +static const u16 NCT6775_REG_FAN[] = { 0x630, 0x632, 0x634, 0x636, 0x638 }; +static const u16 NCT6775_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d }; +static const u16 NCT6775_REG_FAN_PULSES[] = { 0x641, 0x642, 0x643, 0x644, 0 }; +static const u16 NCT6775_FAN_PULSE_SHIFT[] = { 0, 0, 0, 0, 0, 0 }; + +static const u16 NCT6775_REG_TEMP[] = { + 0x27, 0x150, 0x250, 0x62b, 0x62c, 0x62d }; + +static const u16 NCT6775_REG_TEMP_MON[] = { 0x73, 0x75, 0x77 }; + +static const u16 NCT6775_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = { + 0, 0x152, 0x252, 0x628, 0x629, 0x62A }; +static const u16 NCT6775_REG_TEMP_HYST[ARRAY_SIZE(NCT6775_REG_TEMP)] = { + 0x3a, 0x153, 0x253, 0x673, 0x678, 0x67D }; +static const u16 NCT6775_REG_TEMP_OVER[ARRAY_SIZE(NCT6775_REG_TEMP)] = { + 0x39, 0x155, 0x255, 0x672, 0x677, 0x67C }; + +static const u16 NCT6775_REG_TEMP_SOURCE[ARRAY_SIZE(NCT6775_REG_TEMP)] = { + 0x621, 0x622, 0x623, 0x624, 0x625, 0x626 }; + +static const u16 NCT6775_REG_TEMP_SEL[] = { + 0x100, 0x200, 0x300, 0x800, 0x900, 0xa00 }; + +static const u16 NCT6775_REG_WEIGHT_TEMP_SEL[] = { + 0x139, 0x239, 0x339, 0x839, 0x939, 0xa39 }; +static const u16 NCT6775_REG_WEIGHT_TEMP_STEP[] = { + 0x13a, 0x23a, 0x33a, 0x83a, 0x93a, 0xa3a }; +static const u16 NCT6775_REG_WEIGHT_TEMP_STEP_TOL[] = { + 0x13b, 0x23b, 0x33b, 0x83b, 0x93b, 0xa3b }; +static const u16 NCT6775_REG_WEIGHT_DUTY_STEP[] = { + 0x13c, 0x23c, 0x33c, 0x83c, 0x93c, 0xa3c }; +static const u16 NCT6775_REG_WEIGHT_TEMP_BASE[] = { + 0x13d, 0x23d, 0x33d, 0x83d, 0x93d, 0xa3d }; + +static const u16 NCT6775_REG_TEMP_OFFSET[] = { 0x454, 0x455, 0x456 }; + +static const u16 NCT6775_REG_AUTO_TEMP[] = { + 0x121, 0x221, 0x321, 0x821, 0x921, 0xa21 }; +static const u16 NCT6775_REG_AUTO_PWM[] = { + 0x127, 0x227, 0x327, 0x827, 0x927, 0xa27 }; + +#define NCT6775_AUTO_TEMP(data, nr, p) ((data)->REG_AUTO_TEMP[nr] + (p)) +#define NCT6775_AUTO_PWM(data, nr, p) ((data)->REG_AUTO_PWM[nr] + (p)) + +static const u16 NCT6775_REG_CRITICAL_ENAB[] = { 0x134, 0x234, 0x334 }; + +static const u16 NCT6775_REG_CRITICAL_TEMP[] = { + 0x135, 0x235, 0x335, 0x835, 0x935, 0xa35 }; +static const u16 NCT6775_REG_CRITICAL_TEMP_TOLERANCE[] = { + 0x138, 0x238, 0x338, 0x838, 0x938, 0xa38 }; + +static const char *const nct6775_temp_label[] = { + "", + "SYSTIN", + "CPUTIN", + "AUXTIN", + "AMD SB-TSI", + "PECI Agent 0", + "PECI Agent 1", + "PECI Agent 2", + "PECI Agent 3", + "PECI Agent 4", + "PECI Agent 5", + "PECI Agent 6", + "PECI Agent 7", + "PCH_CHIP_CPU_MAX_TEMP", + "PCH_CHIP_TEMP", + "PCH_CPU_TEMP", + "PCH_MCH_TEMP", + "PCH_DIM0_TEMP", + "PCH_DIM1_TEMP", + "PCH_DIM2_TEMP", + "PCH_DIM3_TEMP" +}; + +static const u16 NCT6775_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6775_temp_label) - 1] + = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x661, 0x662, 0x664 }; + +static const u16 NCT6775_REG_TEMP_CRIT[ARRAY_SIZE(nct6775_temp_label) - 1] + = { 0, 0, 0, 0, 0xa00, 0xa01, 0xa02, 0xa03, 0xa04, 0xa05, 0xa06, + 0xa07 }; + +/* NCT6776 specific data */ + +static const s8 NCT6776_ALARM_BITS[] = { + 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */ + 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */ + -1, /* unused */ + 6, 7, 11, 10, 23, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ + 12, 9 }; /* intrusion0, intrusion1 */ + +static const u16 NCT6776_REG_BEEP[NUM_REG_BEEP] = { 0xb2, 0xb3, 0xb4, 0xb5 }; + +static const s8 NCT6776_BEEP_BITS[] = { + 0, 1, 2, 3, 4, 5, 6, 7, /* in0.. in7 */ + 8, -1, -1, -1, -1, -1, -1, /* in8..in14 */ + 24, /* global beep enable */ + 25, 26, 27, 28, 29, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 16, 17, 18, 19, 20, 21, /* temp1..temp6 */ + 30, 31 }; /* intrusion0, intrusion1 */ + +static const u16 NCT6776_REG_TOLERANCE_H[] = { + 0x10c, 0x20c, 0x30c, 0x80c, 0x90c, 0xa0c }; + +static const u8 NCT6776_REG_PWM_MODE[] = { 0x04, 0, 0, 0, 0, 0 }; +static const u8 NCT6776_PWM_MODE_MASK[] = { 0x01, 0, 0, 0, 0, 0 }; + +static const u16 NCT6776_REG_FAN_MIN[] = { 0x63a, 0x63c, 0x63e, 0x640, 0x642 }; +static const u16 NCT6776_REG_FAN_PULSES[] = { 0x644, 0x645, 0x646, 0, 0 }; + +static const u16 NCT6776_REG_WEIGHT_DUTY_BASE[] = { + 0x13e, 0x23e, 0x33e, 0x83e, 0x93e, 0xa3e }; + +static const u16 NCT6776_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = { + 0x18, 0x152, 0x252, 0x628, 0x629, 0x62A }; + +static const char *const nct6776_temp_label[] = { + "", + "SYSTIN", + "CPUTIN", + "AUXTIN", + "SMBUSMASTER 0", + "SMBUSMASTER 1", + "SMBUSMASTER 2", + "SMBUSMASTER 3", + "SMBUSMASTER 4", + "SMBUSMASTER 5", + "SMBUSMASTER 6", + "SMBUSMASTER 7", + "PECI Agent 0", + "PECI Agent 1", + "PCH_CHIP_CPU_MAX_TEMP", + "PCH_CHIP_TEMP", + "PCH_CPU_TEMP", + "PCH_MCH_TEMP", + "PCH_DIM0_TEMP", + "PCH_DIM1_TEMP", + "PCH_DIM2_TEMP", + "PCH_DIM3_TEMP", + "BYTE_TEMP" +}; + +static const u16 NCT6776_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6776_temp_label) - 1] + = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x401, 0x402, 0x404 }; + +static const u16 NCT6776_REG_TEMP_CRIT[ARRAY_SIZE(nct6776_temp_label) - 1] + = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a }; + +/* NCT6779 specific data */ + +static const u16 NCT6779_REG_IN[] = { + 0x480, 0x481, 0x482, 0x483, 0x484, 0x485, 0x486, 0x487, + 0x488, 0x489, 0x48a, 0x48b, 0x48c, 0x48d, 0x48e }; + +static const u16 NCT6779_REG_ALARM[NUM_REG_ALARM] = { + 0x459, 0x45A, 0x45B, 0x568 }; + +static const s8 NCT6779_ALARM_BITS[] = { + 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */ + 17, 24, 25, 26, 27, 28, 29, /* in8..in14 */ + -1, /* unused */ + 6, 7, 11, 10, 23, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ + 12, 9 }; /* intrusion0, intrusion1 */ + +static const s8 NCT6779_BEEP_BITS[] = { + 0, 1, 2, 3, 4, 5, 6, 7, /* in0.. in7 */ + 8, 9, 10, 11, 12, 13, 14, /* in8..in14 */ + 24, /* global beep enable */ + 25, 26, 27, 28, 29, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 16, 17, -1, -1, -1, -1, /* temp1..temp6 */ + 30, 31 }; /* intrusion0, intrusion1 */ + +static const u16 NCT6779_REG_FAN[] = { + 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8, 0x4ba }; +static const u16 NCT6779_REG_FAN_PULSES[] = { + 0x644, 0x645, 0x646, 0x647, 0x648, 0x649 }; + +static const u16 NCT6779_REG_CRITICAL_PWM_ENABLE[] = { + 0x136, 0x236, 0x336, 0x836, 0x936, 0xa36 }; +#define NCT6779_CRITICAL_PWM_ENABLE_MASK 0x01 +static const u16 NCT6779_REG_CRITICAL_PWM[] = { + 0x137, 0x237, 0x337, 0x837, 0x937, 0xa37 }; + +static const u16 NCT6779_REG_TEMP[] = { 0x27, 0x150 }; +static const u16 NCT6779_REG_TEMP_MON[] = { 0x73, 0x75, 0x77, 0x79, 0x7b }; +static const u16 NCT6779_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6779_REG_TEMP)] = { + 0x18, 0x152 }; +static const u16 NCT6779_REG_TEMP_HYST[ARRAY_SIZE(NCT6779_REG_TEMP)] = { + 0x3a, 0x153 }; +static const u16 NCT6779_REG_TEMP_OVER[ARRAY_SIZE(NCT6779_REG_TEMP)] = { + 0x39, 0x155 }; + +static const u16 NCT6779_REG_TEMP_OFFSET[] = { + 0x454, 0x455, 0x456, 0x44a, 0x44b, 0x44c }; + +static const char *const nct6779_temp_label[] = { + "", + "SYSTIN", + "CPUTIN", + "AUXTIN0", + "AUXTIN1", + "AUXTIN2", + "AUXTIN3", + "", + "SMBUSMASTER 0", + "SMBUSMASTER 1", + "SMBUSMASTER 2", + "SMBUSMASTER 3", + "SMBUSMASTER 4", + "SMBUSMASTER 5", + "SMBUSMASTER 6", + "SMBUSMASTER 7", + "PECI Agent 0", + "PECI Agent 1", + "PCH_CHIP_CPU_MAX_TEMP", + "PCH_CHIP_TEMP", + "PCH_CPU_TEMP", + "PCH_MCH_TEMP", + "PCH_DIM0_TEMP", + "PCH_DIM1_TEMP", + "PCH_DIM2_TEMP", + "PCH_DIM3_TEMP", + "BYTE_TEMP" +}; + +static const u16 NCT6779_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6779_temp_label) - 1] + = { 0x490, 0x491, 0x492, 0x493, 0x494, 0x495, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0x400, 0x401, 0x402, 0x404, 0x405, 0x406, 0x407, + 0x408, 0 }; + +static const u16 NCT6779_REG_TEMP_CRIT[ARRAY_SIZE(nct6779_temp_label) - 1] + = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a }; + +/* NCT6791 specific data */ + +#define NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE 0x28 + +static const u16 NCT6791_REG_WEIGHT_TEMP_SEL[6] = { 0, 0x239 }; +static const u16 NCT6791_REG_WEIGHT_TEMP_STEP[6] = { 0, 0x23a }; +static const u16 NCT6791_REG_WEIGHT_TEMP_STEP_TOL[6] = { 0, 0x23b }; +static const u16 NCT6791_REG_WEIGHT_DUTY_STEP[6] = { 0, 0x23c }; +static const u16 NCT6791_REG_WEIGHT_TEMP_BASE[6] = { 0, 0x23d }; +static const u16 NCT6791_REG_WEIGHT_DUTY_BASE[6] = { 0, 0x23e }; + +static const u16 NCT6791_REG_ALARM[NUM_REG_ALARM] = { + 0x459, 0x45A, 0x45B, 0x568, 0x45D }; + +static const s8 NCT6791_ALARM_BITS[] = { + 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */ + 17, 24, 25, 26, 27, 28, 29, /* in8..in14 */ + -1, /* unused */ + 6, 7, 11, 10, 23, 33, /* fan1..fan6 */ + -1, -1, /* unused */ + 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ + 12, 9 }; /* intrusion0, intrusion1 */ + + +/* NCT6102D/NCT6106D specific data */ + +#define NCT6106_REG_VBAT 0x318 +#define NCT6106_REG_DIODE 0x319 +#define NCT6106_DIODE_MASK 0x01 + +static const u16 NCT6106_REG_IN_MAX[] = { + 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9e, 0xa0, 0xa2 }; +static const u16 NCT6106_REG_IN_MIN[] = { + 0x91, 0x93, 0x95, 0x97, 0x99, 0x9b, 0x9f, 0xa1, 0xa3 }; +static const u16 NCT6106_REG_IN[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x07, 0x08, 0x09 }; + +static const u16 NCT6106_REG_TEMP[] = { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 }; +static const u16 NCT6106_REG_TEMP_MON[] = { 0x18, 0x19, 0x1a }; +static const u16 NCT6106_REG_TEMP_HYST[] = { + 0xc3, 0xc7, 0xcb, 0xcf, 0xd3, 0xd7 }; +static const u16 NCT6106_REG_TEMP_OVER[] = { + 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd6 }; +static const u16 NCT6106_REG_TEMP_CRIT_L[] = { + 0xc0, 0xc4, 0xc8, 0xcc, 0xd0, 0xd4 }; +static const u16 NCT6106_REG_TEMP_CRIT_H[] = { + 0xc1, 0xc5, 0xc9, 0xcf, 0xd1, 0xd5 }; +static const u16 NCT6106_REG_TEMP_OFFSET[] = { 0x311, 0x312, 0x313 }; +static const u16 NCT6106_REG_TEMP_CONFIG[] = { + 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc }; + +static const u16 NCT6106_REG_FAN[] = { 0x20, 0x22, 0x24 }; +static const u16 NCT6106_REG_FAN_MIN[] = { 0xe0, 0xe2, 0xe4 }; +static const u16 NCT6106_REG_FAN_PULSES[] = { 0xf6, 0xf6, 0xf6, 0, 0 }; +static const u16 NCT6106_FAN_PULSE_SHIFT[] = { 0, 2, 4, 0, 0 }; + +static const u8 NCT6106_REG_PWM_MODE[] = { 0xf3, 0xf3, 0xf3 }; +static const u8 NCT6106_PWM_MODE_MASK[] = { 0x01, 0x02, 0x04 }; +static const u16 NCT6106_REG_PWM[] = { 0x119, 0x129, 0x139 }; +static const u16 NCT6106_REG_PWM_READ[] = { 0x4a, 0x4b, 0x4c }; +static const u16 NCT6106_REG_FAN_MODE[] = { 0x113, 0x123, 0x133 }; +static const u16 NCT6106_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130 }; +static const u16 NCT6106_REG_TEMP_SOURCE[] = { + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5 }; + +static const u16 NCT6106_REG_CRITICAL_TEMP[] = { 0x11a, 0x12a, 0x13a }; +static const u16 NCT6106_REG_CRITICAL_TEMP_TOLERANCE[] = { + 0x11b, 0x12b, 0x13b }; + +static const u16 NCT6106_REG_CRITICAL_PWM_ENABLE[] = { 0x11c, 0x12c, 0x13c }; +#define NCT6106_CRITICAL_PWM_ENABLE_MASK 0x10 +static const u16 NCT6106_REG_CRITICAL_PWM[] = { 0x11d, 0x12d, 0x13d }; + +static const u16 NCT6106_REG_FAN_STEP_UP_TIME[] = { 0x114, 0x124, 0x134 }; +static const u16 NCT6106_REG_FAN_STEP_DOWN_TIME[] = { 0x115, 0x125, 0x135 }; +static const u16 NCT6106_REG_FAN_STOP_OUTPUT[] = { 0x116, 0x126, 0x136 }; +static const u16 NCT6106_REG_FAN_START_OUTPUT[] = { 0x117, 0x127, 0x137 }; +static const u16 NCT6106_REG_FAN_STOP_TIME[] = { 0x118, 0x128, 0x138 }; +static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 }; + +static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 }; + +static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 }; +static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 }; +static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a }; +static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x17c }; +static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c }; +static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d }; + +static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 }; +static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 }; + +static const u16 NCT6106_REG_ALARM[NUM_REG_ALARM] = { + 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d }; + +static const s8 NCT6106_ALARM_BITS[] = { + 0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */ + 9, -1, -1, -1, -1, -1, -1, /* in8..in14 */ + -1, /* unused */ + 32, 33, 34, -1, -1, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 16, 17, 18, 19, 20, 21, /* temp1..temp6 */ + 48, -1 /* intrusion0, intrusion1 */ +}; + +static const u16 NCT6106_REG_BEEP[NUM_REG_BEEP] = { + 0x3c0, 0x3c1, 0x3c2, 0x3c3, 0x3c4 }; + +static const s8 NCT6106_BEEP_BITS[] = { + 0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */ + 9, 10, 11, 12, -1, -1, -1, /* in8..in14 */ + 32, /* global beep enable */ + 24, 25, 26, 27, 28, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 16, 17, 18, 19, 20, 21, /* temp1..temp6 */ + 34, -1 /* intrusion0, intrusion1 */ +}; + +static const u16 NCT6106_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6776_temp_label) - 1] + = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x51, 0x52, 0x54 }; + +static const u16 NCT6106_REG_TEMP_CRIT[ARRAY_SIZE(nct6776_temp_label) - 1] + = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x204, 0x205 }; + +static enum pwm_enable reg_to_pwm_enable(int pwm, int mode) +{ + if (mode == 0 && pwm == 255) + return off; + return mode + 1; +} + +static int pwm_enable_to_reg(enum pwm_enable mode) +{ + if (mode == off) + return 0; + return mode - 1; +} + +/* + * Conversions + */ + +/* 1 is DC mode, output in ms */ +static unsigned int step_time_from_reg(u8 reg, u8 mode) +{ + return mode ? 400 * reg : 100 * reg; +} + +static u8 step_time_to_reg(unsigned int msec, u8 mode) +{ + return clamp_val((mode ? (msec + 200) / 400 : + (msec + 50) / 100), 1, 255); +} + +static unsigned int fan_from_reg8(u16 reg, unsigned int divreg) +{ + if (reg == 0 || reg == 255) + return 0; + return 1350000U / (reg << divreg); +} + +static unsigned int fan_from_reg13(u16 reg, unsigned int divreg) +{ + if ((reg & 0xff1f) == 0xff1f) + return 0; + + reg = (reg & 0x1f) | ((reg & 0xff00) >> 3); + + if (reg == 0) + return 0; + + return 1350000U / reg; +} + +static unsigned int fan_from_reg16(u16 reg, unsigned int divreg) +{ + if (reg == 0 || reg == 0xffff) + return 0; + + /* + * Even though the registers are 16 bit wide, the fan divisor + * still applies. + */ + return 1350000U / (reg << divreg); +} + +static u16 fan_to_reg(u32 fan, unsigned int divreg) +{ + if (!fan) + return 0; + + return (1350000U / fan) >> divreg; +} + +static inline unsigned int +div_from_reg(u8 reg) +{ + return 1 << reg; +} + +/* + * Some of the voltage inputs have internal scaling, the tables below + * contain 8 (the ADC LSB in mV) * scaling factor * 100 + */ +static const u16 scale_in[15] = { + 800, 800, 1600, 1600, 800, 800, 800, 1600, 1600, 800, 800, 800, 800, + 800, 800 +}; + +static inline long in_from_reg(u8 reg, u8 nr) +{ + return DIV_ROUND_CLOSEST(reg * scale_in[nr], 100); +} + +static inline u8 in_to_reg(u32 val, u8 nr) +{ + return clamp_val(DIV_ROUND_CLOSEST(val * 100, scale_in[nr]), 0, 255); +} + +/* + * Data structures and manipulation thereof + */ + +struct nct6775_data { + int addr; /* IO base of hw monitor block */ + int sioreg; /* SIO register address */ + enum kinds kind; + const char *name; + + int num_attr_groups; + const struct attribute_group *groups[6]; + + u16 reg_temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst, + * 3=temp_crit, 4=temp_lcrit + */ + u8 temp_src[NUM_TEMP]; + u16 reg_temp_config[NUM_TEMP]; + const char * const *temp_label; + int temp_label_num; + + u16 REG_CONFIG; + u16 REG_VBAT; + u16 REG_DIODE; + u8 DIODE_MASK; + + const s8 *ALARM_BITS; + const s8 *BEEP_BITS; + + const u16 *REG_VIN; + const u16 *REG_IN_MINMAX[2]; + + const u16 *REG_TARGET; + const u16 *REG_FAN; + const u16 *REG_FAN_MODE; + const u16 *REG_FAN_MIN; + const u16 *REG_FAN_PULSES; + const u16 *FAN_PULSE_SHIFT; + const u16 *REG_FAN_TIME[3]; + + const u16 *REG_TOLERANCE_H; + + const u8 *REG_PWM_MODE; + const u8 *PWM_MODE_MASK; + + const u16 *REG_PWM[7]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor, + * [3]=pwm_max, [4]=pwm_step, + * [5]=weight_duty_step, [6]=weight_duty_base + */ + const u16 *REG_PWM_READ; + + const u16 *REG_CRITICAL_PWM_ENABLE; + u8 CRITICAL_PWM_ENABLE_MASK; + const u16 *REG_CRITICAL_PWM; + + const u16 *REG_AUTO_TEMP; + const u16 *REG_AUTO_PWM; + + const u16 *REG_CRITICAL_TEMP; + const u16 *REG_CRITICAL_TEMP_TOLERANCE; + + const u16 *REG_TEMP_SOURCE; /* temp register sources */ + const u16 *REG_TEMP_SEL; + const u16 *REG_WEIGHT_TEMP_SEL; + const u16 *REG_WEIGHT_TEMP[3]; /* 0=base, 1=tolerance, 2=step */ + + const u16 *REG_TEMP_OFFSET; + + const u16 *REG_ALARM; + const u16 *REG_BEEP; + + unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg); + unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg); + + struct mutex update_lock; + bool valid; /* true if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + /* Register values */ + u8 bank; /* current register bank */ + u8 in_num; /* number of in inputs we have */ + u8 in[15][3]; /* [0]=in, [1]=in_max, [2]=in_min */ + unsigned int rpm[NUM_FAN]; + u16 fan_min[NUM_FAN]; + u8 fan_pulses[NUM_FAN]; + u8 fan_div[NUM_FAN]; + u8 has_pwm; + u8 has_fan; /* some fan inputs can be disabled */ + u8 has_fan_min; /* some fans don't have min register */ + bool has_fan_div; + + u8 num_temp_alarms; /* 2, 3, or 6 */ + u8 num_temp_beeps; /* 2, 3, or 6 */ + u8 temp_fixed_num; /* 3 or 6 */ + u8 temp_type[NUM_TEMP_FIXED]; + s8 temp_offset[NUM_TEMP_FIXED]; + s16 temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst, + * 3=temp_crit, 4=temp_lcrit */ + u64 alarms; + u64 beeps; + + u8 pwm_num; /* number of pwm */ + u8 pwm_mode[NUM_FAN]; /* 1->DC variable voltage, + * 0->PWM variable duty cycle + */ + enum pwm_enable pwm_enable[NUM_FAN]; + /* 0->off + * 1->manual + * 2->thermal cruise mode (also called SmartFan I) + * 3->fan speed cruise mode + * 4->SmartFan III + * 5->enhanced variable thermal cruise (SmartFan IV) + */ + u8 pwm[7][NUM_FAN]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor, + * [3]=pwm_max, [4]=pwm_step, + * [5]=weight_duty_step, [6]=weight_duty_base + */ + + u8 target_temp[NUM_FAN]; + u8 target_temp_mask; + u32 target_speed[NUM_FAN]; + u32 target_speed_tolerance[NUM_FAN]; + u8 speed_tolerance_limit; + + u8 temp_tolerance[2][NUM_FAN]; + u8 tolerance_mask; + + u8 fan_time[3][NUM_FAN]; /* 0 = stop_time, 1 = step_up, 2 = step_down */ + + /* Automatic fan speed control registers */ + int auto_pwm_num; + u8 auto_pwm[NUM_FAN][7]; + u8 auto_temp[NUM_FAN][7]; + u8 pwm_temp_sel[NUM_FAN]; + u8 pwm_weight_temp_sel[NUM_FAN]; + u8 weight_temp[3][NUM_FAN]; /* 0->temp_step, 1->temp_step_tol, + * 2->temp_base + */ + + u8 vid; + u8 vrm; + + bool have_vid; + + u16 have_temp; + u16 have_temp_fixed; + u16 have_in; +#ifdef CONFIG_PM + /* Remember extra register values over suspend/resume */ + u8 vbat; + u8 fandiv1; + u8 fandiv2; +#endif +}; + +struct nct6775_sio_data { + int sioreg; + enum kinds kind; +}; + +struct sensor_device_template { + struct device_attribute dev_attr; + union { + struct { + u8 nr; + u8 index; + } s; + int index; + } u; + bool s2; /* true if both index and nr are used */ +}; + +struct sensor_device_attr_u { + union { + struct sensor_device_attribute a1; + struct sensor_device_attribute_2 a2; + } u; + char name[32]; +}; + +#define __TEMPLATE_ATTR(_template, _mode, _show, _store) { \ + .attr = {.name = _template, .mode = _mode }, \ + .show = _show, \ + .store = _store, \ +} + +#define SENSOR_DEVICE_TEMPLATE(_template, _mode, _show, _store, _index) \ + { .dev_attr = __TEMPLATE_ATTR(_template, _mode, _show, _store), \ + .u.index = _index, \ + .s2 = false } + +#define SENSOR_DEVICE_TEMPLATE_2(_template, _mode, _show, _store, \ + _nr, _index) \ + { .dev_attr = __TEMPLATE_ATTR(_template, _mode, _show, _store), \ + .u.s.index = _index, \ + .u.s.nr = _nr, \ + .s2 = true } + +#define SENSOR_TEMPLATE(_name, _template, _mode, _show, _store, _index) \ +static struct sensor_device_template sensor_dev_template_##_name \ + = SENSOR_DEVICE_TEMPLATE(_template, _mode, _show, _store, \ + _index) + +#define SENSOR_TEMPLATE_2(_name, _template, _mode, _show, _store, \ + _nr, _index) \ +static struct sensor_device_template sensor_dev_template_##_name \ + = SENSOR_DEVICE_TEMPLATE_2(_template, _mode, _show, _store, \ + _nr, _index) + +struct sensor_template_group { + struct sensor_device_template **templates; + umode_t (*is_visible)(struct kobject *, struct attribute *, int); + int base; +}; + +static struct attribute_group * +nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg, + int repeat) +{ + struct attribute_group *group; + struct sensor_device_attr_u *su; + struct sensor_device_attribute *a; + struct sensor_device_attribute_2 *a2; + struct attribute **attrs; + struct sensor_device_template **t; + int i, count; + + if (repeat <= 0) + return ERR_PTR(-EINVAL); + + t = tg->templates; + for (count = 0; *t; t++, count++) + ; + + if (count == 0) + return ERR_PTR(-EINVAL); + + group = devm_kzalloc(dev, sizeof(*group), GFP_KERNEL); + if (group == NULL) + return ERR_PTR(-ENOMEM); + + attrs = devm_kzalloc(dev, sizeof(*attrs) * (repeat * count + 1), + GFP_KERNEL); + if (attrs == NULL) + return ERR_PTR(-ENOMEM); + + su = devm_kzalloc(dev, sizeof(*su) * repeat * count, + GFP_KERNEL); + if (su == NULL) + return ERR_PTR(-ENOMEM); + + group->attrs = attrs; + group->is_visible = tg->is_visible; + + for (i = 0; i < repeat; i++) { + t = tg->templates; + while (*t != NULL) { + snprintf(su->name, sizeof(su->name), + (*t)->dev_attr.attr.name, tg->base + i); + if ((*t)->s2) { + a2 = &su->u.a2; + a2->dev_attr.attr.name = su->name; + a2->nr = (*t)->u.s.nr + i; + a2->index = (*t)->u.s.index; + a2->dev_attr.attr.mode = + (*t)->dev_attr.attr.mode; + a2->dev_attr.show = (*t)->dev_attr.show; + a2->dev_attr.store = (*t)->dev_attr.store; + *attrs = &a2->dev_attr.attr; + } else { + a = &su->u.a1; + a->dev_attr.attr.name = su->name; + a->index = (*t)->u.index + i; + a->dev_attr.attr.mode = + (*t)->dev_attr.attr.mode; + a->dev_attr.show = (*t)->dev_attr.show; + a->dev_attr.store = (*t)->dev_attr.store; + *attrs = &a->dev_attr.attr; + } + attrs++; + su++; + t++; + } + } + + return group; +} + +static bool is_word_sized(struct nct6775_data *data, u16 reg) +{ + switch (data->kind) { + case nct6106: + return reg == 0x20 || reg == 0x22 || reg == 0x24 || + reg == 0xe0 || reg == 0xe2 || reg == 0xe4 || + reg == 0x111 || reg == 0x121 || reg == 0x131; + case nct6775: + return (((reg & 0xff00) == 0x100 || + (reg & 0xff00) == 0x200) && + ((reg & 0x00ff) == 0x50 || + (reg & 0x00ff) == 0x53 || + (reg & 0x00ff) == 0x55)) || + (reg & 0xfff0) == 0x630 || + reg == 0x640 || reg == 0x642 || + reg == 0x662 || + ((reg & 0xfff0) == 0x650 && (reg & 0x000f) >= 0x06) || + reg == 0x73 || reg == 0x75 || reg == 0x77; + case nct6776: + return (((reg & 0xff00) == 0x100 || + (reg & 0xff00) == 0x200) && + ((reg & 0x00ff) == 0x50 || + (reg & 0x00ff) == 0x53 || + (reg & 0x00ff) == 0x55)) || + (reg & 0xfff0) == 0x630 || + reg == 0x402 || + reg == 0x640 || reg == 0x642 || + ((reg & 0xfff0) == 0x650 && (reg & 0x000f) >= 0x06) || + reg == 0x73 || reg == 0x75 || reg == 0x77; + case nct6779: + case nct6791: + return reg == 0x150 || reg == 0x153 || reg == 0x155 || + ((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x0b) || + reg == 0x402 || + reg == 0x63a || reg == 0x63c || reg == 0x63e || + reg == 0x640 || reg == 0x642 || + reg == 0x73 || reg == 0x75 || reg == 0x77 || reg == 0x79 || + reg == 0x7b; + } + return false; +} + +/* + * On older chips, only registers 0x50-0x5f are banked. + * On more recent chips, all registers are banked. + * Assume that is the case and set the bank number for each access. + * Cache the bank number so it only needs to be set if it changes. + */ +static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg) +{ + u8 bank = reg >> 8; + if (data->bank != bank) { + outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET); + outb_p(bank, data->addr + DATA_REG_OFFSET); + data->bank = bank; + } +} + +static u16 nct6775_read_value(struct nct6775_data *data, u16 reg) +{ + int res, word_sized = is_word_sized(data, reg); + + nct6775_set_bank(data, reg); + outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET); + res = inb_p(data->addr + DATA_REG_OFFSET); + if (word_sized) { + outb_p((reg & 0xff) + 1, + data->addr + ADDR_REG_OFFSET); + res = (res << 8) + inb_p(data->addr + DATA_REG_OFFSET); + } + return res; +} + +static int nct6775_write_value(struct nct6775_data *data, u16 reg, u16 value) +{ + int word_sized = is_word_sized(data, reg); + + nct6775_set_bank(data, reg); + outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET); + if (word_sized) { + outb_p(value >> 8, data->addr + DATA_REG_OFFSET); + outb_p((reg & 0xff) + 1, + data->addr + ADDR_REG_OFFSET); + } + outb_p(value & 0xff, data->addr + DATA_REG_OFFSET); + return 0; +} + +/* We left-align 8-bit temperature values to make the code simpler */ +static u16 nct6775_read_temp(struct nct6775_data *data, u16 reg) +{ + u16 res; + + res = nct6775_read_value(data, reg); + if (!is_word_sized(data, reg)) + res <<= 8; + + return res; +} + +static int nct6775_write_temp(struct nct6775_data *data, u16 reg, u16 value) +{ + if (!is_word_sized(data, reg)) + value >>= 8; + return nct6775_write_value(data, reg, value); +} + +/* This function assumes that the caller holds data->update_lock */ +static void nct6775_write_fan_div(struct nct6775_data *data, int nr) +{ + u8 reg; + + switch (nr) { + case 0: + reg = (nct6775_read_value(data, NCT6775_REG_FANDIV1) & 0x70) + | (data->fan_div[0] & 0x7); + nct6775_write_value(data, NCT6775_REG_FANDIV1, reg); + break; + case 1: + reg = (nct6775_read_value(data, NCT6775_REG_FANDIV1) & 0x7) + | ((data->fan_div[1] << 4) & 0x70); + nct6775_write_value(data, NCT6775_REG_FANDIV1, reg); + break; + case 2: + reg = (nct6775_read_value(data, NCT6775_REG_FANDIV2) & 0x70) + | (data->fan_div[2] & 0x7); + nct6775_write_value(data, NCT6775_REG_FANDIV2, reg); + break; + case 3: + reg = (nct6775_read_value(data, NCT6775_REG_FANDIV2) & 0x7) + | ((data->fan_div[3] << 4) & 0x70); + nct6775_write_value(data, NCT6775_REG_FANDIV2, reg); + break; + } +} + +static void nct6775_write_fan_div_common(struct nct6775_data *data, int nr) +{ + if (data->kind == nct6775) + nct6775_write_fan_div(data, nr); +} + +static void nct6775_update_fan_div(struct nct6775_data *data) +{ + u8 i; + + i = nct6775_read_value(data, NCT6775_REG_FANDIV1); + data->fan_div[0] = i & 0x7; + data->fan_div[1] = (i & 0x70) >> 4; + i = nct6775_read_value(data, NCT6775_REG_FANDIV2); + data->fan_div[2] = i & 0x7; + if (data->has_fan & (1 << 3)) + data->fan_div[3] = (i & 0x70) >> 4; +} + +static void nct6775_update_fan_div_common(struct nct6775_data *data) +{ + if (data->kind == nct6775) + nct6775_update_fan_div(data); +} + +static void nct6775_init_fan_div(struct nct6775_data *data) +{ + int i; + + nct6775_update_fan_div_common(data); + /* + * For all fans, start with highest divider value if the divider + * register is not initialized. This ensures that we get a + * reading from the fan count register, even if it is not optimal. + * We'll compute a better divider later on. + */ + for (i = 0; i < ARRAY_SIZE(data->fan_div); i++) { + if (!(data->has_fan & (1 << i))) + continue; + if (data->fan_div[i] == 0) { + data->fan_div[i] = 7; + nct6775_write_fan_div_common(data, i); + } + } +} + +static void nct6775_init_fan_common(struct device *dev, + struct nct6775_data *data) +{ + int i; + u8 reg; + + if (data->has_fan_div) + nct6775_init_fan_div(data); + + /* + * If fan_min is not set (0), set it to 0xff to disable it. This + * prevents the unnecessary warning when fanX_min is reported as 0. + */ + for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) { + if (data->has_fan_min & (1 << i)) { + reg = nct6775_read_value(data, data->REG_FAN_MIN[i]); + if (!reg) + nct6775_write_value(data, data->REG_FAN_MIN[i], + data->has_fan_div ? 0xff + : 0xff1f); + } + } +} + +static void nct6775_select_fan_div(struct device *dev, + struct nct6775_data *data, int nr, u16 reg) +{ + u8 fan_div = data->fan_div[nr]; + u16 fan_min; + + if (!data->has_fan_div) + return; + + /* + * If we failed to measure the fan speed, or the reported value is not + * in the optimal range, and the clock divider can be modified, + * let's try that for next time. + */ + if (reg == 0x00 && fan_div < 0x07) + fan_div++; + else if (reg != 0x00 && reg < 0x30 && fan_div > 0) + fan_div--; + + if (fan_div != data->fan_div[nr]) { + dev_dbg(dev, "Modifying fan%d clock divider from %u to %u\n", + nr + 1, div_from_reg(data->fan_div[nr]), + div_from_reg(fan_div)); + + /* Preserve min limit if possible */ + if (data->has_fan_min & (1 << nr)) { + fan_min = data->fan_min[nr]; + if (fan_div > data->fan_div[nr]) { + if (fan_min != 255 && fan_min > 1) + fan_min >>= 1; + } else { + if (fan_min != 255) { + fan_min <<= 1; + if (fan_min > 254) + fan_min = 254; + } + } + if (fan_min != data->fan_min[nr]) { + data->fan_min[nr] = fan_min; + nct6775_write_value(data, data->REG_FAN_MIN[nr], + fan_min); + } + } + data->fan_div[nr] = fan_div; + nct6775_write_fan_div_common(data, nr); + } +} + +static void nct6775_update_pwm(struct device *dev) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + int i, j; + int fanmodecfg, reg; + bool duty_is_dc; + + for (i = 0; i < data->pwm_num; i++) { + if (!(data->has_pwm & (1 << i))) + continue; + + duty_is_dc = data->REG_PWM_MODE[i] && + (nct6775_read_value(data, data->REG_PWM_MODE[i]) + & data->PWM_MODE_MASK[i]); + data->pwm_mode[i] = duty_is_dc; + + fanmodecfg = nct6775_read_value(data, data->REG_FAN_MODE[i]); + for (j = 0; j < ARRAY_SIZE(data->REG_PWM); j++) { + if (data->REG_PWM[j] && data->REG_PWM[j][i]) { + data->pwm[j][i] + = nct6775_read_value(data, + data->REG_PWM[j][i]); + } + } + + data->pwm_enable[i] = reg_to_pwm_enable(data->pwm[0][i], + (fanmodecfg >> 4) & 7); + + if (!data->temp_tolerance[0][i] || + data->pwm_enable[i] != speed_cruise) + data->temp_tolerance[0][i] = fanmodecfg & 0x0f; + if (!data->target_speed_tolerance[i] || + data->pwm_enable[i] == speed_cruise) { + u8 t = fanmodecfg & 0x0f; + if (data->REG_TOLERANCE_H) { + t |= (nct6775_read_value(data, + data->REG_TOLERANCE_H[i]) & 0x70) >> 1; + } + data->target_speed_tolerance[i] = t; + } + + data->temp_tolerance[1][i] = + nct6775_read_value(data, + data->REG_CRITICAL_TEMP_TOLERANCE[i]); + + reg = nct6775_read_value(data, data->REG_TEMP_SEL[i]); + data->pwm_temp_sel[i] = reg & 0x1f; + /* If fan can stop, report floor as 0 */ + if (reg & 0x80) + data->pwm[2][i] = 0; + + if (!data->REG_WEIGHT_TEMP_SEL[i]) + continue; + + reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[i]); + data->pwm_weight_temp_sel[i] = reg & 0x1f; + /* If weight is disabled, report weight source as 0 */ + if (j == 1 && !(reg & 0x80)) + data->pwm_weight_temp_sel[i] = 0; + + /* Weight temp data */ + for (j = 0; j < ARRAY_SIZE(data->weight_temp); j++) { + data->weight_temp[j][i] + = nct6775_read_value(data, + data->REG_WEIGHT_TEMP[j][i]); + } + } +} + +static void nct6775_update_pwm_limits(struct device *dev) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + int i, j; + u8 reg; + u16 reg_t; + + for (i = 0; i < data->pwm_num; i++) { + if (!(data->has_pwm & (1 << i))) + continue; + + for (j = 0; j < ARRAY_SIZE(data->fan_time); j++) { + data->fan_time[j][i] = + nct6775_read_value(data, data->REG_FAN_TIME[j][i]); + } + + reg_t = nct6775_read_value(data, data->REG_TARGET[i]); + /* Update only in matching mode or if never updated */ + if (!data->target_temp[i] || + data->pwm_enable[i] == thermal_cruise) + data->target_temp[i] = reg_t & data->target_temp_mask; + if (!data->target_speed[i] || + data->pwm_enable[i] == speed_cruise) { + if (data->REG_TOLERANCE_H) { + reg_t |= (nct6775_read_value(data, + data->REG_TOLERANCE_H[i]) & 0x0f) << 8; + } + data->target_speed[i] = reg_t; + } + + for (j = 0; j < data->auto_pwm_num; j++) { + data->auto_pwm[i][j] = + nct6775_read_value(data, + NCT6775_AUTO_PWM(data, i, j)); + data->auto_temp[i][j] = + nct6775_read_value(data, + NCT6775_AUTO_TEMP(data, i, j)); + } + + /* critical auto_pwm temperature data */ + data->auto_temp[i][data->auto_pwm_num] = + nct6775_read_value(data, data->REG_CRITICAL_TEMP[i]); + + switch (data->kind) { + case nct6775: + reg = nct6775_read_value(data, + NCT6775_REG_CRITICAL_ENAB[i]); + data->auto_pwm[i][data->auto_pwm_num] = + (reg & 0x02) ? 0xff : 0x00; + break; + case nct6776: + data->auto_pwm[i][data->auto_pwm_num] = 0xff; + break; + case nct6106: + case nct6779: + case nct6791: + reg = nct6775_read_value(data, + data->REG_CRITICAL_PWM_ENABLE[i]); + if (reg & data->CRITICAL_PWM_ENABLE_MASK) + reg = nct6775_read_value(data, + data->REG_CRITICAL_PWM[i]); + else + reg = 0xff; + data->auto_pwm[i][data->auto_pwm_num] = reg; + break; + } + } +} + +static struct nct6775_data *nct6775_update_device(struct device *dev) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + int i, j; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + /* Fan clock dividers */ + nct6775_update_fan_div_common(data); + + /* Measured voltages and limits */ + for (i = 0; i < data->in_num; i++) { + if (!(data->have_in & (1 << i))) + continue; + + data->in[i][0] = nct6775_read_value(data, + data->REG_VIN[i]); + data->in[i][1] = nct6775_read_value(data, + data->REG_IN_MINMAX[0][i]); + data->in[i][2] = nct6775_read_value(data, + data->REG_IN_MINMAX[1][i]); + } + + /* Measured fan speeds and limits */ + for (i = 0; i < ARRAY_SIZE(data->rpm); i++) { + u16 reg; + + if (!(data->has_fan & (1 << i))) + continue; + + reg = nct6775_read_value(data, data->REG_FAN[i]); + data->rpm[i] = data->fan_from_reg(reg, + data->fan_div[i]); + + if (data->has_fan_min & (1 << i)) + data->fan_min[i] = nct6775_read_value(data, + data->REG_FAN_MIN[i]); + data->fan_pulses[i] = + (nct6775_read_value(data, data->REG_FAN_PULSES[i]) + >> data->FAN_PULSE_SHIFT[i]) & 0x03; + + nct6775_select_fan_div(dev, data, i, reg); + } + + nct6775_update_pwm(dev); + nct6775_update_pwm_limits(dev); + + /* Measured temperatures and limits */ + for (i = 0; i < NUM_TEMP; i++) { + if (!(data->have_temp & (1 << i))) + continue; + for (j = 0; j < ARRAY_SIZE(data->reg_temp); j++) { + if (data->reg_temp[j][i]) + data->temp[j][i] + = nct6775_read_temp(data, + data->reg_temp[j][i]); + } + if (i >= NUM_TEMP_FIXED || + !(data->have_temp_fixed & (1 << i))) + continue; + data->temp_offset[i] + = nct6775_read_value(data, data->REG_TEMP_OFFSET[i]); + } + + data->alarms = 0; + for (i = 0; i < NUM_REG_ALARM; i++) { + u8 alarm; + if (!data->REG_ALARM[i]) + continue; + alarm = nct6775_read_value(data, data->REG_ALARM[i]); + data->alarms |= ((u64)alarm) << (i << 3); + } + + data->beeps = 0; + for (i = 0; i < NUM_REG_BEEP; i++) { + u8 beep; + if (!data->REG_BEEP[i]) + continue; + beep = nct6775_read_value(data, data->REG_BEEP[i]); + data->beeps |= ((u64)beep) << (i << 3); + } + + data->last_updated = jiffies; + data->valid = true; + } + + mutex_unlock(&data->update_lock); + return data; +} + +/* + * Sysfs callback functions + */ +static ssize_t +show_in_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + return sprintf(buf, "%ld\n", in_from_reg(data->in[nr][index], nr)); +} + +static ssize_t +store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + unsigned long val; + int err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + mutex_lock(&data->update_lock); + data->in[nr][index] = in_to_reg(val, nr); + nct6775_write_value(data, data->REG_IN_MINMAX[index - 1][nr], + data->in[nr][index]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_alarm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = data->ALARM_BITS[sattr->index]; + return sprintf(buf, "%u\n", + (unsigned int)((data->alarms >> nr) & 0x01)); +} + +static int find_temp_source(struct nct6775_data *data, int index, int count) +{ + int source = data->temp_src[index]; + int nr; + + for (nr = 0; nr < count; nr++) { + int src; + + src = nct6775_read_value(data, + data->REG_TEMP_SOURCE[nr]) & 0x1f; + if (src == source) + return nr; + } + return -ENODEV; +} + +static ssize_t +show_temp_alarm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct nct6775_data *data = nct6775_update_device(dev); + unsigned int alarm = 0; + int nr; + + /* + * For temperatures, there is no fixed mapping from registers to alarm + * bits. Alarm bits are determined by the temperature source mapping. + */ + nr = find_temp_source(data, sattr->index, data->num_temp_alarms); + if (nr >= 0) { + int bit = data->ALARM_BITS[nr + TEMP_ALARM_BASE]; + alarm = (data->alarms >> bit) & 0x01; + } + return sprintf(buf, "%u\n", alarm); +} + +static ssize_t +show_beep(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct nct6775_data *data = nct6775_update_device(dev); + int nr = data->BEEP_BITS[sattr->index]; + + return sprintf(buf, "%u\n", + (unsigned int)((data->beeps >> nr) & 0x01)); +} + +static ssize_t +store_beep(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct6775_data *data = dev_get_drvdata(dev); + int nr = data->BEEP_BITS[sattr->index]; + int regindex = nr >> 3; + unsigned long val; + + int err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + if (val > 1) + return -EINVAL; + + mutex_lock(&data->update_lock); + if (val) + data->beeps |= (1ULL << nr); + else + data->beeps &= ~(1ULL << nr); + nct6775_write_value(data, data->REG_BEEP[regindex], + (data->beeps >> (regindex << 3)) & 0xff); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_temp_beep(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct nct6775_data *data = nct6775_update_device(dev); + unsigned int beep = 0; + int nr; + + /* + * For temperatures, there is no fixed mapping from registers to beep + * enable bits. Beep enable bits are determined by the temperature + * source mapping. + */ + nr = find_temp_source(data, sattr->index, data->num_temp_beeps); + if (nr >= 0) { + int bit = data->BEEP_BITS[nr + TEMP_ALARM_BASE]; + beep = (data->beeps >> bit) & 0x01; + } + return sprintf(buf, "%u\n", beep); +} + +static ssize_t +store_temp_beep(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct6775_data *data = dev_get_drvdata(dev); + int nr, bit, regindex; + unsigned long val; + + int err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + if (val > 1) + return -EINVAL; + + nr = find_temp_source(data, sattr->index, data->num_temp_beeps); + if (nr < 0) + return nr; + + bit = data->BEEP_BITS[nr + TEMP_ALARM_BASE]; + regindex = bit >> 3; + + mutex_lock(&data->update_lock); + if (val) + data->beeps |= (1ULL << bit); + else + data->beeps &= ~(1ULL << bit); + nct6775_write_value(data, data->REG_BEEP[regindex], + (data->beeps >> (regindex << 3)) & 0xff); + mutex_unlock(&data->update_lock); + + return count; +} + +static umode_t nct6775_in_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct6775_data *data = dev_get_drvdata(dev); + int in = index / 5; /* voltage index */ + + if (!(data->have_in & (1 << in))) + return 0; + + return attr->mode; +} + +SENSOR_TEMPLATE_2(in_input, "in%d_input", S_IRUGO, show_in_reg, NULL, 0, 0); +SENSOR_TEMPLATE(in_alarm, "in%d_alarm", S_IRUGO, show_alarm, NULL, 0); +SENSOR_TEMPLATE(in_beep, "in%d_beep", S_IWUSR | S_IRUGO, show_beep, store_beep, + 0); +SENSOR_TEMPLATE_2(in_min, "in%d_min", S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 0, 1); +SENSOR_TEMPLATE_2(in_max, "in%d_max", S_IWUSR | S_IRUGO, show_in_reg, + store_in_reg, 0, 2); + +/* + * nct6775_in_is_visible uses the index into the following array + * to determine if attributes should be created or not. + * Any change in order or content must be matched. + */ +static struct sensor_device_template *nct6775_attributes_in_template[] = { + &sensor_dev_template_in_input, + &sensor_dev_template_in_alarm, + &sensor_dev_template_in_beep, + &sensor_dev_template_in_min, + &sensor_dev_template_in_max, + NULL +}; + +static struct sensor_template_group nct6775_in_template_group = { + .templates = nct6775_attributes_in_template, + .is_visible = nct6775_in_is_visible, +}; + +static ssize_t +show_fan(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + return sprintf(buf, "%d\n", data->rpm[nr]); +} + +static ssize_t +show_fan_min(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + return sprintf(buf, "%d\n", + data->fan_from_reg_min(data->fan_min[nr], + data->fan_div[nr])); +} + +static ssize_t +show_fan_div(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr])); +} + +static ssize_t +store_fan_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err; + unsigned int reg; + u8 new_div; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + mutex_lock(&data->update_lock); + if (!data->has_fan_div) { + /* NCT6776F or NCT6779D; we know this is a 13 bit register */ + if (!val) { + val = 0xff1f; + } else { + if (val > 1350000U) + val = 135000U; + val = 1350000U / val; + val = (val & 0x1f) | ((val << 3) & 0xff00); + } + data->fan_min[nr] = val; + goto write_min; /* Leave fan divider alone */ + } + if (!val) { + /* No min limit, alarm disabled */ + data->fan_min[nr] = 255; + new_div = data->fan_div[nr]; /* No change */ + dev_info(dev, "fan%u low limit and alarm disabled\n", nr + 1); + goto write_div; + } + reg = 1350000U / val; + if (reg >= 128 * 255) { + /* + * Speed below this value cannot possibly be represented, + * even with the highest divider (128) + */ + data->fan_min[nr] = 254; + new_div = 7; /* 128 == (1 << 7) */ + dev_warn(dev, + "fan%u low limit %lu below minimum %u, set to minimum\n", + nr + 1, val, data->fan_from_reg_min(254, 7)); + } else if (!reg) { + /* + * Speed above this value cannot possibly be represented, + * even with the lowest divider (1) + */ + data->fan_min[nr] = 1; + new_div = 0; /* 1 == (1 << 0) */ + dev_warn(dev, + "fan%u low limit %lu above maximum %u, set to maximum\n", + nr + 1, val, data->fan_from_reg_min(1, 0)); + } else { + /* + * Automatically pick the best divider, i.e. the one such + * that the min limit will correspond to a register value + * in the 96..192 range + */ + new_div = 0; + while (reg > 192 && new_div < 7) { + reg >>= 1; + new_div++; + } + data->fan_min[nr] = reg; + } + +write_div: + /* + * Write both the fan clock divider (if it changed) and the new + * fan min (unconditionally) + */ + if (new_div != data->fan_div[nr]) { + dev_dbg(dev, "fan%u clock divider changed from %u to %u\n", + nr + 1, div_from_reg(data->fan_div[nr]), + div_from_reg(new_div)); + data->fan_div[nr] = new_div; + nct6775_write_fan_div_common(data, nr); + /* Give the chip time to sample a new speed value */ + data->last_updated = jiffies; + } + +write_min: + nct6775_write_value(data, data->REG_FAN_MIN[nr], data->fan_min[nr]); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t +show_fan_pulses(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int p = data->fan_pulses[sattr->index]; + + return sprintf(buf, "%d\n", p ? : 4); +} + +static ssize_t +store_fan_pulses(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err; + u8 reg; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + if (val > 4) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->fan_pulses[nr] = val & 3; + reg = nct6775_read_value(data, data->REG_FAN_PULSES[nr]); + reg &= ~(0x03 << data->FAN_PULSE_SHIFT[nr]); + reg |= (val & 3) << data->FAN_PULSE_SHIFT[nr]; + nct6775_write_value(data, data->REG_FAN_PULSES[nr], reg); + mutex_unlock(&data->update_lock); + + return count; +} + +static umode_t nct6775_fan_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct6775_data *data = dev_get_drvdata(dev); + int fan = index / 6; /* fan index */ + int nr = index % 6; /* attribute index */ + + if (!(data->has_fan & (1 << fan))) + return 0; + + if (nr == 1 && data->ALARM_BITS[FAN_ALARM_BASE + fan] == -1) + return 0; + if (nr == 2 && data->BEEP_BITS[FAN_ALARM_BASE + fan] == -1) + return 0; + if (nr == 4 && !(data->has_fan_min & (1 << fan))) + return 0; + if (nr == 5 && data->kind != nct6775) + return 0; + + return attr->mode; +} + +SENSOR_TEMPLATE(fan_input, "fan%d_input", S_IRUGO, show_fan, NULL, 0); +SENSOR_TEMPLATE(fan_alarm, "fan%d_alarm", S_IRUGO, show_alarm, NULL, + FAN_ALARM_BASE); +SENSOR_TEMPLATE(fan_beep, "fan%d_beep", S_IWUSR | S_IRUGO, show_beep, + store_beep, FAN_ALARM_BASE); +SENSOR_TEMPLATE(fan_pulses, "fan%d_pulses", S_IWUSR | S_IRUGO, show_fan_pulses, + store_fan_pulses, 0); +SENSOR_TEMPLATE(fan_min, "fan%d_min", S_IWUSR | S_IRUGO, show_fan_min, + store_fan_min, 0); +SENSOR_TEMPLATE(fan_div, "fan%d_div", S_IRUGO, show_fan_div, NULL, 0); + +/* + * nct6775_fan_is_visible uses the index into the following array + * to determine if attributes should be created or not. + * Any change in order or content must be matched. + */ +static struct sensor_device_template *nct6775_attributes_fan_template[] = { + &sensor_dev_template_fan_input, + &sensor_dev_template_fan_alarm, /* 1 */ + &sensor_dev_template_fan_beep, /* 2 */ + &sensor_dev_template_fan_pulses, + &sensor_dev_template_fan_min, /* 4 */ + &sensor_dev_template_fan_div, /* 5 */ + NULL +}; + +static struct sensor_template_group nct6775_fan_template_group = { + .templates = nct6775_attributes_fan_template, + .is_visible = nct6775_fan_is_visible, + .base = 1, +}; + +static ssize_t +show_temp_label(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]); +} + +static ssize_t +show_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + + return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->temp[index][nr])); +} + +static ssize_t +store_temp(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + int err; + long val; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + mutex_lock(&data->update_lock); + data->temp[index][nr] = LM75_TEMP_TO_REG(val); + nct6775_write_temp(data, data->reg_temp[index][nr], + data->temp[index][nr]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_temp_offset(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + + return sprintf(buf, "%d\n", data->temp_offset[sattr->index] * 1000); +} + +static ssize_t +store_temp_offset(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127); + + mutex_lock(&data->update_lock); + data->temp_offset[nr] = val; + nct6775_write_value(data, data->REG_TEMP_OFFSET[nr], val); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t +show_temp_type(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + return sprintf(buf, "%d\n", (int)data->temp_type[nr]); +} + +static ssize_t +store_temp_type(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err; + u8 vbat, diode, vbit, dbit; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + if (val != 1 && val != 3 && val != 4) + return -EINVAL; + + mutex_lock(&data->update_lock); + + data->temp_type[nr] = val; + vbit = 0x02 << nr; + dbit = data->DIODE_MASK << nr; + vbat = nct6775_read_value(data, data->REG_VBAT) & ~vbit; + diode = nct6775_read_value(data, data->REG_DIODE) & ~dbit; + switch (val) { + case 1: /* CPU diode (diode, current mode) */ + vbat |= vbit; + diode |= dbit; + break; + case 3: /* diode, voltage mode */ + vbat |= dbit; + break; + case 4: /* thermistor */ + break; + } + nct6775_write_value(data, data->REG_VBAT, vbat); + nct6775_write_value(data, data->REG_DIODE, diode); + + mutex_unlock(&data->update_lock); + return count; +} + +static umode_t nct6775_temp_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct6775_data *data = dev_get_drvdata(dev); + int temp = index / 10; /* temp index */ + int nr = index % 10; /* attribute index */ + + if (!(data->have_temp & (1 << temp))) + return 0; + + if (nr == 2 && find_temp_source(data, temp, data->num_temp_alarms) < 0) + return 0; /* alarm */ + + if (nr == 3 && find_temp_source(data, temp, data->num_temp_beeps) < 0) + return 0; /* beep */ + + if (nr == 4 && !data->reg_temp[1][temp]) /* max */ + return 0; + + if (nr == 5 && !data->reg_temp[2][temp]) /* max_hyst */ + return 0; + + if (nr == 6 && !data->reg_temp[3][temp]) /* crit */ + return 0; + + if (nr == 7 && !data->reg_temp[4][temp]) /* lcrit */ + return 0; + + /* offset and type only apply to fixed sensors */ + if (nr > 7 && !(data->have_temp_fixed & (1 << temp))) + return 0; + + return attr->mode; +} + +SENSOR_TEMPLATE_2(temp_input, "temp%d_input", S_IRUGO, show_temp, NULL, 0, 0); +SENSOR_TEMPLATE(temp_label, "temp%d_label", S_IRUGO, show_temp_label, NULL, 0); +SENSOR_TEMPLATE_2(temp_max, "temp%d_max", S_IRUGO | S_IWUSR, show_temp, + store_temp, 0, 1); +SENSOR_TEMPLATE_2(temp_max_hyst, "temp%d_max_hyst", S_IRUGO | S_IWUSR, + show_temp, store_temp, 0, 2); +SENSOR_TEMPLATE_2(temp_crit, "temp%d_crit", S_IRUGO | S_IWUSR, show_temp, + store_temp, 0, 3); +SENSOR_TEMPLATE_2(temp_lcrit, "temp%d_lcrit", S_IRUGO | S_IWUSR, show_temp, + store_temp, 0, 4); +SENSOR_TEMPLATE(temp_offset, "temp%d_offset", S_IRUGO | S_IWUSR, + show_temp_offset, store_temp_offset, 0); +SENSOR_TEMPLATE(temp_type, "temp%d_type", S_IRUGO | S_IWUSR, show_temp_type, + store_temp_type, 0); +SENSOR_TEMPLATE(temp_alarm, "temp%d_alarm", S_IRUGO, show_temp_alarm, NULL, 0); +SENSOR_TEMPLATE(temp_beep, "temp%d_beep", S_IRUGO | S_IWUSR, show_temp_beep, + store_temp_beep, 0); + +/* + * nct6775_temp_is_visible uses the index into the following array + * to determine if attributes should be created or not. + * Any change in order or content must be matched. + */ +static struct sensor_device_template *nct6775_attributes_temp_template[] = { + &sensor_dev_template_temp_input, + &sensor_dev_template_temp_label, + &sensor_dev_template_temp_alarm, /* 2 */ + &sensor_dev_template_temp_beep, /* 3 */ + &sensor_dev_template_temp_max, /* 4 */ + &sensor_dev_template_temp_max_hyst, /* 5 */ + &sensor_dev_template_temp_crit, /* 6 */ + &sensor_dev_template_temp_lcrit, /* 7 */ + &sensor_dev_template_temp_offset, /* 8 */ + &sensor_dev_template_temp_type, /* 9 */ + NULL +}; + +static struct sensor_template_group nct6775_temp_template_group = { + .templates = nct6775_attributes_temp_template, + .is_visible = nct6775_temp_is_visible, + .base = 1, +}; + +static ssize_t +show_pwm_mode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + + return sprintf(buf, "%d\n", !data->pwm_mode[sattr->index]); +} + +static ssize_t +store_pwm_mode(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err; + u8 reg; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + if (val > 1) + return -EINVAL; + + /* Setting DC mode is not supported for all chips/channels */ + if (data->REG_PWM_MODE[nr] == 0) { + if (val) + return -EINVAL; + return count; + } + + mutex_lock(&data->update_lock); + data->pwm_mode[nr] = val; + reg = nct6775_read_value(data, data->REG_PWM_MODE[nr]); + reg &= ~data->PWM_MODE_MASK[nr]; + if (val) + reg |= data->PWM_MODE_MASK[nr]; + nct6775_write_value(data, data->REG_PWM_MODE[nr], reg); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_pwm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + int pwm; + + /* + * For automatic fan control modes, show current pwm readings. + * Otherwise, show the configured value. + */ + if (index == 0 && data->pwm_enable[nr] > manual) + pwm = nct6775_read_value(data, data->REG_PWM_READ[nr]); + else + pwm = data->pwm[index][nr]; + + return sprintf(buf, "%d\n", pwm); +} + +static ssize_t +store_pwm(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + unsigned long val; + int minval[7] = { 0, 1, 1, data->pwm[2][nr], 0, 0, 0 }; + int maxval[7] + = { 255, 255, data->pwm[3][nr] ? : 255, 255, 255, 255, 255 }; + int err; + u8 reg; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + val = clamp_val(val, minval[index], maxval[index]); + + mutex_lock(&data->update_lock); + data->pwm[index][nr] = val; + nct6775_write_value(data, data->REG_PWM[index][nr], val); + if (index == 2) { /* floor: disable if val == 0 */ + reg = nct6775_read_value(data, data->REG_TEMP_SEL[nr]); + reg &= 0x7f; + if (val) + reg |= 0x80; + nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg); + } + mutex_unlock(&data->update_lock); + return count; +} + +/* Returns 0 if OK, -EINVAL otherwise */ +static int check_trip_points(struct nct6775_data *data, int nr) +{ + int i; + + for (i = 0; i < data->auto_pwm_num - 1; i++) { + if (data->auto_temp[nr][i] > data->auto_temp[nr][i + 1]) + return -EINVAL; + } + for (i = 0; i < data->auto_pwm_num - 1; i++) { + if (data->auto_pwm[nr][i] > data->auto_pwm[nr][i + 1]) + return -EINVAL; + } + /* validate critical temperature and pwm if enabled (pwm > 0) */ + if (data->auto_pwm[nr][data->auto_pwm_num]) { + if (data->auto_temp[nr][data->auto_pwm_num - 1] > + data->auto_temp[nr][data->auto_pwm_num] || + data->auto_pwm[nr][data->auto_pwm_num - 1] > + data->auto_pwm[nr][data->auto_pwm_num]) + return -EINVAL; + } + return 0; +} + +static void pwm_update_registers(struct nct6775_data *data, int nr) +{ + u8 reg; + + switch (data->pwm_enable[nr]) { + case off: + case manual: + break; + case speed_cruise: + reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]); + reg = (reg & ~data->tolerance_mask) | + (data->target_speed_tolerance[nr] & data->tolerance_mask); + nct6775_write_value(data, data->REG_FAN_MODE[nr], reg); + nct6775_write_value(data, data->REG_TARGET[nr], + data->target_speed[nr] & 0xff); + if (data->REG_TOLERANCE_H) { + reg = (data->target_speed[nr] >> 8) & 0x0f; + reg |= (data->target_speed_tolerance[nr] & 0x38) << 1; + nct6775_write_value(data, + data->REG_TOLERANCE_H[nr], + reg); + } + break; + case thermal_cruise: + nct6775_write_value(data, data->REG_TARGET[nr], + data->target_temp[nr]); + /* intentional */ + default: + reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]); + reg = (reg & ~data->tolerance_mask) | + data->temp_tolerance[0][nr]; + nct6775_write_value(data, data->REG_FAN_MODE[nr], reg); + break; + } +} + +static ssize_t +show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + + return sprintf(buf, "%d\n", data->pwm_enable[sattr->index]); +} + +static ssize_t +store_pwm_enable(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err; + u16 reg; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + if (val > sf4) + return -EINVAL; + + if (val == sf3 && data->kind != nct6775) + return -EINVAL; + + if (val == sf4 && check_trip_points(data, nr)) { + dev_err(dev, "Inconsistent trip points, not switching to SmartFan IV mode\n"); + dev_err(dev, "Adjust trip points and try again\n"); + return -EINVAL; + } + + mutex_lock(&data->update_lock); + data->pwm_enable[nr] = val; + if (val == off) { + /* + * turn off pwm control: select manual mode, set pwm to maximum + */ + data->pwm[0][nr] = 255; + nct6775_write_value(data, data->REG_PWM[0][nr], 255); + } + pwm_update_registers(data, nr); + reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]); + reg &= 0x0f; + reg |= pwm_enable_to_reg(val) << 4; + nct6775_write_value(data, data->REG_FAN_MODE[nr], reg); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_pwm_temp_sel_common(struct nct6775_data *data, char *buf, int src) +{ + int i, sel = 0; + + for (i = 0; i < NUM_TEMP; i++) { + if (!(data->have_temp & (1 << i))) + continue; + if (src == data->temp_src[i]) { + sel = i + 1; + break; + } + } + + return sprintf(buf, "%d\n", sel); +} + +static ssize_t +show_pwm_temp_sel(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int index = sattr->index; + + return show_pwm_temp_sel_common(data, buf, data->pwm_temp_sel[index]); +} + +static ssize_t +store_pwm_temp_sel(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err, reg, src; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + if (val == 0 || val > NUM_TEMP) + return -EINVAL; + if (!(data->have_temp & (1 << (val - 1))) || !data->temp_src[val - 1]) + return -EINVAL; + + mutex_lock(&data->update_lock); + src = data->temp_src[val - 1]; + data->pwm_temp_sel[nr] = src; + reg = nct6775_read_value(data, data->REG_TEMP_SEL[nr]); + reg &= 0xe0; + reg |= src; + nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t +show_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int index = sattr->index; + + return show_pwm_temp_sel_common(data, buf, + data->pwm_weight_temp_sel[index]); +} + +static ssize_t +store_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err, reg, src; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + if (val > NUM_TEMP) + return -EINVAL; + if (val && (!(data->have_temp & (1 << (val - 1))) || + !data->temp_src[val - 1])) + return -EINVAL; + + mutex_lock(&data->update_lock); + if (val) { + src = data->temp_src[val - 1]; + data->pwm_weight_temp_sel[nr] = src; + reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr]); + reg &= 0xe0; + reg |= (src | 0x80); + nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg); + } else { + data->pwm_weight_temp_sel[nr] = 0; + reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr]); + reg &= 0x7f; + nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg); + } + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t +show_target_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + + return sprintf(buf, "%d\n", data->target_temp[sattr->index] * 1000); +} + +static ssize_t +store_target_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, + data->target_temp_mask); + + mutex_lock(&data->update_lock); + data->target_temp[nr] = val; + pwm_update_registers(data, nr); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_target_speed(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + + return sprintf(buf, "%d\n", + fan_from_reg16(data->target_speed[nr], + data->fan_div[nr])); +} + +static ssize_t +store_target_speed(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err; + u16 speed; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + val = clamp_val(val, 0, 1350000U); + speed = fan_to_reg(val, data->fan_div[nr]); + + mutex_lock(&data->update_lock); + data->target_speed[nr] = speed; + pwm_update_registers(data, nr); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_temp_tolerance(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + + return sprintf(buf, "%d\n", data->temp_tolerance[index][nr] * 1000); +} + +static ssize_t +store_temp_tolerance(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + /* Limit tolerance as needed */ + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, data->tolerance_mask); + + mutex_lock(&data->update_lock); + data->temp_tolerance[index][nr] = val; + if (index) + pwm_update_registers(data, nr); + else + nct6775_write_value(data, + data->REG_CRITICAL_TEMP_TOLERANCE[nr], + val); + mutex_unlock(&data->update_lock); + return count; +} + +/* + * Fan speed tolerance is a tricky beast, since the associated register is + * a tick counter, but the value is reported and configured as rpm. + * Compute resulting low and high rpm values and report the difference. + */ +static ssize_t +show_speed_tolerance(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + int low = data->target_speed[nr] - data->target_speed_tolerance[nr]; + int high = data->target_speed[nr] + data->target_speed_tolerance[nr]; + int tolerance; + + if (low <= 0) + low = 1; + if (high > 0xffff) + high = 0xffff; + if (high < low) + high = low; + + tolerance = (fan_from_reg16(low, data->fan_div[nr]) + - fan_from_reg16(high, data->fan_div[nr])) / 2; + + return sprintf(buf, "%d\n", tolerance); +} + +static ssize_t +store_speed_tolerance(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + int nr = sattr->index; + unsigned long val; + int err; + int low, high; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + high = fan_from_reg16(data->target_speed[nr], + data->fan_div[nr]) + val; + low = fan_from_reg16(data->target_speed[nr], + data->fan_div[nr]) - val; + if (low <= 0) + low = 1; + if (high < low) + high = low; + + val = (fan_to_reg(low, data->fan_div[nr]) - + fan_to_reg(high, data->fan_div[nr])) / 2; + + /* Limit tolerance as needed */ + val = clamp_val(val, 0, data->speed_tolerance_limit); + + mutex_lock(&data->update_lock); + data->target_speed_tolerance[nr] = val; + pwm_update_registers(data, nr); + mutex_unlock(&data->update_lock); + return count; +} + +SENSOR_TEMPLATE_2(pwm, "pwm%d", S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 0); +SENSOR_TEMPLATE(pwm_mode, "pwm%d_mode", S_IWUSR | S_IRUGO, show_pwm_mode, + store_pwm_mode, 0); +SENSOR_TEMPLATE(pwm_enable, "pwm%d_enable", S_IWUSR | S_IRUGO, show_pwm_enable, + store_pwm_enable, 0); +SENSOR_TEMPLATE(pwm_temp_sel, "pwm%d_temp_sel", S_IWUSR | S_IRUGO, + show_pwm_temp_sel, store_pwm_temp_sel, 0); +SENSOR_TEMPLATE(pwm_target_temp, "pwm%d_target_temp", S_IWUSR | S_IRUGO, + show_target_temp, store_target_temp, 0); +SENSOR_TEMPLATE(fan_target, "fan%d_target", S_IWUSR | S_IRUGO, + show_target_speed, store_target_speed, 0); +SENSOR_TEMPLATE(fan_tolerance, "fan%d_tolerance", S_IWUSR | S_IRUGO, + show_speed_tolerance, store_speed_tolerance, 0); + +/* Smart Fan registers */ + +static ssize_t +show_weight_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + + return sprintf(buf, "%d\n", data->weight_temp[index][nr] * 1000); +} + +static ssize_t +store_weight_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 255); + + mutex_lock(&data->update_lock); + data->weight_temp[index][nr] = val; + nct6775_write_value(data, data->REG_WEIGHT_TEMP[index][nr], val); + mutex_unlock(&data->update_lock); + return count; +} + +SENSOR_TEMPLATE(pwm_weight_temp_sel, "pwm%d_weight_temp_sel", S_IWUSR | S_IRUGO, + show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, 0); +SENSOR_TEMPLATE_2(pwm_weight_temp_step, "pwm%d_weight_temp_step", + S_IWUSR | S_IRUGO, show_weight_temp, store_weight_temp, 0, 0); +SENSOR_TEMPLATE_2(pwm_weight_temp_step_tol, "pwm%d_weight_temp_step_tol", + S_IWUSR | S_IRUGO, show_weight_temp, store_weight_temp, 0, 1); +SENSOR_TEMPLATE_2(pwm_weight_temp_step_base, "pwm%d_weight_temp_step_base", + S_IWUSR | S_IRUGO, show_weight_temp, store_weight_temp, 0, 2); +SENSOR_TEMPLATE_2(pwm_weight_duty_step, "pwm%d_weight_duty_step", + S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 5); +SENSOR_TEMPLATE_2(pwm_weight_duty_base, "pwm%d_weight_duty_base", + S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 6); + +static ssize_t +show_fan_time(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + + return sprintf(buf, "%d\n", + step_time_from_reg(data->fan_time[index][nr], + data->pwm_mode[nr])); +} + +static ssize_t +store_fan_time(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int index = sattr->index; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + val = step_time_to_reg(val, data->pwm_mode[nr]); + mutex_lock(&data->update_lock); + data->fan_time[index][nr] = val; + nct6775_write_value(data, data->REG_FAN_TIME[index][nr], val); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_auto_pwm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + + return sprintf(buf, "%d\n", data->auto_pwm[sattr->nr][sattr->index]); +} + +static ssize_t +store_auto_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int point = sattr->index; + unsigned long val; + int err; + u8 reg; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + if (val > 255) + return -EINVAL; + + if (point == data->auto_pwm_num) { + if (data->kind != nct6775 && !val) + return -EINVAL; + if (data->kind != nct6779 && val) + val = 0xff; + } + + mutex_lock(&data->update_lock); + data->auto_pwm[nr][point] = val; + if (point < data->auto_pwm_num) { + nct6775_write_value(data, + NCT6775_AUTO_PWM(data, nr, point), + data->auto_pwm[nr][point]); + } else { + switch (data->kind) { + case nct6775: + /* disable if needed (pwm == 0) */ + reg = nct6775_read_value(data, + NCT6775_REG_CRITICAL_ENAB[nr]); + if (val) + reg |= 0x02; + else + reg &= ~0x02; + nct6775_write_value(data, NCT6775_REG_CRITICAL_ENAB[nr], + reg); + break; + case nct6776: + break; /* always enabled, nothing to do */ + case nct6106: + case nct6779: + case nct6791: + nct6775_write_value(data, data->REG_CRITICAL_PWM[nr], + val); + reg = nct6775_read_value(data, + data->REG_CRITICAL_PWM_ENABLE[nr]); + if (val == 255) + reg &= ~data->CRITICAL_PWM_ENABLE_MASK; + else + reg |= data->CRITICAL_PWM_ENABLE_MASK; + nct6775_write_value(data, + data->REG_CRITICAL_PWM_ENABLE[nr], + reg); + break; + } + } + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t +show_auto_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = nct6775_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int point = sattr->index; + + /* + * We don't know for sure if the temperature is signed or unsigned. + * Assume it is unsigned. + */ + return sprintf(buf, "%d\n", data->auto_temp[nr][point] * 1000); +} + +static ssize_t +store_auto_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int nr = sattr->nr; + int point = sattr->index; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + if (val > 255000) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->auto_temp[nr][point] = DIV_ROUND_CLOSEST(val, 1000); + if (point < data->auto_pwm_num) { + nct6775_write_value(data, + NCT6775_AUTO_TEMP(data, nr, point), + data->auto_temp[nr][point]); + } else { + nct6775_write_value(data, data->REG_CRITICAL_TEMP[nr], + data->auto_temp[nr][point]); + } + mutex_unlock(&data->update_lock); + return count; +} + +static umode_t nct6775_pwm_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct6775_data *data = dev_get_drvdata(dev); + int pwm = index / 36; /* pwm index */ + int nr = index % 36; /* attribute index */ + + if (!(data->has_pwm & (1 << pwm))) + return 0; + + if ((nr >= 14 && nr <= 18) || nr == 21) /* weight */ + if (!data->REG_WEIGHT_TEMP_SEL[pwm]) + return 0; + if (nr == 19 && data->REG_PWM[3] == NULL) /* pwm_max */ + return 0; + if (nr == 20 && data->REG_PWM[4] == NULL) /* pwm_step */ + return 0; + if (nr == 21 && data->REG_PWM[6] == NULL) /* weight_duty_base */ + return 0; + + if (nr >= 22 && nr <= 35) { /* auto point */ + int api = (nr - 22) / 2; /* auto point index */ + + if (api > data->auto_pwm_num) + return 0; + } + return attr->mode; +} + +SENSOR_TEMPLATE_2(pwm_stop_time, "pwm%d_stop_time", S_IWUSR | S_IRUGO, + show_fan_time, store_fan_time, 0, 0); +SENSOR_TEMPLATE_2(pwm_step_up_time, "pwm%d_step_up_time", S_IWUSR | S_IRUGO, + show_fan_time, store_fan_time, 0, 1); +SENSOR_TEMPLATE_2(pwm_step_down_time, "pwm%d_step_down_time", S_IWUSR | S_IRUGO, + show_fan_time, store_fan_time, 0, 2); +SENSOR_TEMPLATE_2(pwm_start, "pwm%d_start", S_IWUSR | S_IRUGO, show_pwm, + store_pwm, 0, 1); +SENSOR_TEMPLATE_2(pwm_floor, "pwm%d_floor", S_IWUSR | S_IRUGO, show_pwm, + store_pwm, 0, 2); +SENSOR_TEMPLATE_2(pwm_temp_tolerance, "pwm%d_temp_tolerance", S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 0, 0); +SENSOR_TEMPLATE_2(pwm_crit_temp_tolerance, "pwm%d_crit_temp_tolerance", + S_IWUSR | S_IRUGO, show_temp_tolerance, store_temp_tolerance, + 0, 1); + +SENSOR_TEMPLATE_2(pwm_max, "pwm%d_max", S_IWUSR | S_IRUGO, show_pwm, store_pwm, + 0, 3); + +SENSOR_TEMPLATE_2(pwm_step, "pwm%d_step", S_IWUSR | S_IRUGO, show_pwm, + store_pwm, 0, 4); + +SENSOR_TEMPLATE_2(pwm_auto_point1_pwm, "pwm%d_auto_point1_pwm", + S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 0); +SENSOR_TEMPLATE_2(pwm_auto_point1_temp, "pwm%d_auto_point1_temp", + S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 0); + +SENSOR_TEMPLATE_2(pwm_auto_point2_pwm, "pwm%d_auto_point2_pwm", + S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 1); +SENSOR_TEMPLATE_2(pwm_auto_point2_temp, "pwm%d_auto_point2_temp", + S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 1); + +SENSOR_TEMPLATE_2(pwm_auto_point3_pwm, "pwm%d_auto_point3_pwm", + S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 2); +SENSOR_TEMPLATE_2(pwm_auto_point3_temp, "pwm%d_auto_point3_temp", + S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 2); + +SENSOR_TEMPLATE_2(pwm_auto_point4_pwm, "pwm%d_auto_point4_pwm", + S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 3); +SENSOR_TEMPLATE_2(pwm_auto_point4_temp, "pwm%d_auto_point4_temp", + S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 3); + +SENSOR_TEMPLATE_2(pwm_auto_point5_pwm, "pwm%d_auto_point5_pwm", + S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 4); +SENSOR_TEMPLATE_2(pwm_auto_point5_temp, "pwm%d_auto_point5_temp", + S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 4); + +SENSOR_TEMPLATE_2(pwm_auto_point6_pwm, "pwm%d_auto_point6_pwm", + S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 5); +SENSOR_TEMPLATE_2(pwm_auto_point6_temp, "pwm%d_auto_point6_temp", + S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 5); + +SENSOR_TEMPLATE_2(pwm_auto_point7_pwm, "pwm%d_auto_point7_pwm", + S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 6); +SENSOR_TEMPLATE_2(pwm_auto_point7_temp, "pwm%d_auto_point7_temp", + S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 6); + +/* + * nct6775_pwm_is_visible uses the index into the following array + * to determine if attributes should be created or not. + * Any change in order or content must be matched. + */ +static struct sensor_device_template *nct6775_attributes_pwm_template[] = { + &sensor_dev_template_pwm, + &sensor_dev_template_pwm_mode, + &sensor_dev_template_pwm_enable, + &sensor_dev_template_pwm_temp_sel, + &sensor_dev_template_pwm_temp_tolerance, + &sensor_dev_template_pwm_crit_temp_tolerance, + &sensor_dev_template_pwm_target_temp, + &sensor_dev_template_fan_target, + &sensor_dev_template_fan_tolerance, + &sensor_dev_template_pwm_stop_time, + &sensor_dev_template_pwm_step_up_time, + &sensor_dev_template_pwm_step_down_time, + &sensor_dev_template_pwm_start, + &sensor_dev_template_pwm_floor, + &sensor_dev_template_pwm_weight_temp_sel, /* 14 */ + &sensor_dev_template_pwm_weight_temp_step, + &sensor_dev_template_pwm_weight_temp_step_tol, + &sensor_dev_template_pwm_weight_temp_step_base, + &sensor_dev_template_pwm_weight_duty_step, /* 18 */ + &sensor_dev_template_pwm_max, /* 19 */ + &sensor_dev_template_pwm_step, /* 20 */ + &sensor_dev_template_pwm_weight_duty_base, /* 21 */ + &sensor_dev_template_pwm_auto_point1_pwm, /* 22 */ + &sensor_dev_template_pwm_auto_point1_temp, + &sensor_dev_template_pwm_auto_point2_pwm, + &sensor_dev_template_pwm_auto_point2_temp, + &sensor_dev_template_pwm_auto_point3_pwm, + &sensor_dev_template_pwm_auto_point3_temp, + &sensor_dev_template_pwm_auto_point4_pwm, + &sensor_dev_template_pwm_auto_point4_temp, + &sensor_dev_template_pwm_auto_point5_pwm, + &sensor_dev_template_pwm_auto_point5_temp, + &sensor_dev_template_pwm_auto_point6_pwm, + &sensor_dev_template_pwm_auto_point6_temp, + &sensor_dev_template_pwm_auto_point7_pwm, + &sensor_dev_template_pwm_auto_point7_temp, /* 35 */ + + NULL +}; + +static struct sensor_template_group nct6775_pwm_template_group = { + .templates = nct6775_attributes_pwm_template, + .is_visible = nct6775_pwm_is_visible, + .base = 1, +}; + +static ssize_t +show_vid(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); +} + +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); + +/* Case open detection */ + +static ssize_t +clear_caseopen(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + int nr = to_sensor_dev_attr(attr)->index - INTRUSION_ALARM_BASE; + unsigned long val; + u8 reg; + int ret; + + if (kstrtoul(buf, 10, &val) || val != 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + + /* + * Use CR registers to clear caseopen status. + * The CR registers are the same for all chips, and not all chips + * support clearing the caseopen status through "regular" registers. + */ + ret = superio_enter(data->sioreg); + if (ret) { + count = ret; + goto error; + } + + superio_select(data->sioreg, NCT6775_LD_ACPI); + reg = superio_inb(data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr]); + reg |= NCT6775_CR_CASEOPEN_CLR_MASK[nr]; + superio_outb(data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg); + reg &= ~NCT6775_CR_CASEOPEN_CLR_MASK[nr]; + superio_outb(data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg); + superio_exit(data->sioreg); + + data->valid = false; /* Force cache refresh */ +error: + mutex_unlock(&data->update_lock); + return count; +} + +static SENSOR_DEVICE_ATTR(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm, + clear_caseopen, INTRUSION_ALARM_BASE); +static SENSOR_DEVICE_ATTR(intrusion1_alarm, S_IWUSR | S_IRUGO, show_alarm, + clear_caseopen, INTRUSION_ALARM_BASE + 1); +static SENSOR_DEVICE_ATTR(intrusion0_beep, S_IWUSR | S_IRUGO, show_beep, + store_beep, INTRUSION_ALARM_BASE); +static SENSOR_DEVICE_ATTR(intrusion1_beep, S_IWUSR | S_IRUGO, show_beep, + store_beep, INTRUSION_ALARM_BASE + 1); +static SENSOR_DEVICE_ATTR(beep_enable, S_IWUSR | S_IRUGO, show_beep, + store_beep, BEEP_ENABLE_BASE); + +static umode_t nct6775_other_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct6775_data *data = dev_get_drvdata(dev); + + if (index == 0 && !data->have_vid) + return 0; + + if (index == 1 || index == 2) { + if (data->ALARM_BITS[INTRUSION_ALARM_BASE + index - 1] < 0) + return 0; + } + + if (index == 3 || index == 4) { + if (data->BEEP_BITS[INTRUSION_ALARM_BASE + index - 3] < 0) + return 0; + } + + return attr->mode; +} + +/* + * nct6775_other_is_visible uses the index into the following array + * to determine if attributes should be created or not. + * Any change in order or content must be matched. + */ +static struct attribute *nct6775_attributes_other[] = { + &dev_attr_cpu0_vid.attr, /* 0 */ + &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, /* 1 */ + &sensor_dev_attr_intrusion1_alarm.dev_attr.attr, /* 2 */ + &sensor_dev_attr_intrusion0_beep.dev_attr.attr, /* 3 */ + &sensor_dev_attr_intrusion1_beep.dev_attr.attr, /* 4 */ + &sensor_dev_attr_beep_enable.dev_attr.attr, /* 5 */ + + NULL +}; + +static const struct attribute_group nct6775_group_other = { + .attrs = nct6775_attributes_other, + .is_visible = nct6775_other_is_visible, +}; + +static inline void nct6775_init_device(struct nct6775_data *data) +{ + int i; + u8 tmp, diode; + + /* Start monitoring if needed */ + if (data->REG_CONFIG) { + tmp = nct6775_read_value(data, data->REG_CONFIG); + if (!(tmp & 0x01)) + nct6775_write_value(data, data->REG_CONFIG, tmp | 0x01); + } + + /* Enable temperature sensors if needed */ + for (i = 0; i < NUM_TEMP; i++) { + if (!(data->have_temp & (1 << i))) + continue; + if (!data->reg_temp_config[i]) + continue; + tmp = nct6775_read_value(data, data->reg_temp_config[i]); + if (tmp & 0x01) + nct6775_write_value(data, data->reg_temp_config[i], + tmp & 0xfe); + } + + /* Enable VBAT monitoring if needed */ + tmp = nct6775_read_value(data, data->REG_VBAT); + if (!(tmp & 0x01)) + nct6775_write_value(data, data->REG_VBAT, tmp | 0x01); + + diode = nct6775_read_value(data, data->REG_DIODE); + + for (i = 0; i < data->temp_fixed_num; i++) { + if (!(data->have_temp_fixed & (1 << i))) + continue; + if ((tmp & (data->DIODE_MASK << i))) /* diode */ + data->temp_type[i] + = 3 - ((diode >> i) & data->DIODE_MASK); + else /* thermistor */ + data->temp_type[i] = 4; + } +} + +static void +nct6775_check_fan_inputs(struct nct6775_data *data) +{ + bool fan3pin, fan4pin, fan4min, fan5pin, fan6pin; + bool pwm3pin, pwm4pin, pwm5pin, pwm6pin; + int sioreg = data->sioreg; + int regval; + + /* fan4 and fan5 share some pins with the GPIO and serial flash */ + if (data->kind == nct6775) { + regval = superio_inb(sioreg, 0x2c); + + fan3pin = regval & (1 << 6); + pwm3pin = regval & (1 << 7); + + /* On NCT6775, fan4 shares pins with the fdc interface */ + fan4pin = !(superio_inb(sioreg, 0x2A) & 0x80); + fan4min = false; + fan5pin = false; + fan6pin = false; + pwm4pin = false; + pwm5pin = false; + pwm6pin = false; + } else if (data->kind == nct6776) { + bool gpok = superio_inb(sioreg, 0x27) & 0x80; + + superio_select(sioreg, NCT6775_LD_HWM); + regval = superio_inb(sioreg, SIO_REG_ENABLE); + + if (regval & 0x80) + fan3pin = gpok; + else + fan3pin = !(superio_inb(sioreg, 0x24) & 0x40); + + if (regval & 0x40) + fan4pin = gpok; + else + fan4pin = superio_inb(sioreg, 0x1C) & 0x01; + + if (regval & 0x20) + fan5pin = gpok; + else + fan5pin = superio_inb(sioreg, 0x1C) & 0x02; + + fan4min = fan4pin; + fan6pin = false; + pwm3pin = fan3pin; + pwm4pin = false; + pwm5pin = false; + pwm6pin = false; + } else if (data->kind == nct6106) { + regval = superio_inb(sioreg, 0x24); + fan3pin = !(regval & 0x80); + pwm3pin = regval & 0x08; + + fan4pin = false; + fan4min = false; + fan5pin = false; + fan6pin = false; + pwm4pin = false; + pwm5pin = false; + pwm6pin = false; + } else { /* NCT6779D or NCT6791D */ + regval = superio_inb(sioreg, 0x1c); + + fan3pin = !(regval & (1 << 5)); + fan4pin = !(regval & (1 << 6)); + fan5pin = !(regval & (1 << 7)); + + pwm3pin = !(regval & (1 << 0)); + pwm4pin = !(regval & (1 << 1)); + pwm5pin = !(regval & (1 << 2)); + + fan4min = fan4pin; + + if (data->kind == nct6791) { + regval = superio_inb(sioreg, 0x2d); + fan6pin = (regval & (1 << 1)); + pwm6pin = (regval & (1 << 0)); + } else { /* NCT6779D */ + fan6pin = false; + pwm6pin = false; + } + } + + /* fan 1 and 2 (0x03) are always present */ + data->has_fan = 0x03 | (fan3pin << 2) | (fan4pin << 3) | + (fan5pin << 4) | (fan6pin << 5); + data->has_fan_min = 0x03 | (fan3pin << 2) | (fan4min << 3) | + (fan5pin << 4); + data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) | + (pwm5pin << 4) | (pwm6pin << 5); +} + +static void add_temp_sensors(struct nct6775_data *data, const u16 *regp, + int *available, int *mask) +{ + int i; + u8 src; + + for (i = 0; i < data->pwm_num && *available; i++) { + int index; + + if (!regp[i]) + continue; + src = nct6775_read_value(data, regp[i]); + src &= 0x1f; + if (!src || (*mask & (1 << src))) + continue; + if (src >= data->temp_label_num || + !strlen(data->temp_label[src])) + continue; + + index = __ffs(*available); + nct6775_write_value(data, data->REG_TEMP_SOURCE[index], src); + *available &= ~(1 << index); + *mask |= 1 << src; + } +} + +static int nct6775_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct nct6775_sio_data *sio_data = dev_get_platdata(dev); + struct nct6775_data *data; + struct resource *res; + int i, s, err = 0; + int src, mask, available; + const u16 *reg_temp, *reg_temp_over, *reg_temp_hyst, *reg_temp_config; + const u16 *reg_temp_mon, *reg_temp_alternate, *reg_temp_crit; + const u16 *reg_temp_crit_l = NULL, *reg_temp_crit_h = NULL; + int num_reg_temp, num_reg_temp_mon; + u8 cr2a; + struct attribute_group *group; + struct device *hwmon_dev; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH, + DRVNAME)) + return -EBUSY; + + data = devm_kzalloc(&pdev->dev, sizeof(struct nct6775_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->kind = sio_data->kind; + data->sioreg = sio_data->sioreg; + data->addr = res->start; + mutex_init(&data->update_lock); + data->name = nct6775_device_names[data->kind]; + data->bank = 0xff; /* Force initial bank selection */ + platform_set_drvdata(pdev, data); + + switch (data->kind) { + case nct6106: + data->in_num = 9; + data->pwm_num = 3; + data->auto_pwm_num = 4; + data->temp_fixed_num = 3; + data->num_temp_alarms = 6; + data->num_temp_beeps = 6; + + data->fan_from_reg = fan_from_reg13; + data->fan_from_reg_min = fan_from_reg13; + + data->temp_label = nct6776_temp_label; + data->temp_label_num = ARRAY_SIZE(nct6776_temp_label); + + data->REG_VBAT = NCT6106_REG_VBAT; + data->REG_DIODE = NCT6106_REG_DIODE; + data->DIODE_MASK = NCT6106_DIODE_MASK; + data->REG_VIN = NCT6106_REG_IN; + data->REG_IN_MINMAX[0] = NCT6106_REG_IN_MIN; + data->REG_IN_MINMAX[1] = NCT6106_REG_IN_MAX; + data->REG_TARGET = NCT6106_REG_TARGET; + data->REG_FAN = NCT6106_REG_FAN; + data->REG_FAN_MODE = NCT6106_REG_FAN_MODE; + data->REG_FAN_MIN = NCT6106_REG_FAN_MIN; + data->REG_FAN_PULSES = NCT6106_REG_FAN_PULSES; + data->FAN_PULSE_SHIFT = NCT6106_FAN_PULSE_SHIFT; + data->REG_FAN_TIME[0] = NCT6106_REG_FAN_STOP_TIME; + data->REG_FAN_TIME[1] = NCT6106_REG_FAN_STEP_UP_TIME; + data->REG_FAN_TIME[2] = NCT6106_REG_FAN_STEP_DOWN_TIME; + data->REG_PWM[0] = NCT6106_REG_PWM; + data->REG_PWM[1] = NCT6106_REG_FAN_START_OUTPUT; + data->REG_PWM[2] = NCT6106_REG_FAN_STOP_OUTPUT; + data->REG_PWM[5] = NCT6106_REG_WEIGHT_DUTY_STEP; + data->REG_PWM[6] = NCT6106_REG_WEIGHT_DUTY_BASE; + data->REG_PWM_READ = NCT6106_REG_PWM_READ; + data->REG_PWM_MODE = NCT6106_REG_PWM_MODE; + data->PWM_MODE_MASK = NCT6106_PWM_MODE_MASK; + data->REG_AUTO_TEMP = NCT6106_REG_AUTO_TEMP; + data->REG_AUTO_PWM = NCT6106_REG_AUTO_PWM; + data->REG_CRITICAL_TEMP = NCT6106_REG_CRITICAL_TEMP; + data->REG_CRITICAL_TEMP_TOLERANCE + = NCT6106_REG_CRITICAL_TEMP_TOLERANCE; + data->REG_CRITICAL_PWM_ENABLE = NCT6106_REG_CRITICAL_PWM_ENABLE; + data->CRITICAL_PWM_ENABLE_MASK + = NCT6106_CRITICAL_PWM_ENABLE_MASK; + data->REG_CRITICAL_PWM = NCT6106_REG_CRITICAL_PWM; + data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET; + data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE; + data->REG_TEMP_SEL = NCT6106_REG_TEMP_SEL; + data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL; + data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP; + data->REG_WEIGHT_TEMP[1] = NCT6106_REG_WEIGHT_TEMP_STEP_TOL; + data->REG_WEIGHT_TEMP[2] = NCT6106_REG_WEIGHT_TEMP_BASE; + data->REG_ALARM = NCT6106_REG_ALARM; + data->ALARM_BITS = NCT6106_ALARM_BITS; + data->REG_BEEP = NCT6106_REG_BEEP; + data->BEEP_BITS = NCT6106_BEEP_BITS; + + reg_temp = NCT6106_REG_TEMP; + reg_temp_mon = NCT6106_REG_TEMP_MON; + num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP); + num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON); + reg_temp_over = NCT6106_REG_TEMP_OVER; + reg_temp_hyst = NCT6106_REG_TEMP_HYST; + reg_temp_config = NCT6106_REG_TEMP_CONFIG; + reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6106_REG_TEMP_CRIT; + reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L; + reg_temp_crit_h = NCT6106_REG_TEMP_CRIT_H; + + break; + case nct6775: + data->in_num = 9; + data->pwm_num = 3; + data->auto_pwm_num = 6; + data->has_fan_div = true; + data->temp_fixed_num = 3; + data->num_temp_alarms = 3; + data->num_temp_beeps = 3; + + data->ALARM_BITS = NCT6775_ALARM_BITS; + data->BEEP_BITS = NCT6775_BEEP_BITS; + + data->fan_from_reg = fan_from_reg16; + data->fan_from_reg_min = fan_from_reg8; + data->target_temp_mask = 0x7f; + data->tolerance_mask = 0x0f; + data->speed_tolerance_limit = 15; + + data->temp_label = nct6775_temp_label; + data->temp_label_num = ARRAY_SIZE(nct6775_temp_label); + + data->REG_CONFIG = NCT6775_REG_CONFIG; + data->REG_VBAT = NCT6775_REG_VBAT; + data->REG_DIODE = NCT6775_REG_DIODE; + data->DIODE_MASK = NCT6775_DIODE_MASK; + data->REG_VIN = NCT6775_REG_IN; + data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; + data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; + data->REG_TARGET = NCT6775_REG_TARGET; + data->REG_FAN = NCT6775_REG_FAN; + data->REG_FAN_MODE = NCT6775_REG_FAN_MODE; + data->REG_FAN_MIN = NCT6775_REG_FAN_MIN; + data->REG_FAN_PULSES = NCT6775_REG_FAN_PULSES; + data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; + data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; + data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; + data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_PWM[0] = NCT6775_REG_PWM; + data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; + data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT; + data->REG_PWM[3] = NCT6775_REG_FAN_MAX_OUTPUT; + data->REG_PWM[4] = NCT6775_REG_FAN_STEP_OUTPUT; + data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP; + data->REG_PWM_READ = NCT6775_REG_PWM_READ; + data->REG_PWM_MODE = NCT6775_REG_PWM_MODE; + data->PWM_MODE_MASK = NCT6775_PWM_MODE_MASK; + data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP; + data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM; + data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP; + data->REG_CRITICAL_TEMP_TOLERANCE + = NCT6775_REG_CRITICAL_TEMP_TOLERANCE; + data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; + data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; + data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL; + data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL; + data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP; + data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL; + data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE; + data->REG_ALARM = NCT6775_REG_ALARM; + data->REG_BEEP = NCT6775_REG_BEEP; + + reg_temp = NCT6775_REG_TEMP; + reg_temp_mon = NCT6775_REG_TEMP_MON; + num_reg_temp = ARRAY_SIZE(NCT6775_REG_TEMP); + num_reg_temp_mon = ARRAY_SIZE(NCT6775_REG_TEMP_MON); + reg_temp_over = NCT6775_REG_TEMP_OVER; + reg_temp_hyst = NCT6775_REG_TEMP_HYST; + reg_temp_config = NCT6775_REG_TEMP_CONFIG; + reg_temp_alternate = NCT6775_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6775_REG_TEMP_CRIT; + + break; + case nct6776: + data->in_num = 9; + data->pwm_num = 3; + data->auto_pwm_num = 4; + data->has_fan_div = false; + data->temp_fixed_num = 3; + data->num_temp_alarms = 3; + data->num_temp_beeps = 6; + + data->ALARM_BITS = NCT6776_ALARM_BITS; + data->BEEP_BITS = NCT6776_BEEP_BITS; + + data->fan_from_reg = fan_from_reg13; + data->fan_from_reg_min = fan_from_reg13; + data->target_temp_mask = 0xff; + data->tolerance_mask = 0x07; + data->speed_tolerance_limit = 63; + + data->temp_label = nct6776_temp_label; + data->temp_label_num = ARRAY_SIZE(nct6776_temp_label); + + data->REG_CONFIG = NCT6775_REG_CONFIG; + data->REG_VBAT = NCT6775_REG_VBAT; + data->REG_DIODE = NCT6775_REG_DIODE; + data->DIODE_MASK = NCT6775_DIODE_MASK; + data->REG_VIN = NCT6775_REG_IN; + data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; + data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; + data->REG_TARGET = NCT6775_REG_TARGET; + data->REG_FAN = NCT6775_REG_FAN; + data->REG_FAN_MODE = NCT6775_REG_FAN_MODE; + data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; + data->REG_FAN_PULSES = NCT6776_REG_FAN_PULSES; + data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; + data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; + data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; + data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; + data->REG_PWM[0] = NCT6775_REG_PWM; + data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; + data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT; + data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP; + data->REG_PWM[6] = NCT6776_REG_WEIGHT_DUTY_BASE; + data->REG_PWM_READ = NCT6775_REG_PWM_READ; + data->REG_PWM_MODE = NCT6776_REG_PWM_MODE; + data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK; + data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP; + data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM; + data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP; + data->REG_CRITICAL_TEMP_TOLERANCE + = NCT6775_REG_CRITICAL_TEMP_TOLERANCE; + data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; + data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; + data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL; + data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL; + data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP; + data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL; + data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE; + data->REG_ALARM = NCT6775_REG_ALARM; + data->REG_BEEP = NCT6776_REG_BEEP; + + reg_temp = NCT6775_REG_TEMP; + reg_temp_mon = NCT6775_REG_TEMP_MON; + num_reg_temp = ARRAY_SIZE(NCT6775_REG_TEMP); + num_reg_temp_mon = ARRAY_SIZE(NCT6775_REG_TEMP_MON); + reg_temp_over = NCT6775_REG_TEMP_OVER; + reg_temp_hyst = NCT6775_REG_TEMP_HYST; + reg_temp_config = NCT6776_REG_TEMP_CONFIG; + reg_temp_alternate = NCT6776_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6776_REG_TEMP_CRIT; + + break; + case nct6779: + data->in_num = 15; + data->pwm_num = 5; + data->auto_pwm_num = 4; + data->has_fan_div = false; + data->temp_fixed_num = 6; + data->num_temp_alarms = 2; + data->num_temp_beeps = 2; + + data->ALARM_BITS = NCT6779_ALARM_BITS; + data->BEEP_BITS = NCT6779_BEEP_BITS; + + data->fan_from_reg = fan_from_reg13; + data->fan_from_reg_min = fan_from_reg13; + data->target_temp_mask = 0xff; + data->tolerance_mask = 0x07; + data->speed_tolerance_limit = 63; + + data->temp_label = nct6779_temp_label; + data->temp_label_num = ARRAY_SIZE(nct6779_temp_label); + + data->REG_CONFIG = NCT6775_REG_CONFIG; + data->REG_VBAT = NCT6775_REG_VBAT; + data->REG_DIODE = NCT6775_REG_DIODE; + data->DIODE_MASK = NCT6775_DIODE_MASK; + data->REG_VIN = NCT6779_REG_IN; + data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; + data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; + data->REG_TARGET = NCT6775_REG_TARGET; + data->REG_FAN = NCT6779_REG_FAN; + data->REG_FAN_MODE = NCT6775_REG_FAN_MODE; + data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; + data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES; + data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; + data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; + data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; + data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; + data->REG_PWM[0] = NCT6775_REG_PWM; + data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; + data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT; + data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP; + data->REG_PWM[6] = NCT6776_REG_WEIGHT_DUTY_BASE; + data->REG_PWM_READ = NCT6775_REG_PWM_READ; + data->REG_PWM_MODE = NCT6776_REG_PWM_MODE; + data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK; + data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP; + data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM; + data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP; + data->REG_CRITICAL_TEMP_TOLERANCE + = NCT6775_REG_CRITICAL_TEMP_TOLERANCE; + data->REG_CRITICAL_PWM_ENABLE = NCT6779_REG_CRITICAL_PWM_ENABLE; + data->CRITICAL_PWM_ENABLE_MASK + = NCT6779_CRITICAL_PWM_ENABLE_MASK; + data->REG_CRITICAL_PWM = NCT6779_REG_CRITICAL_PWM; + data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET; + data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; + data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL; + data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL; + data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP; + data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL; + data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE; + data->REG_ALARM = NCT6779_REG_ALARM; + data->REG_BEEP = NCT6776_REG_BEEP; + + reg_temp = NCT6779_REG_TEMP; + reg_temp_mon = NCT6779_REG_TEMP_MON; + num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP); + num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON); + reg_temp_over = NCT6779_REG_TEMP_OVER; + reg_temp_hyst = NCT6779_REG_TEMP_HYST; + reg_temp_config = NCT6779_REG_TEMP_CONFIG; + reg_temp_alternate = NCT6779_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6779_REG_TEMP_CRIT; + + break; + case nct6791: + data->in_num = 15; + data->pwm_num = 6; + data->auto_pwm_num = 4; + data->has_fan_div = false; + data->temp_fixed_num = 6; + data->num_temp_alarms = 2; + data->num_temp_beeps = 2; + + data->ALARM_BITS = NCT6791_ALARM_BITS; + data->BEEP_BITS = NCT6779_BEEP_BITS; + + data->fan_from_reg = fan_from_reg13; + data->fan_from_reg_min = fan_from_reg13; + data->target_temp_mask = 0xff; + data->tolerance_mask = 0x07; + data->speed_tolerance_limit = 63; + + data->temp_label = nct6779_temp_label; + data->temp_label_num = ARRAY_SIZE(nct6779_temp_label); + + data->REG_CONFIG = NCT6775_REG_CONFIG; + data->REG_VBAT = NCT6775_REG_VBAT; + data->REG_DIODE = NCT6775_REG_DIODE; + data->DIODE_MASK = NCT6775_DIODE_MASK; + data->REG_VIN = NCT6779_REG_IN; + data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; + data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; + data->REG_TARGET = NCT6775_REG_TARGET; + data->REG_FAN = NCT6779_REG_FAN; + data->REG_FAN_MODE = NCT6775_REG_FAN_MODE; + data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; + data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES; + data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; + data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; + data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; + data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; + data->REG_PWM[0] = NCT6775_REG_PWM; + data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; + data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT; + data->REG_PWM[5] = NCT6791_REG_WEIGHT_DUTY_STEP; + data->REG_PWM[6] = NCT6791_REG_WEIGHT_DUTY_BASE; + data->REG_PWM_READ = NCT6775_REG_PWM_READ; + data->REG_PWM_MODE = NCT6776_REG_PWM_MODE; + data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK; + data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP; + data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM; + data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP; + data->REG_CRITICAL_TEMP_TOLERANCE + = NCT6775_REG_CRITICAL_TEMP_TOLERANCE; + data->REG_CRITICAL_PWM_ENABLE = NCT6779_REG_CRITICAL_PWM_ENABLE; + data->CRITICAL_PWM_ENABLE_MASK + = NCT6779_CRITICAL_PWM_ENABLE_MASK; + data->REG_CRITICAL_PWM = NCT6779_REG_CRITICAL_PWM; + data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET; + data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; + data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL; + data->REG_WEIGHT_TEMP_SEL = NCT6791_REG_WEIGHT_TEMP_SEL; + data->REG_WEIGHT_TEMP[0] = NCT6791_REG_WEIGHT_TEMP_STEP; + data->REG_WEIGHT_TEMP[1] = NCT6791_REG_WEIGHT_TEMP_STEP_TOL; + data->REG_WEIGHT_TEMP[2] = NCT6791_REG_WEIGHT_TEMP_BASE; + data->REG_ALARM = NCT6791_REG_ALARM; + data->REG_BEEP = NCT6776_REG_BEEP; + + reg_temp = NCT6779_REG_TEMP; + reg_temp_mon = NCT6779_REG_TEMP_MON; + num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP); + num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON); + reg_temp_over = NCT6779_REG_TEMP_OVER; + reg_temp_hyst = NCT6779_REG_TEMP_HYST; + reg_temp_config = NCT6779_REG_TEMP_CONFIG; + reg_temp_alternate = NCT6779_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6779_REG_TEMP_CRIT; + + break; + default: + return -ENODEV; + } + data->have_in = (1 << data->in_num) - 1; + data->have_temp = 0; + + /* + * On some boards, not all available temperature sources are monitored, + * even though some of the monitoring registers are unused. + * Get list of unused monitoring registers, then detect if any fan + * controls are configured to use unmonitored temperature sources. + * If so, assign the unmonitored temperature sources to available + * monitoring registers. + */ + mask = 0; + available = 0; + for (i = 0; i < num_reg_temp; i++) { + if (reg_temp[i] == 0) + continue; + + src = nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f; + if (!src || (mask & (1 << src))) + available |= 1 << i; + + mask |= 1 << src; + } + + /* + * Now find unmonitored temperature registers and enable monitoring + * if additional monitoring registers are available. + */ + add_temp_sensors(data, data->REG_TEMP_SEL, &available, &mask); + add_temp_sensors(data, data->REG_WEIGHT_TEMP_SEL, &available, &mask); + + mask = 0; + s = NUM_TEMP_FIXED; /* First dynamic temperature attribute */ + for (i = 0; i < num_reg_temp; i++) { + if (reg_temp[i] == 0) + continue; + + src = nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f; + if (!src || (mask & (1 << src))) + continue; + + if (src >= data->temp_label_num || + !strlen(data->temp_label[src])) { + dev_info(dev, + "Invalid temperature source %d at index %d, source register 0x%x, temp register 0x%x\n", + src, i, data->REG_TEMP_SOURCE[i], reg_temp[i]); + continue; + } + + mask |= 1 << src; + + /* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */ + if (src <= data->temp_fixed_num) { + data->have_temp |= 1 << (src - 1); + data->have_temp_fixed |= 1 << (src - 1); + data->reg_temp[0][src - 1] = reg_temp[i]; + data->reg_temp[1][src - 1] = reg_temp_over[i]; + data->reg_temp[2][src - 1] = reg_temp_hyst[i]; + if (reg_temp_crit_h && reg_temp_crit_h[i]) + data->reg_temp[3][src - 1] = reg_temp_crit_h[i]; + else if (reg_temp_crit[src - 1]) + data->reg_temp[3][src - 1] + = reg_temp_crit[src - 1]; + if (reg_temp_crit_l && reg_temp_crit_l[i]) + data->reg_temp[4][src - 1] = reg_temp_crit_l[i]; + data->reg_temp_config[src - 1] = reg_temp_config[i]; + data->temp_src[src - 1] = src; + continue; + } + + if (s >= NUM_TEMP) + continue; + + /* Use dynamic index for other sources */ + data->have_temp |= 1 << s; + data->reg_temp[0][s] = reg_temp[i]; + data->reg_temp[1][s] = reg_temp_over[i]; + data->reg_temp[2][s] = reg_temp_hyst[i]; + data->reg_temp_config[s] = reg_temp_config[i]; + if (reg_temp_crit_h && reg_temp_crit_h[i]) + data->reg_temp[3][s] = reg_temp_crit_h[i]; + else if (reg_temp_crit[src - 1]) + data->reg_temp[3][s] = reg_temp_crit[src - 1]; + if (reg_temp_crit_l && reg_temp_crit_l[i]) + data->reg_temp[4][s] = reg_temp_crit_l[i]; + + data->temp_src[s] = src; + s++; + } + + /* + * Repeat with temperatures used for fan control. + * This set of registers does not support limits. + */ + for (i = 0; i < num_reg_temp_mon; i++) { + if (reg_temp_mon[i] == 0) + continue; + + src = nct6775_read_value(data, data->REG_TEMP_SEL[i]) & 0x1f; + if (!src || (mask & (1 << src))) + continue; + + if (src >= data->temp_label_num || + !strlen(data->temp_label[src])) { + dev_info(dev, + "Invalid temperature source %d at index %d, source register 0x%x, temp register 0x%x\n", + src, i, data->REG_TEMP_SEL[i], + reg_temp_mon[i]); + continue; + } + + mask |= 1 << src; + + /* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */ + if (src <= data->temp_fixed_num) { + if (data->have_temp & (1 << (src - 1))) + continue; + data->have_temp |= 1 << (src - 1); + data->have_temp_fixed |= 1 << (src - 1); + data->reg_temp[0][src - 1] = reg_temp_mon[i]; + data->temp_src[src - 1] = src; + continue; + } + + if (s >= NUM_TEMP) + continue; + + /* Use dynamic index for other sources */ + data->have_temp |= 1 << s; + data->reg_temp[0][s] = reg_temp_mon[i]; + data->temp_src[s] = src; + s++; + } + +#ifdef USE_ALTERNATE + /* + * Go through the list of alternate temp registers and enable + * if possible. + * The temperature is already monitored if the respective bit in <mask> + * is set. + */ + for (i = 0; i < data->temp_label_num - 1; i++) { + if (!reg_temp_alternate[i]) + continue; + if (mask & (1 << (i + 1))) + continue; + if (i < data->temp_fixed_num) { + if (data->have_temp & (1 << i)) + continue; + data->have_temp |= 1 << i; + data->have_temp_fixed |= 1 << i; + data->reg_temp[0][i] = reg_temp_alternate[i]; + if (i < num_reg_temp) { + data->reg_temp[1][i] = reg_temp_over[i]; + data->reg_temp[2][i] = reg_temp_hyst[i]; + } + data->temp_src[i] = i + 1; + continue; + } + + if (s >= NUM_TEMP) /* Abort if no more space */ + break; + + data->have_temp |= 1 << s; + data->reg_temp[0][s] = reg_temp_alternate[i]; + data->temp_src[s] = i + 1; + s++; + } +#endif /* USE_ALTERNATE */ + + /* Initialize the chip */ + nct6775_init_device(data); + + err = superio_enter(sio_data->sioreg); + if (err) + return err; + + cr2a = superio_inb(sio_data->sioreg, 0x2a); + switch (data->kind) { + case nct6775: + data->have_vid = (cr2a & 0x40); + break; + case nct6776: + data->have_vid = (cr2a & 0x60) == 0x40; + break; + case nct6106: + case nct6779: + case nct6791: + break; + } + + /* + * Read VID value + * We can get the VID input values directly at logical device D 0xe3. + */ + if (data->have_vid) { + superio_select(sio_data->sioreg, NCT6775_LD_VID); + data->vid = superio_inb(sio_data->sioreg, 0xe3); + data->vrm = vid_which_vrm(); + } + + if (fan_debounce) { + u8 tmp; + + superio_select(sio_data->sioreg, NCT6775_LD_HWM); + tmp = superio_inb(sio_data->sioreg, + NCT6775_REG_CR_FAN_DEBOUNCE); + switch (data->kind) { + case nct6106: + tmp |= 0xe0; + break; + case nct6775: + tmp |= 0x1e; + break; + case nct6776: + case nct6779: + tmp |= 0x3e; + break; + case nct6791: + tmp |= 0x7e; + break; + } + superio_outb(sio_data->sioreg, NCT6775_REG_CR_FAN_DEBOUNCE, + tmp); + dev_info(&pdev->dev, "Enabled fan debounce for chip %s\n", + data->name); + } + + nct6775_check_fan_inputs(data); + + superio_exit(sio_data->sioreg); + + /* Read fan clock dividers immediately */ + nct6775_init_fan_common(dev, data); + + /* Register sysfs hooks */ + group = nct6775_create_attr_group(dev, &nct6775_pwm_template_group, + data->pwm_num); + if (IS_ERR(group)) + return PTR_ERR(group); + + data->groups[data->num_attr_groups++] = group; + + group = nct6775_create_attr_group(dev, &nct6775_in_template_group, + fls(data->have_in)); + if (IS_ERR(group)) + return PTR_ERR(group); + + data->groups[data->num_attr_groups++] = group; + + group = nct6775_create_attr_group(dev, &nct6775_fan_template_group, + fls(data->has_fan)); + if (IS_ERR(group)) + return PTR_ERR(group); + + data->groups[data->num_attr_groups++] = group; + + group = nct6775_create_attr_group(dev, &nct6775_temp_template_group, + fls(data->have_temp)); + if (IS_ERR(group)) + return PTR_ERR(group); + + data->groups[data->num_attr_groups++] = group; + data->groups[data->num_attr_groups++] = &nct6775_group_other; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, data->name, + data, data->groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static void nct6791_enable_io_mapping(int sioaddr) +{ + int val; + + val = superio_inb(sioaddr, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE); + if (val & 0x10) { + pr_info("Enabling hardware monitor logical device mappings.\n"); + superio_outb(sioaddr, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE, + val & ~0x10); + } +} + +#ifdef CONFIG_PM +static int nct6775_suspend(struct device *dev) +{ + struct nct6775_data *data = nct6775_update_device(dev); + + mutex_lock(&data->update_lock); + data->vbat = nct6775_read_value(data, data->REG_VBAT); + if (data->kind == nct6775) { + data->fandiv1 = nct6775_read_value(data, NCT6775_REG_FANDIV1); + data->fandiv2 = nct6775_read_value(data, NCT6775_REG_FANDIV2); + } + mutex_unlock(&data->update_lock); + + return 0; +} + +static int nct6775_resume(struct device *dev) +{ + struct nct6775_data *data = dev_get_drvdata(dev); + int i, j, err = 0; + + mutex_lock(&data->update_lock); + data->bank = 0xff; /* Force initial bank selection */ + + if (data->kind == nct6791) { + err = superio_enter(data->sioreg); + if (err) + goto abort; + + nct6791_enable_io_mapping(data->sioreg); + superio_exit(data->sioreg); + } + + /* Restore limits */ + for (i = 0; i < data->in_num; i++) { + if (!(data->have_in & (1 << i))) + continue; + + nct6775_write_value(data, data->REG_IN_MINMAX[0][i], + data->in[i][1]); + nct6775_write_value(data, data->REG_IN_MINMAX[1][i], + data->in[i][2]); + } + + for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) { + if (!(data->has_fan_min & (1 << i))) + continue; + + nct6775_write_value(data, data->REG_FAN_MIN[i], + data->fan_min[i]); + } + + for (i = 0; i < NUM_TEMP; i++) { + if (!(data->have_temp & (1 << i))) + continue; + + for (j = 1; j < ARRAY_SIZE(data->reg_temp); j++) + if (data->reg_temp[j][i]) + nct6775_write_temp(data, data->reg_temp[j][i], + data->temp[j][i]); + } + + /* Restore other settings */ + nct6775_write_value(data, data->REG_VBAT, data->vbat); + if (data->kind == nct6775) { + nct6775_write_value(data, NCT6775_REG_FANDIV1, data->fandiv1); + nct6775_write_value(data, NCT6775_REG_FANDIV2, data->fandiv2); + } + +abort: + /* Force re-reading all values */ + data->valid = false; + mutex_unlock(&data->update_lock); + + return err; +} + +static const struct dev_pm_ops nct6775_dev_pm_ops = { + .suspend = nct6775_suspend, + .resume = nct6775_resume, + .freeze = nct6775_suspend, + .restore = nct6775_resume, +}; + +#define NCT6775_DEV_PM_OPS (&nct6775_dev_pm_ops) +#else +#define NCT6775_DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + +static struct platform_driver nct6775_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, + .pm = NCT6775_DEV_PM_OPS, + }, + .probe = nct6775_probe, +}; + +static const char * const nct6775_sio_names[] __initconst = { + "NCT6106D", + "NCT6775F", + "NCT6776D/F", + "NCT6779D", + "NCT6791D", +}; + +/* nct6775_find() looks for a '627 in the Super-I/O config space */ +static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) +{ + u16 val; + int err; + int addr; + + err = superio_enter(sioaddr); + if (err) + return err; + + if (force_id) + val = force_id; + else + val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8) + | superio_inb(sioaddr, SIO_REG_DEVID + 1); + switch (val & SIO_ID_MASK) { + case SIO_NCT6106_ID: + sio_data->kind = nct6106; + break; + case SIO_NCT6775_ID: + sio_data->kind = nct6775; + break; + case SIO_NCT6776_ID: + sio_data->kind = nct6776; + break; + case SIO_NCT6779_ID: + sio_data->kind = nct6779; + break; + case SIO_NCT6791_ID: + sio_data->kind = nct6791; + break; + default: + if (val != 0xffff) + pr_debug("unsupported chip ID: 0x%04x\n", val); + superio_exit(sioaddr); + return -ENODEV; + } + + /* We have a known chip, find the HWM I/O address */ + superio_select(sioaddr, NCT6775_LD_HWM); + val = (superio_inb(sioaddr, SIO_REG_ADDR) << 8) + | superio_inb(sioaddr, SIO_REG_ADDR + 1); + addr = val & IOREGION_ALIGNMENT; + if (addr == 0) { + pr_err("Refusing to enable a Super-I/O device with a base I/O port 0\n"); + superio_exit(sioaddr); + return -ENODEV; + } + + /* Activate logical device if needed */ + val = superio_inb(sioaddr, SIO_REG_ENABLE); + if (!(val & 0x01)) { + pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n"); + superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01); + } + + if (sio_data->kind == nct6791) + nct6791_enable_io_mapping(sioaddr); + + superio_exit(sioaddr); + pr_info("Found %s or compatible chip at %#x:%#x\n", + nct6775_sio_names[sio_data->kind], sioaddr, addr); + sio_data->sioreg = sioaddr; + + return addr; +} + +/* + * when Super-I/O functions move to a separate file, the Super-I/O + * bus will manage the lifetime of the device and this module will only keep + * track of the nct6775 driver. But since we use platform_device_alloc(), we + * must keep track of the device + */ +static struct platform_device *pdev[2]; + +static int __init sensors_nct6775_init(void) +{ + int i, err; + bool found = false; + int address; + struct resource res; + struct nct6775_sio_data sio_data; + int sioaddr[2] = { 0x2e, 0x4e }; + + err = platform_driver_register(&nct6775_driver); + if (err) + return err; + + /* + * initialize sio_data->kind and sio_data->sioreg. + * + * when Super-I/O functions move to a separate file, the Super-I/O + * driver will probe 0x2e and 0x4e and auto-detect the presence of a + * nct6775 hardware monitor, and call probe() + */ + for (i = 0; i < ARRAY_SIZE(pdev); i++) { + address = nct6775_find(sioaddr[i], &sio_data); + if (address <= 0) + continue; + + found = true; + + pdev[i] = platform_device_alloc(DRVNAME, address); + if (!pdev[i]) { + err = -ENOMEM; + goto exit_device_unregister; + } + + err = platform_device_add_data(pdev[i], &sio_data, + sizeof(struct nct6775_sio_data)); + if (err) + goto exit_device_put; + + memset(&res, 0, sizeof(res)); + res.name = DRVNAME; + res.start = address + IOREGION_OFFSET; + res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1; + res.flags = IORESOURCE_IO; + + err = acpi_check_resource_conflict(&res); + if (err) { + platform_device_put(pdev[i]); + pdev[i] = NULL; + continue; + } + + err = platform_device_add_resources(pdev[i], &res, 1); + if (err) + goto exit_device_put; + + /* platform_device_add calls probe() */ + err = platform_device_add(pdev[i]); + if (err) + goto exit_device_put; + } + if (!found) { + err = -ENODEV; + goto exit_unregister; + } + + return 0; + +exit_device_put: + platform_device_put(pdev[i]); +exit_device_unregister: + while (--i >= 0) { + if (pdev[i]) + platform_device_unregister(pdev[i]); + } +exit_unregister: + platform_driver_unregister(&nct6775_driver); + return err; +} + +static void __exit sensors_nct6775_exit(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pdev); i++) { + if (pdev[i]) + platform_device_unregister(pdev[i]); + } + platform_driver_unregister(&nct6775_driver); +} + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("NCT6775F/NCT6776F/NCT6779D driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_nct6775_init); +module_exit(sensors_nct6775_exit); diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c new file mode 100644 index 00000000000..ae66f42c4d6 --- /dev/null +++ b/drivers/hwmon/ntc_thermistor.c @@ -0,0 +1,552 @@ +/* + * ntc_thermistor.c - NTC Thermistors + * + * Copyright (C) 2010 Samsung Electronics + * MyungJoo Ham <myungjoo.ham@samsung.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/slab.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/math64.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_device.h> + +#include <linux/platform_data/ntc_thermistor.h> + +#include <linux/iio/iio.h> +#include <linux/iio/machine.h> +#include <linux/iio/driver.h> +#include <linux/iio/consumer.h> + +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> + +struct ntc_compensation { + int temp_c; + unsigned int ohm; +}; + +/* Order matters, ntc_match references the entries by index */ +static const struct platform_device_id ntc_thermistor_id[] = { + { "ncp15wb473", TYPE_NCPXXWB473 }, + { "ncp18wb473", TYPE_NCPXXWB473 }, + { "ncp21wb473", TYPE_NCPXXWB473 }, + { "ncp03wb473", TYPE_NCPXXWB473 }, + { "ncp15wl333", TYPE_NCPXXWL333 }, + { }, +}; + +/* + * A compensation table should be sorted by the values of .ohm + * in descending order. + * The following compensation tables are from the specification of Murata NTC + * Thermistors Datasheet + */ +static const struct ntc_compensation ncpXXwb473[] = { + { .temp_c = -40, .ohm = 1747920 }, + { .temp_c = -35, .ohm = 1245428 }, + { .temp_c = -30, .ohm = 898485 }, + { .temp_c = -25, .ohm = 655802 }, + { .temp_c = -20, .ohm = 483954 }, + { .temp_c = -15, .ohm = 360850 }, + { .temp_c = -10, .ohm = 271697 }, + { .temp_c = -5, .ohm = 206463 }, + { .temp_c = 0, .ohm = 158214 }, + { .temp_c = 5, .ohm = 122259 }, + { .temp_c = 10, .ohm = 95227 }, + { .temp_c = 15, .ohm = 74730 }, + { .temp_c = 20, .ohm = 59065 }, + { .temp_c = 25, .ohm = 47000 }, + { .temp_c = 30, .ohm = 37643 }, + { .temp_c = 35, .ohm = 30334 }, + { .temp_c = 40, .ohm = 24591 }, + { .temp_c = 45, .ohm = 20048 }, + { .temp_c = 50, .ohm = 16433 }, + { .temp_c = 55, .ohm = 13539 }, + { .temp_c = 60, .ohm = 11209 }, + { .temp_c = 65, .ohm = 9328 }, + { .temp_c = 70, .ohm = 7798 }, + { .temp_c = 75, .ohm = 6544 }, + { .temp_c = 80, .ohm = 5518 }, + { .temp_c = 85, .ohm = 4674 }, + { .temp_c = 90, .ohm = 3972 }, + { .temp_c = 95, .ohm = 3388 }, + { .temp_c = 100, .ohm = 2902 }, + { .temp_c = 105, .ohm = 2494 }, + { .temp_c = 110, .ohm = 2150 }, + { .temp_c = 115, .ohm = 1860 }, + { .temp_c = 120, .ohm = 1615 }, + { .temp_c = 125, .ohm = 1406 }, +}; +static const struct ntc_compensation ncpXXwl333[] = { + { .temp_c = -40, .ohm = 1610154 }, + { .temp_c = -35, .ohm = 1130850 }, + { .temp_c = -30, .ohm = 802609 }, + { .temp_c = -25, .ohm = 575385 }, + { .temp_c = -20, .ohm = 416464 }, + { .temp_c = -15, .ohm = 304219 }, + { .temp_c = -10, .ohm = 224193 }, + { .temp_c = -5, .ohm = 166623 }, + { .temp_c = 0, .ohm = 124850 }, + { .temp_c = 5, .ohm = 94287 }, + { .temp_c = 10, .ohm = 71747 }, + { .temp_c = 15, .ohm = 54996 }, + { .temp_c = 20, .ohm = 42455 }, + { .temp_c = 25, .ohm = 33000 }, + { .temp_c = 30, .ohm = 25822 }, + { .temp_c = 35, .ohm = 20335 }, + { .temp_c = 40, .ohm = 16115 }, + { .temp_c = 45, .ohm = 12849 }, + { .temp_c = 50, .ohm = 10306 }, + { .temp_c = 55, .ohm = 8314 }, + { .temp_c = 60, .ohm = 6746 }, + { .temp_c = 65, .ohm = 5503 }, + { .temp_c = 70, .ohm = 4513 }, + { .temp_c = 75, .ohm = 3721 }, + { .temp_c = 80, .ohm = 3084 }, + { .temp_c = 85, .ohm = 2569 }, + { .temp_c = 90, .ohm = 2151 }, + { .temp_c = 95, .ohm = 1809 }, + { .temp_c = 100, .ohm = 1529 }, + { .temp_c = 105, .ohm = 1299 }, + { .temp_c = 110, .ohm = 1108 }, + { .temp_c = 115, .ohm = 949 }, + { .temp_c = 120, .ohm = 817 }, + { .temp_c = 125, .ohm = 707 }, +}; + +struct ntc_data { + struct device *hwmon_dev; + struct ntc_thermistor_platform_data *pdata; + const struct ntc_compensation *comp; + struct device *dev; + int n_comp; + char name[PLATFORM_NAME_SIZE]; +}; + +#if defined(CONFIG_OF) && IS_ENABLED(CONFIG_IIO) +static int ntc_adc_iio_read(struct ntc_thermistor_platform_data *pdata) +{ + struct iio_channel *channel = pdata->chan; + s64 result; + int val, ret; + + ret = iio_read_channel_raw(channel, &val); + if (ret < 0) { + pr_err("read channel() error: %d\n", ret); + return ret; + } + + /* unit: mV */ + result = pdata->pullup_uv * (s64) val; + result >>= 12; + + return (int)result; +} + +static const struct of_device_id ntc_match[] = { + { .compatible = "murata,ncp15wb473", + .data = &ntc_thermistor_id[0] }, + { .compatible = "murata,ncp18wb473", + .data = &ntc_thermistor_id[1] }, + { .compatible = "murata,ncp21wb473", + .data = &ntc_thermistor_id[2] }, + { .compatible = "murata,ncp03wb473", + .data = &ntc_thermistor_id[3] }, + { .compatible = "murata,ncp15wl333", + .data = &ntc_thermistor_id[4] }, + + /* Usage of vendor name "ntc" is deprecated */ + { .compatible = "ntc,ncp15wb473", + .data = &ntc_thermistor_id[0] }, + { .compatible = "ntc,ncp18wb473", + .data = &ntc_thermistor_id[1] }, + { .compatible = "ntc,ncp21wb473", + .data = &ntc_thermistor_id[2] }, + { .compatible = "ntc,ncp03wb473", + .data = &ntc_thermistor_id[3] }, + { .compatible = "ntc,ncp15wl333", + .data = &ntc_thermistor_id[4] }, + { }, +}; +MODULE_DEVICE_TABLE(of, ntc_match); + +static struct ntc_thermistor_platform_data * +ntc_thermistor_parse_dt(struct platform_device *pdev) +{ + struct iio_channel *chan; + struct device_node *np = pdev->dev.of_node; + struct ntc_thermistor_platform_data *pdata; + + if (!np) + return NULL; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + chan = iio_channel_get(&pdev->dev, NULL); + if (IS_ERR(chan)) + return ERR_CAST(chan); + + if (of_property_read_u32(np, "pullup-uv", &pdata->pullup_uv)) + return ERR_PTR(-ENODEV); + if (of_property_read_u32(np, "pullup-ohm", &pdata->pullup_ohm)) + return ERR_PTR(-ENODEV); + if (of_property_read_u32(np, "pulldown-ohm", &pdata->pulldown_ohm)) + return ERR_PTR(-ENODEV); + + if (of_find_property(np, "connected-positive", NULL)) + pdata->connect = NTC_CONNECTED_POSITIVE; + else /* status change should be possible if not always on. */ + pdata->connect = NTC_CONNECTED_GROUND; + + pdata->chan = chan; + pdata->read_uv = ntc_adc_iio_read; + + return pdata; +} +static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata) +{ + if (pdata->chan) + iio_channel_release(pdata->chan); +} +#else +static struct ntc_thermistor_platform_data * +ntc_thermistor_parse_dt(struct platform_device *pdev) +{ + return NULL; +} + +#define ntc_match NULL + +static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata) +{ } +#endif + +static inline u64 div64_u64_safe(u64 dividend, u64 divisor) +{ + if (divisor == 0 && dividend == 0) + return 0; + if (divisor == 0) + return UINT_MAX; + return div64_u64(dividend, divisor); +} + +static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uv) +{ + struct ntc_thermistor_platform_data *pdata = data->pdata; + u64 mv = uv / 1000; + u64 pmv = pdata->pullup_uv / 1000; + u64 n, puo, pdo; + puo = pdata->pullup_ohm; + pdo = pdata->pulldown_ohm; + + if (mv == 0) { + if (pdata->connect == NTC_CONNECTED_POSITIVE) + return INT_MAX; + return 0; + } + if (mv >= pmv) + return (pdata->connect == NTC_CONNECTED_POSITIVE) ? + 0 : INT_MAX; + + if (pdata->connect == NTC_CONNECTED_POSITIVE && puo == 0) + n = div64_u64_safe(pdo * (pmv - mv), mv); + else if (pdata->connect == NTC_CONNECTED_GROUND && pdo == 0) + n = div64_u64_safe(puo * mv, pmv - mv); + else if (pdata->connect == NTC_CONNECTED_POSITIVE) + n = div64_u64_safe(pdo * puo * (pmv - mv), + puo * mv - pdo * (pmv - mv)); + else + n = div64_u64_safe(pdo * puo * mv, pdo * (pmv - mv) - puo * mv); + + if (n > INT_MAX) + n = INT_MAX; + return n; +} + +static void lookup_comp(struct ntc_data *data, unsigned int ohm, + int *i_low, int *i_high) +{ + int start, end, mid; + + /* + * Handle special cases: Resistance is higher than or equal to + * resistance in first table entry, or resistance is lower or equal + * to resistance in last table entry. + * In these cases, return i_low == i_high, either pointing to the + * beginning or to the end of the table depending on the condition. + */ + if (ohm >= data->comp[0].ohm) { + *i_low = 0; + *i_high = 0; + return; + } + if (ohm <= data->comp[data->n_comp - 1].ohm) { + *i_low = data->n_comp - 1; + *i_high = data->n_comp - 1; + return; + } + + /* Do a binary search on compensation table */ + start = 0; + end = data->n_comp; + while (start < end) { + mid = start + (end - start) / 2; + /* + * start <= mid < end + * data->comp[start].ohm > ohm >= data->comp[end].ohm + * + * We could check for "ohm == data->comp[mid].ohm" here, but + * that is a quite unlikely condition, and we would have to + * check again after updating start. Check it at the end instead + * for simplicity. + */ + if (ohm >= data->comp[mid].ohm) { + end = mid; + } else { + start = mid + 1; + /* + * ohm >= data->comp[start].ohm might be true here, + * since we set start to mid + 1. In that case, we are + * done. We could keep going, but the condition is quite + * likely to occur, so it is worth checking for it. + */ + if (ohm >= data->comp[start].ohm) + end = start; + } + /* + * start <= end + * data->comp[start].ohm >= ohm >= data->comp[end].ohm + */ + } + /* + * start == end + * ohm >= data->comp[end].ohm + */ + *i_low = end; + if (ohm == data->comp[end].ohm) + *i_high = end; + else + *i_high = end - 1; +} + +static int get_temp_mc(struct ntc_data *data, unsigned int ohm) +{ + int low, high; + int temp; + + lookup_comp(data, ohm, &low, &high); + if (low == high) { + /* Unable to use linear approximation */ + temp = data->comp[low].temp_c * 1000; + } else { + temp = data->comp[low].temp_c * 1000 + + ((data->comp[high].temp_c - data->comp[low].temp_c) * + 1000 * ((int)ohm - (int)data->comp[low].ohm)) / + ((int)data->comp[high].ohm - (int)data->comp[low].ohm); + } + return temp; +} + +static int ntc_thermistor_get_ohm(struct ntc_data *data) +{ + int read_uv; + + if (data->pdata->read_ohm) + return data->pdata->read_ohm(); + + if (data->pdata->read_uv) { + read_uv = data->pdata->read_uv(data->pdata); + if (read_uv < 0) + return read_uv; + return get_ohm_of_thermistor(data, read_uv); + } + return -EINVAL; +} + +static ssize_t ntc_show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ntc_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", data->name); +} + +static ssize_t ntc_show_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "4\n"); +} + +static ssize_t ntc_show_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ntc_data *data = dev_get_drvdata(dev); + int ohm; + + ohm = ntc_thermistor_get_ohm(data); + if (ohm < 0) + return ohm; + + return sprintf(buf, "%d\n", get_temp_mc(data, ohm)); +} + +static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ntc_show_temp, NULL, 0); +static DEVICE_ATTR(name, S_IRUGO, ntc_show_name, NULL); + +static struct attribute *ntc_attributes[] = { + &dev_attr_name.attr, + &sensor_dev_attr_temp1_type.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ntc_attr_group = { + .attrs = ntc_attributes, +}; + +static int ntc_thermistor_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_id = + of_match_device(of_match_ptr(ntc_match), &pdev->dev); + const struct platform_device_id *pdev_id; + struct ntc_thermistor_platform_data *pdata; + struct ntc_data *data; + int ret; + + pdata = ntc_thermistor_parse_dt(pdev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + else if (pdata == NULL) + pdata = dev_get_platdata(&pdev->dev); + + if (!pdata) { + dev_err(&pdev->dev, "No platform init data supplied.\n"); + return -ENODEV; + } + + /* Either one of the two is required. */ + if (!pdata->read_uv && !pdata->read_ohm) { + dev_err(&pdev->dev, + "Both read_uv and read_ohm missing. Need either one of the two.\n"); + return -EINVAL; + } + + if (pdata->read_uv && pdata->read_ohm) { + dev_warn(&pdev->dev, + "Only one of read_uv and read_ohm is needed; ignoring read_uv.\n"); + pdata->read_uv = NULL; + } + + if (pdata->read_uv && (pdata->pullup_uv == 0 || + (pdata->pullup_ohm == 0 && pdata->connect == + NTC_CONNECTED_GROUND) || + (pdata->pulldown_ohm == 0 && pdata->connect == + NTC_CONNECTED_POSITIVE) || + (pdata->connect != NTC_CONNECTED_POSITIVE && + pdata->connect != NTC_CONNECTED_GROUND))) { + dev_err(&pdev->dev, + "Required data to use read_uv not supplied.\n"); + return -EINVAL; + } + + data = devm_kzalloc(&pdev->dev, sizeof(struct ntc_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + pdev_id = of_id ? of_id->data : platform_get_device_id(pdev); + + data->dev = &pdev->dev; + data->pdata = pdata; + strlcpy(data->name, pdev_id->name, sizeof(data->name)); + + switch (pdev_id->driver_data) { + case TYPE_NCPXXWB473: + data->comp = ncpXXwb473; + data->n_comp = ARRAY_SIZE(ncpXXwb473); + break; + case TYPE_NCPXXWL333: + data->comp = ncpXXwl333; + data->n_comp = ARRAY_SIZE(ncpXXwl333); + break; + default: + dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n", + pdev_id->driver_data, pdev_id->name); + return -EINVAL; + } + + platform_set_drvdata(pdev, data); + + ret = sysfs_create_group(&data->dev->kobj, &ntc_attr_group); + if (ret) { + dev_err(data->dev, "unable to create sysfs files\n"); + return ret; + } + + data->hwmon_dev = hwmon_device_register(data->dev); + if (IS_ERR(data->hwmon_dev)) { + dev_err(data->dev, "unable to register as hwmon device.\n"); + ret = PTR_ERR(data->hwmon_dev); + goto err_after_sysfs; + } + + dev_info(&pdev->dev, "Thermistor type: %s successfully probed.\n", + pdev_id->name); + + return 0; +err_after_sysfs: + sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); + ntc_iio_channel_release(pdata); + return ret; +} + +static int ntc_thermistor_remove(struct platform_device *pdev) +{ + struct ntc_data *data = platform_get_drvdata(pdev); + struct ntc_thermistor_platform_data *pdata = data->pdata; + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); + ntc_iio_channel_release(pdata); + + return 0; +} + +static struct platform_driver ntc_thermistor_driver = { + .driver = { + .name = "ntc-thermistor", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ntc_match), + }, + .probe = ntc_thermistor_probe, + .remove = ntc_thermistor_remove, + .id_table = ntc_thermistor_id, +}; + +module_platform_driver(ntc_thermistor_driver); + +MODULE_DESCRIPTION("NTC Thermistor Driver from Murata"); +MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ntc-thermistor"); diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index 4a64b85d4ec..988181e4cfc 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c @@ -1,7 +1,7 @@ /* * pc87360.c - Part of lm_sensors, Linux kernel modules * for hardware monitoring - * Copyright (C) 2004, 2007 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2004, 2007 Jean Delvare <jdelvare@suse.de> * * Copied from smsc47m1.c: * Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com> @@ -33,6 +33,8 @@ * the standard Super-I/O addresses is used (0x2E/0x2F or 0x4E/0x4F). */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> @@ -54,11 +56,11 @@ static u8 confreg[4]; static int init = 1; module_param(init, int, 0); MODULE_PARM_DESC(init, - "Chip initialization level:\n" - " 0: None\n" - "*1: Forcibly enable internal voltage and temperature channels, except in9\n" - " 2: Forcibly enable all voltage and temperature channels, except in9\n" - " 3: Forcibly enable all voltage and temperature channels, including in9"); +"Chip initialization level:\n" +" 0: None\n" +"*1: Forcibly enable internal voltage and temperature channels, except in9\n" +" 2: Forcibly enable all voltage and temperature channels, except in9\n" +" 3: Forcibly enable all voltage and temperature channels, including in9"); static unsigned short force_id; module_param(force_id, ushort, 0); @@ -86,19 +88,19 @@ static const u8 logdev[LDNI_MAX] = { FSCM, VLM, TMS }; static inline void superio_outb(int sioaddr, int reg, int val) { outb(reg, sioaddr); - outb(val, sioaddr+1); + outb(val, sioaddr + 1); } static inline int superio_inb(int sioaddr, int reg) { outb(reg, sioaddr); - return inb(sioaddr+1); + return inb(sioaddr + 1); } static inline void superio_exit(int sioaddr) { outb(0x02, sioaddr); - outb(0x02, sioaddr+1); + outb(0x02, sioaddr + 1); } /* @@ -120,18 +122,18 @@ static inline void superio_exit(int sioaddr) #define PC87360_REG_FAN(nr) (0x07 + 3 * (nr)) #define PC87360_REG_FAN_STATUS(nr) (0x08 + 3 * (nr)) -#define FAN_FROM_REG(val,div) ((val) == 0 ? 0: \ - 480000 / ((val)*(div))) -#define FAN_TO_REG(val,div) ((val) <= 100 ? 0 : \ - 480000 / ((val)*(div))) -#define FAN_DIV_FROM_REG(val) (1 << ((val >> 5) & 0x03)) +#define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : \ + 480000 / ((val) * (div))) +#define FAN_TO_REG(val, div) ((val) <= 100 ? 0 : \ + 480000 / ((val) * (div))) +#define FAN_DIV_FROM_REG(val) (1 << (((val) >> 5) & 0x03)) #define FAN_STATUS_FROM_REG(val) ((val) & 0x07) -#define FAN_CONFIG_MONITOR(val,nr) (((val) >> (2 + nr * 3)) & 1) -#define FAN_CONFIG_CONTROL(val,nr) (((val) >> (3 + nr * 3)) & 1) -#define FAN_CONFIG_INVERT(val,nr) (((val) >> (4 + nr * 3)) & 1) +#define FAN_CONFIG_MONITOR(val, nr) (((val) >> (2 + (nr) * 3)) & 1) +#define FAN_CONFIG_CONTROL(val, nr) (((val) >> (3 + (nr) * 3)) & 1) +#define FAN_CONFIG_INVERT(val, nr) (((val) >> (4 + (nr) * 3)) & 1) -#define PWM_FROM_REG(val,inv) ((inv) ? 255 - (val) : (val)) +#define PWM_FROM_REG(val, inv) ((inv) ? 255 - (val) : (val)) static inline u8 PWM_TO_REG(int val, int inv) { if (inv) @@ -157,10 +159,10 @@ static inline u8 PWM_TO_REG(int val, int inv) #define PC87365_REG_IN_ALARMS2 0x01 #define PC87365_REG_VID 0x06 -#define IN_FROM_REG(val,ref) (((val) * (ref) + 128) / 256) -#define IN_TO_REG(val,ref) ((val) < 0 ? 0 : \ - (val)*256 >= (ref)*255 ? 255: \ - ((val) * 256 + (ref)/2) / (ref)) +#define IN_FROM_REG(val, ref) (((val) * (ref) + 128) / 256) +#define IN_TO_REG(val, ref) ((val) < 0 ? 0 : \ + (val) * 256 >= (ref) * 255 ? 255 : \ + ((val) * 256 + (ref) / 2) / (ref)) /* * Temperature registers and conversions @@ -226,7 +228,7 @@ struct pc87360_data { */ static int pc87360_probe(struct platform_device *pdev); -static int __devexit pc87360_remove(struct platform_device *pdev); +static int pc87360_remove(struct platform_device *pdev); static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank, u8 reg); @@ -246,50 +248,61 @@ static struct platform_driver pc87360_driver = { .name = "pc87360", }, .probe = pc87360_probe, - .remove = __devexit_p(pc87360_remove), + .remove = pc87360_remove, }; /* * Sysfs stuff */ -static ssize_t show_fan_input(struct device *dev, struct device_attribute *devattr, char *buf) +static ssize_t show_fan_input(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan[attr->index], FAN_DIV_FROM_REG(data->fan_status[attr->index]))); } -static ssize_t show_fan_min(struct device *dev, struct device_attribute *devattr, char *buf) +static ssize_t show_fan_min(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan_min[attr->index], FAN_DIV_FROM_REG(data->fan_status[attr->index]))); } -static ssize_t show_fan_div(struct device *dev, struct device_attribute *devattr, char *buf) +static ssize_t show_fan_div(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", FAN_DIV_FROM_REG(data->fan_status[attr->index])); } -static ssize_t show_fan_status(struct device *dev, struct device_attribute *devattr, char *buf) +static ssize_t show_fan_status(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", FAN_STATUS_FROM_REG(data->fan_status[attr->index])); } -static ssize_t set_fan_min(struct device *dev, struct device_attribute *devattr, const char *buf, +static ssize_t set_fan_min(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); - long fan_min = simple_strtol(buf, NULL, 10); + long fan_min; + int err; + + err = kstrtol(buf, 10, &fan_min); + if (err) + return err; mutex_lock(&data->update_lock); - fan_min = FAN_TO_REG(fan_min, FAN_DIV_FROM_REG(data->fan_status[attr->index])); + fan_min = FAN_TO_REG(fan_min, + FAN_DIV_FROM_REG(data->fan_status[attr->index])); /* If it wouldn't fit, change clock divisor */ while (fan_min > 255 @@ -299,11 +312,13 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *devattr, data->fan_status[attr->index] += 0x20; } data->fan_min[attr->index] = fan_min > 255 ? 255 : fan_min; - pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_FAN_MIN(attr->index), + pc87360_write_value(data, LD_FAN, NO_BANK, + PC87360_REG_FAN_MIN(attr->index), data->fan_min[attr->index]); /* Write new divider, preserve alarm bits */ - pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_FAN_STATUS(attr->index), + pc87360_write_value(data, LD_FAN, NO_BANK, + PC87360_REG_FAN_STATUS(attr->index), data->fan_status[attr->index] & 0xF9); mutex_unlock(&data->update_lock); @@ -331,13 +346,16 @@ static struct sensor_device_attribute fan_min[] = { SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 2), }; -#define FAN_UNIT_ATTRS(X) \ - &fan_input[X].dev_attr.attr, \ +#define FAN_UNIT_ATTRS(X) \ +{ &fan_input[X].dev_attr.attr, \ &fan_status[X].dev_attr.attr, \ &fan_div[X].dev_attr.attr, \ - &fan_min[X].dev_attr.attr + &fan_min[X].dev_attr.attr, \ + NULL \ +} -static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, char *buf) +static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, + char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); @@ -346,12 +364,17 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, ch FAN_CONFIG_INVERT(data->fan_conf, attr->index))); } -static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, const char *buf, - size_t count) +static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->pwm[attr->index] = PWM_TO_REG(val, @@ -368,52 +391,60 @@ static struct sensor_device_attribute pwm[] = { SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2), }; -static struct attribute * pc8736x_fan_attr_array[] = { +static struct attribute *pc8736x_fan_attr[][5] = { FAN_UNIT_ATTRS(0), FAN_UNIT_ATTRS(1), - FAN_UNIT_ATTRS(2), - &pwm[0].dev_attr.attr, - &pwm[1].dev_attr.attr, - &pwm[2].dev_attr.attr, - NULL + FAN_UNIT_ATTRS(2) }; -static const struct attribute_group pc8736x_fan_group = { - .attrs = pc8736x_fan_attr_array, + +static const struct attribute_group pc8736x_fan_attr_group[] = { + { .attrs = pc8736x_fan_attr[0], }, + { .attrs = pc8736x_fan_attr[1], }, + { .attrs = pc8736x_fan_attr[2], }, }; -static ssize_t show_in_input(struct device *dev, struct device_attribute *devattr, char *buf) +static ssize_t show_in_input(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", IN_FROM_REG(data->in[attr->index], data->in_vref)); } -static ssize_t show_in_min(struct device *dev, struct device_attribute *devattr, char *buf) +static ssize_t show_in_min(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index], data->in_vref)); } -static ssize_t show_in_max(struct device *dev, struct device_attribute *devattr, char *buf) +static ssize_t show_in_max(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index], data->in_vref)); } -static ssize_t show_in_status(struct device *dev, struct device_attribute *devattr, char *buf) +static ssize_t show_in_status(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", data->in_status[attr->index]); } -static ssize_t set_in_min(struct device *dev, struct device_attribute *devattr, const char *buf, - size_t count) +static ssize_t set_in_min(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_min[attr->index] = IN_TO_REG(val, data->in_vref); @@ -422,12 +453,17 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *devattr, mutex_unlock(&data->update_lock); return count; } -static ssize_t set_in_max(struct device *dev, struct device_attribute *devattr, const char *buf, - size_t count) +static ssize_t set_in_max(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_max[attr->index] = IN_TO_REG(val, @@ -496,9 +532,11 @@ static struct sensor_device_attribute in_max[] = { #define CHAN_ALM_MAX 0x04 /* max limit exceeded */ #define TEMP_ALM_CRIT 0x08 /* temp crit exceeded (temp only) */ -/* show_in_min/max_alarm() reads data from the per-channel status - register (sec 11.5.12), not the vin event status registers (sec - 11.5.2) that (legacy) show_in_alarm() resds (via data->in_alarms) */ +/* + * show_in_min/max_alarm() reads data from the per-channel status + * register (sec 11.5.12), not the vin event status registers (sec + * 11.5.2) that (legacy) show_in_alarm() resds (via data->in_alarms) + */ static ssize_t show_in_min_alarm(struct device *dev, struct device_attribute *devattr, char *buf) @@ -552,27 +590,38 @@ static struct sensor_device_attribute in_max_alarm[] = { &in_min_alarm[X].dev_attr.attr, \ &in_max_alarm[X].dev_attr.attr -static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_vid(struct device *dev, struct device_attribute *attr, + char *buf) { struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm)); } static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); -static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, + char *buf) { struct pc87360_data *data = dev_get_drvdata(dev); return sprintf(buf, "%u\n", data->vrm); } -static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct pc87360_data *data = dev_get_drvdata(dev); - data->vrm = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + data->vrm = val; return count; } static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); -static ssize_t show_in_alarms(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_in_alarms(struct device *dev, + struct device_attribute *attr, char *buf) { struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", data->in_alarms); @@ -600,46 +649,58 @@ static const struct attribute_group pc8736x_vin_group = { .attrs = pc8736x_vin_attr_array, }; -static ssize_t show_therm_input(struct device *dev, struct device_attribute *devattr, char *buf) +static ssize_t show_therm_input(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", IN_FROM_REG(data->in[attr->index], data->in_vref)); } -static ssize_t show_therm_min(struct device *dev, struct device_attribute *devattr, char *buf) +static ssize_t show_therm_min(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index], data->in_vref)); } -static ssize_t show_therm_max(struct device *dev, struct device_attribute *devattr, char *buf) +static ssize_t show_therm_max(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index], data->in_vref)); } -static ssize_t show_therm_crit(struct device *dev, struct device_attribute *devattr, char *buf) +static ssize_t show_therm_crit(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", IN_FROM_REG(data->in_crit[attr->index-11], data->in_vref)); } -static ssize_t show_therm_status(struct device *dev, struct device_attribute *devattr, char *buf) +static ssize_t show_therm_status(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", data->in_status[attr->index]); } -static ssize_t set_therm_min(struct device *dev, struct device_attribute *devattr, const char *buf, - size_t count) + +static ssize_t set_therm_min(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_min[attr->index] = IN_TO_REG(val, data->in_vref); @@ -648,12 +709,19 @@ static ssize_t set_therm_min(struct device *dev, struct device_attribute *devatt mutex_unlock(&data->update_lock); return count; } -static ssize_t set_therm_max(struct device *dev, struct device_attribute *devattr, const char *buf, - size_t count) + +static ssize_t set_therm_max(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_max[attr->index] = IN_TO_REG(val, data->in_vref); @@ -662,12 +730,18 @@ static ssize_t set_therm_max(struct device *dev, struct device_attribute *devatt mutex_unlock(&data->update_lock); return count; } -static ssize_t set_therm_crit(struct device *dev, struct device_attribute *devattr, const char *buf, - size_t count) +static ssize_t set_therm_crit(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_crit[attr->index-11] = IN_TO_REG(val, data->in_vref); @@ -677,46 +751,49 @@ static ssize_t set_therm_crit(struct device *dev, struct device_attribute *devat return count; } -/* the +11 term below reflects the fact that VLM units 11,12,13 are - used in the chip to measure voltage across the thermistors -*/ +/* + * the +11 term below reflects the fact that VLM units 11,12,13 are + * used in the chip to measure voltage across the thermistors + */ static struct sensor_device_attribute therm_input[] = { - SENSOR_ATTR(temp4_input, S_IRUGO, show_therm_input, NULL, 0+11), - SENSOR_ATTR(temp5_input, S_IRUGO, show_therm_input, NULL, 1+11), - SENSOR_ATTR(temp6_input, S_IRUGO, show_therm_input, NULL, 2+11), + SENSOR_ATTR(temp4_input, S_IRUGO, show_therm_input, NULL, 0 + 11), + SENSOR_ATTR(temp5_input, S_IRUGO, show_therm_input, NULL, 1 + 11), + SENSOR_ATTR(temp6_input, S_IRUGO, show_therm_input, NULL, 2 + 11), }; static struct sensor_device_attribute therm_status[] = { - SENSOR_ATTR(temp4_status, S_IRUGO, show_therm_status, NULL, 0+11), - SENSOR_ATTR(temp5_status, S_IRUGO, show_therm_status, NULL, 1+11), - SENSOR_ATTR(temp6_status, S_IRUGO, show_therm_status, NULL, 2+11), + SENSOR_ATTR(temp4_status, S_IRUGO, show_therm_status, NULL, 0 + 11), + SENSOR_ATTR(temp5_status, S_IRUGO, show_therm_status, NULL, 1 + 11), + SENSOR_ATTR(temp6_status, S_IRUGO, show_therm_status, NULL, 2 + 11), }; static struct sensor_device_attribute therm_min[] = { SENSOR_ATTR(temp4_min, S_IRUGO | S_IWUSR, - show_therm_min, set_therm_min, 0+11), + show_therm_min, set_therm_min, 0 + 11), SENSOR_ATTR(temp5_min, S_IRUGO | S_IWUSR, - show_therm_min, set_therm_min, 1+11), + show_therm_min, set_therm_min, 1 + 11), SENSOR_ATTR(temp6_min, S_IRUGO | S_IWUSR, - show_therm_min, set_therm_min, 2+11), + show_therm_min, set_therm_min, 2 + 11), }; static struct sensor_device_attribute therm_max[] = { SENSOR_ATTR(temp4_max, S_IRUGO | S_IWUSR, - show_therm_max, set_therm_max, 0+11), + show_therm_max, set_therm_max, 0 + 11), SENSOR_ATTR(temp5_max, S_IRUGO | S_IWUSR, - show_therm_max, set_therm_max, 1+11), + show_therm_max, set_therm_max, 1 + 11), SENSOR_ATTR(temp6_max, S_IRUGO | S_IWUSR, - show_therm_max, set_therm_max, 2+11), + show_therm_max, set_therm_max, 2 + 11), }; static struct sensor_device_attribute therm_crit[] = { SENSOR_ATTR(temp4_crit, S_IRUGO | S_IWUSR, - show_therm_crit, set_therm_crit, 0+11), + show_therm_crit, set_therm_crit, 0 + 11), SENSOR_ATTR(temp5_crit, S_IRUGO | S_IWUSR, - show_therm_crit, set_therm_crit, 1+11), + show_therm_crit, set_therm_crit, 1 + 11), SENSOR_ATTR(temp6_crit, S_IRUGO | S_IWUSR, - show_therm_crit, set_therm_crit, 2+11), + show_therm_crit, set_therm_crit, 2 + 11), }; -/* show_therm_min/max_alarm() reads data from the per-channel voltage - status register (sec 11.5.12) */ +/* + * show_therm_min/max_alarm() reads data from the per-channel voltage + * status register (sec 11.5.12) + */ static ssize_t show_therm_min_alarm(struct device *dev, struct device_attribute *devattr, char *buf) @@ -745,27 +822,27 @@ static ssize_t show_therm_crit_alarm(struct device *dev, static struct sensor_device_attribute therm_min_alarm[] = { SENSOR_ATTR(temp4_min_alarm, S_IRUGO, - show_therm_min_alarm, NULL, 0+11), + show_therm_min_alarm, NULL, 0 + 11), SENSOR_ATTR(temp5_min_alarm, S_IRUGO, - show_therm_min_alarm, NULL, 1+11), + show_therm_min_alarm, NULL, 1 + 11), SENSOR_ATTR(temp6_min_alarm, S_IRUGO, - show_therm_min_alarm, NULL, 2+11), + show_therm_min_alarm, NULL, 2 + 11), }; static struct sensor_device_attribute therm_max_alarm[] = { SENSOR_ATTR(temp4_max_alarm, S_IRUGO, - show_therm_max_alarm, NULL, 0+11), + show_therm_max_alarm, NULL, 0 + 11), SENSOR_ATTR(temp5_max_alarm, S_IRUGO, - show_therm_max_alarm, NULL, 1+11), + show_therm_max_alarm, NULL, 1 + 11), SENSOR_ATTR(temp6_max_alarm, S_IRUGO, - show_therm_max_alarm, NULL, 2+11), + show_therm_max_alarm, NULL, 2 + 11), }; static struct sensor_device_attribute therm_crit_alarm[] = { SENSOR_ATTR(temp4_crit_alarm, S_IRUGO, - show_therm_crit_alarm, NULL, 0+11), + show_therm_crit_alarm, NULL, 0 + 11), SENSOR_ATTR(temp5_crit_alarm, S_IRUGO, - show_therm_crit_alarm, NULL, 1+11), + show_therm_crit_alarm, NULL, 1 + 11), SENSOR_ATTR(temp6_crit_alarm, S_IRUGO, - show_therm_crit_alarm, NULL, 2+11), + show_therm_crit_alarm, NULL, 2 + 11), }; #define THERM_UNIT_ATTRS(X) \ @@ -778,7 +855,7 @@ static struct sensor_device_attribute therm_crit_alarm[] = { &therm_max_alarm[X].dev_attr.attr, \ &therm_crit_alarm[X].dev_attr.attr -static struct attribute * pc8736x_therm_attr_array[] = { +static struct attribute *pc8736x_therm_attr_array[] = { THERM_UNIT_ATTRS(0), THERM_UNIT_ATTRS(1), THERM_UNIT_ATTRS(2), @@ -788,42 +865,59 @@ static const struct attribute_group pc8736x_therm_group = { .attrs = pc8736x_therm_attr_array, }; -static ssize_t show_temp_input(struct device *dev, struct device_attribute *devattr, char *buf) +static ssize_t show_temp_input(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])); } -static ssize_t show_temp_min(struct device *dev, struct device_attribute *devattr, char *buf) + +static ssize_t show_temp_min(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[attr->index])); } -static ssize_t show_temp_max(struct device *dev, struct device_attribute *devattr, char *buf) + +static ssize_t show_temp_max(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[attr->index])); } -static ssize_t show_temp_crit(struct device *dev, struct device_attribute *devattr, char *buf) + +static ssize_t show_temp_crit(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit[attr->index])); + return sprintf(buf, "%d\n", + TEMP_FROM_REG(data->temp_crit[attr->index])); } -static ssize_t show_temp_status(struct device *dev, struct device_attribute *devattr, char *buf) + +static ssize_t show_temp_status(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%d\n", data->temp_status[attr->index]); } -static ssize_t set_temp_min(struct device *dev, struct device_attribute *devattr, const char *buf, - size_t count) + +static ssize_t set_temp_min(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_min[attr->index] = TEMP_TO_REG(val); @@ -832,12 +926,19 @@ static ssize_t set_temp_min(struct device *dev, struct device_attribute *devattr mutex_unlock(&data->update_lock); return count; } -static ssize_t set_temp_max(struct device *dev, struct device_attribute *devattr, const char *buf, - size_t count) + +static ssize_t set_temp_max(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_max[attr->index] = TEMP_TO_REG(val); @@ -846,12 +947,19 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *devattr mutex_unlock(&data->update_lock); return count; } -static ssize_t set_temp_crit(struct device *dev, struct device_attribute *devattr, const char *buf, - size_t count) + +static ssize_t set_temp_crit(struct device *dev, + struct device_attribute *devattr, const char *buf, + size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_crit[attr->index] = TEMP_TO_REG(val); @@ -896,16 +1004,20 @@ static struct sensor_device_attribute temp_crit[] = { show_temp_crit, set_temp_crit, 2), }; -static ssize_t show_temp_alarms(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_temp_alarms(struct device *dev, + struct device_attribute *attr, char *buf) { struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", data->temp_alarms); } + static DEVICE_ATTR(alarms_temp, S_IRUGO, show_temp_alarms, NULL); -/* show_temp_min/max_alarm() reads data from the per-channel status - register (sec 12.3.7), not the temp event status registers (sec - 12.3.2) that show_temp_alarm() reads (via data->temp_alarms) */ +/* + * show_temp_min/max_alarm() reads data from the per-channel status + * register (sec 12.3.7), not the temp event status registers (sec + * 12.3.2) that show_temp_alarm() reads (via data->temp_alarms) + */ static ssize_t show_temp_min_alarm(struct device *dev, struct device_attribute *devattr, char *buf) @@ -915,6 +1027,7 @@ static ssize_t show_temp_min_alarm(struct device *dev, return sprintf(buf, "%u\n", !!(data->temp_status[nr] & CHAN_ALM_MIN)); } + static ssize_t show_temp_max_alarm(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -923,6 +1036,7 @@ static ssize_t show_temp_max_alarm(struct device *dev, return sprintf(buf, "%u\n", !!(data->temp_status[nr] & CHAN_ALM_MAX)); } + static ssize_t show_temp_crit_alarm(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -937,11 +1051,13 @@ static struct sensor_device_attribute temp_min_alarm[] = { SENSOR_ATTR(temp2_min_alarm, S_IRUGO, show_temp_min_alarm, NULL, 1), SENSOR_ATTR(temp3_min_alarm, S_IRUGO, show_temp_min_alarm, NULL, 2), }; + static struct sensor_device_attribute temp_max_alarm[] = { SENSOR_ATTR(temp1_max_alarm, S_IRUGO, show_temp_max_alarm, NULL, 0), SENSOR_ATTR(temp2_max_alarm, S_IRUGO, show_temp_max_alarm, NULL, 1), SENSOR_ATTR(temp3_max_alarm, S_IRUGO, show_temp_max_alarm, NULL, 2), }; + static struct sensor_device_attribute temp_crit_alarm[] = { SENSOR_ATTR(temp1_crit_alarm, S_IRUGO, show_temp_crit_alarm, NULL, 0), SENSOR_ATTR(temp2_crit_alarm, S_IRUGO, show_temp_crit_alarm, NULL, 1), @@ -963,27 +1079,29 @@ static struct sensor_device_attribute temp_fault[] = { SENSOR_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2), }; -#define TEMP_UNIT_ATTRS(X) \ - &temp_input[X].dev_attr.attr, \ - &temp_status[X].dev_attr.attr, \ - &temp_min[X].dev_attr.attr, \ - &temp_max[X].dev_attr.attr, \ - &temp_crit[X].dev_attr.attr, \ - &temp_min_alarm[X].dev_attr.attr, \ - &temp_max_alarm[X].dev_attr.attr, \ - &temp_crit_alarm[X].dev_attr.attr, \ - &temp_fault[X].dev_attr.attr - -static struct attribute * pc8736x_temp_attr_array[] = { +#define TEMP_UNIT_ATTRS(X) \ +{ &temp_input[X].dev_attr.attr, \ + &temp_status[X].dev_attr.attr, \ + &temp_min[X].dev_attr.attr, \ + &temp_max[X].dev_attr.attr, \ + &temp_crit[X].dev_attr.attr, \ + &temp_min_alarm[X].dev_attr.attr, \ + &temp_max_alarm[X].dev_attr.attr, \ + &temp_crit_alarm[X].dev_attr.attr, \ + &temp_fault[X].dev_attr.attr, \ + NULL \ +} + +static struct attribute *pc8736x_temp_attr[][10] = { TEMP_UNIT_ATTRS(0), TEMP_UNIT_ATTRS(1), - TEMP_UNIT_ATTRS(2), - /* include the few miscellaneous atts here */ - &dev_attr_alarms_temp.attr, - NULL + TEMP_UNIT_ATTRS(2) }; -static const struct attribute_group pc8736x_temp_group = { - .attrs = pc8736x_temp_attr_array, + +static const struct attribute_group pc8736x_temp_attr_group[] = { + { .attrs = pc8736x_temp_attr[0] }, + { .attrs = pc8736x_temp_attr[1] }, + { .attrs = pc8736x_temp_attr[2] } }; static ssize_t show_name(struct device *dev, @@ -992,13 +1110,15 @@ static ssize_t show_name(struct device *dev, struct pc87360_data *data = dev_get_drvdata(dev); return sprintf(buf, "%s\n", data->name); } + static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); /* * Device detection, registration and update */ -static int __init pc87360_find(int sioaddr, u8 *devid, unsigned short *addresses) +static int __init pc87360_find(int sioaddr, u8 *devid, + unsigned short *addresses) { u16 val; int i; @@ -1031,54 +1151,50 @@ static int __init pc87360_find(int sioaddr, u8 *devid, unsigned short *addresses val = superio_inb(sioaddr, ACT); if (!(val & 0x01)) { - printk(KERN_INFO "pc87360: Device 0x%02x not " - "activated\n", logdev[i]); + pr_info("Device 0x%02x not activated\n", logdev[i]); continue; } val = (superio_inb(sioaddr, BASE) << 8) | superio_inb(sioaddr, BASE + 1); if (!val) { - printk(KERN_INFO "pc87360: Base address not set for " - "device 0x%02x\n", logdev[i]); + pr_info("Base address not set for device 0x%02x\n", + logdev[i]); continue; } addresses[i] = val; - if (i==0) { /* Fans */ + if (i == 0) { /* Fans */ confreg[0] = superio_inb(sioaddr, 0xF0); confreg[1] = superio_inb(sioaddr, 0xF1); -#ifdef DEBUG - printk(KERN_DEBUG "pc87360: Fan 1: mon=%d " - "ctrl=%d inv=%d\n", (confreg[0]>>2)&1, - (confreg[0]>>3)&1, (confreg[0]>>4)&1); - printk(KERN_DEBUG "pc87360: Fan 2: mon=%d " - "ctrl=%d inv=%d\n", (confreg[0]>>5)&1, - (confreg[0]>>6)&1, (confreg[0]>>7)&1); - printk(KERN_DEBUG "pc87360: Fan 3: mon=%d " - "ctrl=%d inv=%d\n", confreg[1]&1, - (confreg[1]>>1)&1, (confreg[1]>>2)&1); -#endif - } else if (i==1) { /* Voltages */ + pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 1, + (confreg[0] >> 2) & 1, (confreg[0] >> 3) & 1, + (confreg[0] >> 4) & 1); + pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 2, + (confreg[0] >> 5) & 1, (confreg[0] >> 6) & 1, + (confreg[0] >> 7) & 1); + pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 3, + confreg[1] & 1, (confreg[1] >> 1) & 1, + (confreg[1] >> 2) & 1); + } else if (i == 1) { /* Voltages */ /* Are we using thermistors? */ if (*devid == 0xE9) { /* PC87366 */ - /* These registers are not logical-device - specific, just that we won't need them if - we don't use the VLM device */ + /* + * These registers are not logical-device + * specific, just that we won't need them if + * we don't use the VLM device + */ confreg[2] = superio_inb(sioaddr, 0x2B); confreg[3] = superio_inb(sioaddr, 0x25); if (confreg[2] & 0x40) { - printk(KERN_INFO "pc87360: Using " - "thermistors for temperature " - "monitoring\n"); + pr_info("Using thermistors for temperature monitoring\n"); } if (confreg[3] & 0xE0) { - printk(KERN_INFO "pc87360: VID " - "inputs routed (mode %u)\n", - confreg[3] >> 5); + pr_info("VID inputs routed (mode %u)\n", + confreg[3] >> 5); } } } @@ -1088,25 +1204,43 @@ static int __init pc87360_find(int sioaddr, u8 *devid, unsigned short *addresses return 0; } -static int __devinit pc87360_probe(struct platform_device *pdev) +static void pc87360_remove_files(struct device *dev) +{ + int i; + + device_remove_file(dev, &dev_attr_name); + device_remove_file(dev, &dev_attr_alarms_temp); + for (i = 0; i < ARRAY_SIZE(pc8736x_temp_attr_group); i++) + sysfs_remove_group(&dev->kobj, &pc8736x_temp_attr_group[i]); + for (i = 0; i < ARRAY_SIZE(pc8736x_fan_attr_group); i++) { + sysfs_remove_group(&pdev->dev.kobj, &pc8736x_fan_attr_group[i]); + device_remove_file(dev, &pwm[i].dev_attr); + } + sysfs_remove_group(&dev->kobj, &pc8736x_therm_group); + sysfs_remove_group(&dev->kobj, &pc8736x_vin_group); +} + +static int pc87360_probe(struct platform_device *pdev) { int i; struct pc87360_data *data; int err = 0; - const char *name = "pc87360"; + const char *name; int use_thermistors = 0; struct device *dev = &pdev->dev; - if (!(data = kzalloc(sizeof(struct pc87360_data), GFP_KERNEL))) + data = devm_kzalloc(dev, sizeof(struct pc87360_data), GFP_KERNEL); + if (!data) return -ENOMEM; - data->fannr = 2; - data->innr = 0; - data->tempnr = 0; - switch (devid) { + default: + name = "pc87360"; + data->fannr = 2; + break; case 0xe8: name = "pc87363"; + data->fannr = 2; break; case 0xe4: name = "pc87364"; @@ -1127,22 +1261,19 @@ static int __devinit pc87360_probe(struct platform_device *pdev) } data->name = name; - data->valid = 0; mutex_init(&data->lock); mutex_init(&data->update_lock); platform_set_drvdata(pdev, data); for (i = 0; i < LDNI_MAX; i++) { - if (((data->address[i] = extra_isa[i])) - && !request_region(extra_isa[i], PC87360_EXTENT, - pc87360_driver.driver.name)) { - dev_err(dev, "Region 0x%x-0x%x already " - "in use!\n", extra_isa[i], - extra_isa[i]+PC87360_EXTENT-1); - for (i--; i >= 0; i--) - release_region(extra_isa[i], PC87360_EXTENT); - err = -EBUSY; - goto ERROR1; + data->address[i] = extra_isa[i]; + if (data->address[i] + && !devm_request_region(dev, extra_isa[i], PC87360_EXTENT, + pc87360_driver.driver.name)) { + dev_err(dev, + "Region 0x%x-0x%x already in use!\n", + extra_isa[i], extra_isa[i]+PC87360_EXTENT-1); + return -EBUSY; } } @@ -1150,9 +1281,11 @@ static int __devinit pc87360_probe(struct platform_device *pdev) if (data->fannr) data->fan_conf = confreg[0] | (confreg[1] << 8); - /* Use the correct reference voltage - Unless both the VLM and the TMS logical devices agree to - use an external Vref, the internal one is used. */ + /* + * Use the correct reference voltage + * Unless both the VLM and the TMS logical devices agree to + * use an external Vref, the internal one is used. + */ if (data->innr) { i = pc87360_read_value(data, LD_IN, NO_BANK, PC87365_REG_IN_CONFIG); @@ -1185,113 +1318,76 @@ static int __devinit pc87360_probe(struct platform_device *pdev) /* Register all-or-nothing sysfs groups */ - if (data->innr && - (err = sysfs_create_group(&dev->kobj, - &pc8736x_vin_group))) - goto ERROR3; + if (data->innr) { + err = sysfs_create_group(&dev->kobj, &pc8736x_vin_group); + if (err) + goto error; + } - if (data->innr == 14 && - (err = sysfs_create_group(&dev->kobj, - &pc8736x_therm_group))) - goto ERROR3; + if (data->innr == 14) { + err = sysfs_create_group(&dev->kobj, &pc8736x_therm_group); + if (err) + goto error; + } /* create device attr-files for varying sysfs groups */ if (data->tempnr) { for (i = 0; i < data->tempnr; i++) { - if ((err = device_create_file(dev, - &temp_input[i].dev_attr)) - || (err = device_create_file(dev, - &temp_min[i].dev_attr)) - || (err = device_create_file(dev, - &temp_max[i].dev_attr)) - || (err = device_create_file(dev, - &temp_crit[i].dev_attr)) - || (err = device_create_file(dev, - &temp_status[i].dev_attr)) - || (err = device_create_file(dev, - &temp_min_alarm[i].dev_attr)) - || (err = device_create_file(dev, - &temp_max_alarm[i].dev_attr)) - || (err = device_create_file(dev, - &temp_crit_alarm[i].dev_attr)) - || (err = device_create_file(dev, - &temp_fault[i].dev_attr))) - goto ERROR3; + err = sysfs_create_group(&dev->kobj, + &pc8736x_temp_attr_group[i]); + if (err) + goto error; } - if ((err = device_create_file(dev, &dev_attr_alarms_temp))) - goto ERROR3; + err = device_create_file(dev, &dev_attr_alarms_temp); + if (err) + goto error; } for (i = 0; i < data->fannr; i++) { - if (FAN_CONFIG_MONITOR(data->fan_conf, i) - && ((err = device_create_file(dev, - &fan_input[i].dev_attr)) - || (err = device_create_file(dev, - &fan_min[i].dev_attr)) - || (err = device_create_file(dev, - &fan_div[i].dev_attr)) - || (err = device_create_file(dev, - &fan_status[i].dev_attr)))) - goto ERROR3; - - if (FAN_CONFIG_CONTROL(data->fan_conf, i) - && (err = device_create_file(dev, &pwm[i].dev_attr))) - goto ERROR3; + if (FAN_CONFIG_MONITOR(data->fan_conf, i)) { + err = sysfs_create_group(&dev->kobj, + &pc8736x_fan_attr_group[i]); + if (err) + goto error; + } + if (FAN_CONFIG_CONTROL(data->fan_conf, i)) { + err = device_create_file(dev, &pwm[i].dev_attr); + if (err) + goto error; + } } - if ((err = device_create_file(dev, &dev_attr_name))) - goto ERROR3; + err = device_create_file(dev, &dev_attr_name); + if (err) + goto error; data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); - goto ERROR3; + goto error; } return 0; -ERROR3: - device_remove_file(dev, &dev_attr_name); - /* can still remove groups whose members were added individually */ - sysfs_remove_group(&dev->kobj, &pc8736x_temp_group); - sysfs_remove_group(&dev->kobj, &pc8736x_fan_group); - sysfs_remove_group(&dev->kobj, &pc8736x_therm_group); - sysfs_remove_group(&dev->kobj, &pc8736x_vin_group); - for (i = 0; i < 3; i++) { - if (data->address[i]) { - release_region(data->address[i], PC87360_EXTENT); - } - } -ERROR1: - kfree(data); +error: + pc87360_remove_files(dev); return err; } -static int __devexit pc87360_remove(struct platform_device *pdev) +static int pc87360_remove(struct platform_device *pdev) { struct pc87360_data *data = platform_get_drvdata(pdev); - int i; hwmon_device_unregister(data->hwmon_dev); - - device_remove_file(&pdev->dev, &dev_attr_name); - sysfs_remove_group(&pdev->dev.kobj, &pc8736x_temp_group); - sysfs_remove_group(&pdev->dev.kobj, &pc8736x_fan_group); - sysfs_remove_group(&pdev->dev.kobj, &pc8736x_therm_group); - sysfs_remove_group(&pdev->dev.kobj, &pc8736x_vin_group); - - for (i = 0; i < 3; i++) { - if (data->address[i]) { - release_region(data->address[i], PC87360_EXTENT); - } - } - kfree(data); + pc87360_remove_files(&pdev->dev); return 0; } -/* ldi is the logical device index - bank is for voltages and temperatures only */ +/* + * ldi is the logical device index + * bank is for voltages and temperatures only + */ static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank, u8 reg) { @@ -1338,8 +1434,8 @@ static void pc87360_init_device(struct platform_device *pdev, if (init >= 2 && data->innr) { reg = pc87360_read_value(data, LD_IN, NO_BANK, PC87365_REG_IN_CONVRATE); - dev_info(&pdev->dev, "VLM conversion set to " - "1s period, 160us delay\n"); + dev_info(&pdev->dev, + "VLM conversion set to 1s period, 160us delay\n"); pc87360_write_value(data, LD_IN, NO_BANK, PC87365_REG_IN_CONVRATE, (reg & 0xC0) | 0x11); @@ -1353,8 +1449,8 @@ static void pc87360_init_device(struct platform_device *pdev, if (init >= init_in[i]) { /* Forcibly enable voltage channel */ if (!(reg & CHAN_ENA)) { - dev_dbg(&pdev->dev, "Forcibly " - "enabling in%d\n", i); + dev_dbg(&pdev->dev, "Forcibly enabling in%d\n", + i); pc87360_write_value(data, LD_IN, i, PC87365_REG_IN_STATUS, (reg & 0x68) | 0x87); @@ -1362,8 +1458,10 @@ static void pc87360_init_device(struct platform_device *pdev, } } - /* We can't blindly trust the Super-I/O space configuration bit, - most BIOS won't set it properly */ + /* + * We can't blindly trust the Super-I/O space configuration bit, + * most BIOS won't set it properly + */ dev_dbg(&pdev->dev, "bios thermistors:%d\n", use_thermistors); for (i = 11; i < data->innr; i++) { reg = pc87360_read_value(data, LD_IN, i, @@ -1378,12 +1476,12 @@ static void pc87360_init_device(struct platform_device *pdev, for (; i < data->tempnr; i++) { reg = pc87360_read_value(data, LD_TEMP, i, PC87365_REG_TEMP_STATUS); - dev_dbg(&pdev->dev, "bios temp%d_status:0x%02x\n", i+1, reg); + dev_dbg(&pdev->dev, "bios temp%d_status:0x%02x\n", i + 1, reg); if (init >= init_temp[i]) { /* Forcibly enable temperature channel */ if (!(reg & CHAN_ENA)) { - dev_dbg(&pdev->dev, "Forcibly " - "enabling temp%d\n", i+1); + dev_dbg(&pdev->dev, + "Forcibly enabling temp%d\n", i + 1); pc87360_write_value(data, LD_TEMP, i, PC87365_REG_TEMP_STATUS, 0xCF); @@ -1394,14 +1492,16 @@ static void pc87360_init_device(struct platform_device *pdev, if (use_thermistors) { for (i = 11; i < data->innr; i++) { if (init >= init_in[i]) { - /* The pin may already be used by thermal - diodes */ + /* + * The pin may already be used by thermal + * diodes + */ reg = pc87360_read_value(data, LD_TEMP, - (i-11)/2, PC87365_REG_TEMP_STATUS); + (i - 11) / 2, PC87365_REG_TEMP_STATUS); if (reg & CHAN_ENA) { - dev_dbg(&pdev->dev, "Skipping " - "temp%d, pin already in use " - "by temp%d\n", i-7, (i-11)/2); + dev_dbg(&pdev->dev, + "Skipping temp%d, pin already in use by temp%d\n", + i - 7, (i - 11) / 2); continue; } @@ -1409,8 +1509,9 @@ static void pc87360_init_device(struct platform_device *pdev, reg = pc87360_read_value(data, LD_IN, i, PC87365_REG_IN_STATUS); if (!(reg & CHAN_ENA)) { - dev_dbg(&pdev->dev, "Forcibly " - "enabling temp%d\n", i-7); + dev_dbg(&pdev->dev, + "Forcibly enabling temp%d\n", + i - 7); pc87360_write_value(data, LD_IN, i, PC87365_REG_TEMP_STATUS, (reg & 0x60) | 0x8F); @@ -1424,8 +1525,8 @@ static void pc87360_init_device(struct platform_device *pdev, PC87365_REG_IN_CONFIG); dev_dbg(&pdev->dev, "bios vin-cfg:0x%02x\n", reg); if (reg & CHAN_ENA) { - dev_dbg(&pdev->dev, "Forcibly " - "enabling monitoring (VLM)\n"); + dev_dbg(&pdev->dev, + "Forcibly enabling monitoring (VLM)\n"); pc87360_write_value(data, LD_IN, NO_BANK, PC87365_REG_IN_CONFIG, reg & 0xFE); @@ -1437,8 +1538,8 @@ static void pc87360_init_device(struct platform_device *pdev, PC87365_REG_TEMP_CONFIG); dev_dbg(&pdev->dev, "bios temp-cfg:0x%02x\n", reg); if (reg & CHAN_ENA) { - dev_dbg(&pdev->dev, "Forcibly enabling " - "monitoring (TMS)\n"); + dev_dbg(&pdev->dev, + "Forcibly enabling monitoring (TMS)\n"); pc87360_write_value(data, LD_TEMP, NO_BANK, PC87365_REG_TEMP_CONFIG, reg & 0xFE); @@ -1447,10 +1548,12 @@ static void pc87360_init_device(struct platform_device *pdev, if (init >= 2) { /* Chip config as documented by National Semi. */ pc87360_write_value(data, LD_TEMP, 0xF, 0xA, 0x08); - /* We voluntarily omit the bank here, in case the - sequence itself matters. It shouldn't be a problem, - since nobody else is supposed to access the - device at that point. */ + /* + * We voluntarily omit the bank here, in case the + * sequence itself matters. It shouldn't be a problem, + * since nobody else is supposed to access the + * device at that point. + */ pc87360_write_value(data, LD_TEMP, NO_BANK, 0xB, 0x04); pc87360_write_value(data, LD_TEMP, NO_BANK, 0xC, 0x35); pc87360_write_value(data, LD_TEMP, NO_BANK, 0xD, 0x05); @@ -1471,9 +1574,9 @@ static void pc87360_autodiv(struct device *dev, int nr) data->fan_status[nr] += 0x20; data->fan_min[nr] >>= 1; data->fan[nr] >>= 1; - dev_dbg(dev, "Increasing " - "clock divider to %d for fan %d\n", - FAN_DIV_FROM_REG(data->fan_status[nr]), nr+1); + dev_dbg(dev, + "Increasing clock divider to %d for fan %d\n", + FAN_DIV_FROM_REG(data->fan_status[nr]), nr + 1); } } else { /* Decrease clock divider if possible */ @@ -1483,10 +1586,10 @@ static void pc87360_autodiv(struct device *dev, int nr) data->fan_status[nr] -= 0x20; data->fan_min[nr] <<= 1; data->fan[nr] <<= 1; - dev_dbg(dev, "Decreasing " - "clock divider to %d for fan %d\n", + dev_dbg(dev, + "Decreasing clock divider to %d for fan %d\n", FAN_DIV_FROM_REG(data->fan_status[nr]), - nr+1); + nr + 1); } } @@ -1610,41 +1713,42 @@ static struct pc87360_data *pc87360_update_device(struct device *dev) static int __init pc87360_device_add(unsigned short address) { - struct resource res = { - .name = "pc87360", - .flags = IORESOURCE_IO, - }; - int err, i; + struct resource res[3]; + int err, i, res_count; pdev = platform_device_alloc("pc87360", address); if (!pdev) { err = -ENOMEM; - printk(KERN_ERR "pc87360: Device allocation failed\n"); + pr_err("Device allocation failed\n"); goto exit; } + memset(res, 0, 3 * sizeof(struct resource)); + res_count = 0; for (i = 0; i < 3; i++) { if (!extra_isa[i]) continue; - res.start = extra_isa[i]; - res.end = extra_isa[i] + PC87360_EXTENT - 1; + res[res_count].start = extra_isa[i]; + res[res_count].end = extra_isa[i] + PC87360_EXTENT - 1; + res[res_count].name = "pc87360", + res[res_count].flags = IORESOURCE_IO, - err = acpi_check_resource_conflict(&res); + err = acpi_check_resource_conflict(&res[res_count]); if (err) goto exit_device_put; - err = platform_device_add_resources(pdev, &res, 1); - if (err) { - printk(KERN_ERR "pc87360: Device resource[%d] " - "addition failed (%d)\n", i, err); - goto exit_device_put; - } + res_count++; + } + + err = platform_device_add_resources(pdev, res, res_count); + if (err) { + pr_err("Device resources addition failed (%d)\n", err); + goto exit_device_put; } err = platform_device_add(pdev); if (err) { - printk(KERN_ERR "pc87360: Device addition failed (%d)\n", - err); + pr_err("Device addition failed (%d)\n", err); goto exit_device_put; } @@ -1663,8 +1767,7 @@ static int __init pc87360_init(void) if (pc87360_find(0x2e, &devid, extra_isa) && pc87360_find(0x4e, &devid, extra_isa)) { - printk(KERN_WARNING "pc87360: PC8736x not detected, " - "module not inserted.\n"); + pr_warn("PC8736x not detected, module not inserted\n"); return -ENODEV; } @@ -1677,8 +1780,7 @@ static int __init pc87360_init(void) } if (address == 0x0000) { - printk(KERN_WARNING "pc87360: No active logical device, " - "module not inserted.\n"); + pr_warn("No active logical device, module not inserted\n"); return -ENODEV; } @@ -1706,7 +1808,7 @@ static void __exit pc87360_exit(void) } -MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); +MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); MODULE_DESCRIPTION("PC8736x hardware monitor"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pc87427.c b/drivers/hwmon/pc87427.c index 3170b26d244..9e4684e747e 100644 --- a/drivers/hwmon/pc87427.c +++ b/drivers/hwmon/pc87427.c @@ -1,7 +1,7 @@ /* * pc87427.c - hardware monitoring driver for the * National Semiconductor PC87427 Super-I/O chip - * Copyright (C) 2006 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2006, 2008, 2010 Jean Delvare <jdelvare@suse.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,12 +15,15 @@ * Supports the following chips: * * Chip #vin #fan #pwm #temp devid - * PC87427 - 8 - - 0xF2 + * PC87427 - 8 4 6 0xF2 * * This driver assumes that no more than one chip is present. - * Only fan inputs are supported so far, although the chip can do much more. + * Only fans are fully supported so far. Temperatures are in read-only + * mode, and voltages aren't supported at all. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> @@ -43,9 +46,11 @@ static struct platform_device *pdev; #define DRVNAME "pc87427" -/* The lock mutex protects both the I/O accesses (needed because the - device is using banked registers) and the register cache (needed to keep - the data in the registers and the cache in sync at any time). */ +/* + * The lock mutex protects both the I/O accesses (needed because the + * device is using banked registers) and the register cache (needed to keep + * the data in the registers and the cache in sync at any time). + */ struct pc87427_data { struct device *hwmon_dev; struct mutex lock; @@ -57,6 +62,25 @@ struct pc87427_data { u16 fan[8]; /* register values */ u16 fan_min[8]; /* register values */ u8 fan_status[8]; /* register values */ + + u8 pwm_enabled; /* bit vector */ + u8 pwm_auto_ok; /* bit vector */ + u8 pwm_enable[4]; /* register values */ + u8 pwm[4]; /* register values */ + + u8 temp_enabled; /* bit vector */ + s16 temp[6]; /* register values */ + s8 temp_min[6]; /* register values */ + s8 temp_max[6]; /* register values */ + s8 temp_crit[6]; /* register values */ + u8 temp_status[6]; /* register values */ + u8 temp_type[6]; /* register values */ +}; + +struct pc87427_sio_data { + unsigned short address[2]; + u8 has_fanin; + u8 has_fanout; }; /* @@ -65,6 +89,13 @@ struct pc87427_data { #define SIOREG_LDSEL 0x07 /* Logical device select */ #define SIOREG_DEVID 0x20 /* Device ID */ +#define SIOREG_CF2 0x22 /* Configuration 2 */ +#define SIOREG_CF3 0x23 /* Configuration 3 */ +#define SIOREG_CF4 0x24 /* Configuration 4 */ +#define SIOREG_CF5 0x25 /* Configuration 5 */ +#define SIOREG_CFB 0x2B /* Configuration B */ +#define SIOREG_CFC 0x2C /* Configuration C */ +#define SIOREG_CFD 0x2D /* Configuration D */ #define SIOREG_ACT 0x30 /* Device activation */ #define SIOREG_MAP 0x50 /* I/O or memory mapping */ #define SIOREG_IOBASE 0x60 /* I/O base address */ @@ -102,6 +133,8 @@ static inline void superio_exit(int sioaddr) #define BANK_FM(nr) (nr) #define BANK_FT(nr) (0x08 + (nr)) #define BANK_FC(nr) (0x10 + (nr) * 2) +#define BANK_TM(nr) (nr) +#define BANK_VM(nr) (0x08 + (nr)) /* * I/O access functions @@ -142,10 +175,12 @@ static inline void pc87427_write8_bank(struct pc87427_data *data, u8 ldi, #define FAN_STATUS_LOSPD (1 << 1) #define FAN_STATUS_MONEN (1 << 0) -/* Dedicated function to read all registers related to a given fan input. - This saves us quite a few locks and bank selections. - Must be called with data->lock held. - nr is from 0 to 7 */ +/* + * Dedicated function to read all registers related to a given fan input. + * This saves us quite a few locks and bank selections. + * Must be called with data->lock held. + * nr is from 0 to 7 + */ static void pc87427_readall_fan(struct pc87427_data *data, u8 nr) { int iobase = data->address[LD_FAN]; @@ -158,8 +193,10 @@ static void pc87427_readall_fan(struct pc87427_data *data, u8 nr) outb(data->fan_status[nr], iobase + PC87427_REG_FAN_STATUS); } -/* The 2 LSB of fan speed registers are used for something different. - The actual 2 LSB of the measurements are not available. */ +/* + * The 2 LSB of fan speed registers are used for something different. + * The actual 2 LSB of the measurements are not available. + */ static inline unsigned long fan_from_reg(u16 reg) { reg &= 0xfffc; @@ -179,6 +216,133 @@ static inline u16 fan_to_reg(unsigned long val) } /* + * PWM registers and conversions + */ + +#define PC87427_REG_PWM_ENABLE 0x10 +#define PC87427_REG_PWM_DUTY 0x12 + +#define PWM_ENABLE_MODE_MASK (7 << 4) +#define PWM_ENABLE_CTLEN (1 << 0) + +#define PWM_MODE_MANUAL (0 << 4) +#define PWM_MODE_AUTO (1 << 4) +#define PWM_MODE_OFF (2 << 4) +#define PWM_MODE_ON (7 << 4) + +/* + * Dedicated function to read all registers related to a given PWM output. + * This saves us quite a few locks and bank selections. + * Must be called with data->lock held. + * nr is from 0 to 3 + */ +static void pc87427_readall_pwm(struct pc87427_data *data, u8 nr) +{ + int iobase = data->address[LD_FAN]; + + outb(BANK_FC(nr), iobase + PC87427_REG_BANK); + data->pwm_enable[nr] = inb(iobase + PC87427_REG_PWM_ENABLE); + data->pwm[nr] = inb(iobase + PC87427_REG_PWM_DUTY); +} + +static inline int pwm_enable_from_reg(u8 reg) +{ + switch (reg & PWM_ENABLE_MODE_MASK) { + case PWM_MODE_ON: + return 0; + case PWM_MODE_MANUAL: + case PWM_MODE_OFF: + return 1; + case PWM_MODE_AUTO: + return 2; + default: + return -EPROTO; + } +} + +static inline u8 pwm_enable_to_reg(unsigned long val, u8 pwmval) +{ + switch (val) { + default: + return PWM_MODE_ON; + case 1: + return pwmval ? PWM_MODE_MANUAL : PWM_MODE_OFF; + case 2: + return PWM_MODE_AUTO; + } +} + +/* + * Temperature registers and conversions + */ + +#define PC87427_REG_TEMP_STATUS 0x10 +#define PC87427_REG_TEMP 0x14 +#define PC87427_REG_TEMP_MAX 0x18 +#define PC87427_REG_TEMP_MIN 0x19 +#define PC87427_REG_TEMP_CRIT 0x1a +#define PC87427_REG_TEMP_TYPE 0x1d + +#define TEMP_STATUS_CHANEN (1 << 0) +#define TEMP_STATUS_LOWFLG (1 << 1) +#define TEMP_STATUS_HIGHFLG (1 << 2) +#define TEMP_STATUS_CRITFLG (1 << 3) +#define TEMP_STATUS_SENSERR (1 << 5) +#define TEMP_TYPE_MASK (3 << 5) + +#define TEMP_TYPE_THERMISTOR (1 << 5) +#define TEMP_TYPE_REMOTE_DIODE (2 << 5) +#define TEMP_TYPE_LOCAL_DIODE (3 << 5) + +/* + * Dedicated function to read all registers related to a given temperature + * input. This saves us quite a few locks and bank selections. + * Must be called with data->lock held. + * nr is from 0 to 5 + */ +static void pc87427_readall_temp(struct pc87427_data *data, u8 nr) +{ + int iobase = data->address[LD_TEMP]; + + outb(BANK_TM(nr), iobase + PC87427_REG_BANK); + data->temp[nr] = le16_to_cpu(inw(iobase + PC87427_REG_TEMP)); + data->temp_max[nr] = inb(iobase + PC87427_REG_TEMP_MAX); + data->temp_min[nr] = inb(iobase + PC87427_REG_TEMP_MIN); + data->temp_crit[nr] = inb(iobase + PC87427_REG_TEMP_CRIT); + data->temp_type[nr] = inb(iobase + PC87427_REG_TEMP_TYPE); + data->temp_status[nr] = inb(iobase + PC87427_REG_TEMP_STATUS); + /* Clear fan alarm bits */ + outb(data->temp_status[nr], iobase + PC87427_REG_TEMP_STATUS); +} + +static inline unsigned int temp_type_from_reg(u8 reg) +{ + switch (reg & TEMP_TYPE_MASK) { + case TEMP_TYPE_THERMISTOR: + return 4; + case TEMP_TYPE_REMOTE_DIODE: + case TEMP_TYPE_LOCAL_DIODE: + return 3; + default: + return 0; + } +} + +/* + * We assume 8-bit thermal sensors; 9-bit thermal sensors are possible + * too, but I have no idea how to figure out when they are used. + */ +static inline long temp_from_reg(s16 reg) +{ + return reg * 1000 / 256; +} + +static inline long temp_from_reg8(s8 reg) +{ + return reg * 1000; +} + +/* * Data interface */ @@ -198,6 +362,21 @@ static struct pc87427_data *pc87427_update_device(struct device *dev) continue; pc87427_readall_fan(data, i); } + + /* PWM outputs */ + for (i = 0; i < 4; i++) { + if (!(data->pwm_enabled & (1 << i))) + continue; + pc87427_readall_pwm(data, i); + } + + /* Temperature channels */ + for (i = 0; i < 6; i++) { + if (!(data->temp_enabled & (1 << i))) + continue; + pc87427_readall_temp(data, i); + } + data->last_updated = jiffies; done: @@ -208,9 +387,8 @@ done: static ssize_t show_fan_input(struct device *dev, struct device_attribute *devattr, char *buf) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87427_data *data = pc87427_update_device(dev); - int nr = attr->index; + int nr = to_sensor_dev_attr(devattr)->index; return sprintf(buf, "%lu\n", fan_from_reg(data->fan[nr])); } @@ -218,9 +396,8 @@ static ssize_t show_fan_input(struct device *dev, struct device_attribute static ssize_t show_fan_min(struct device *dev, struct device_attribute *devattr, char *buf) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87427_data *data = pc87427_update_device(dev); - int nr = attr->index; + int nr = to_sensor_dev_attr(devattr)->index; return sprintf(buf, "%lu\n", fan_from_reg(data->fan_min[nr])); } @@ -228,9 +405,8 @@ static ssize_t show_fan_min(struct device *dev, struct device_attribute static ssize_t show_fan_alarm(struct device *dev, struct device_attribute *devattr, char *buf) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87427_data *data = pc87427_update_device(dev); - int nr = attr->index; + int nr = to_sensor_dev_attr(devattr)->index; return sprintf(buf, "%d\n", !!(data->fan_status[nr] & FAN_STATUS_LOSPD)); @@ -239,9 +415,8 @@ static ssize_t show_fan_alarm(struct device *dev, struct device_attribute static ssize_t show_fan_fault(struct device *dev, struct device_attribute *devattr, char *buf) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87427_data *data = pc87427_update_device(dev); - int nr = attr->index; + int nr = to_sensor_dev_attr(devattr)->index; return sprintf(buf, "%d\n", !!(data->fan_status[nr] & FAN_STATUS_STALL)); @@ -251,16 +426,20 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct pc87427_data *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - int nr = attr->index; - unsigned long val = simple_strtoul(buf, NULL, 10); + int nr = to_sensor_dev_attr(devattr)->index; + unsigned long val; int iobase = data->address[LD_FAN]; + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + mutex_lock(&data->lock); outb(BANK_FM(nr), iobase + PC87427_REG_BANK); - /* The low speed limit registers are read-only while monitoring - is enabled, so we have to disable monitoring, then change the - limit, and finally enable monitoring again. */ + /* + * The low speed limit registers are read-only while monitoring + * is enabled, so we have to disable monitoring, then change the + * limit, and finally enable monitoring again. + */ outb(0, iobase + PC87427_REG_FAN_STATUS); data->fan_min[nr] = fan_to_reg(val); outw(data->fan_min[nr], iobase + PC87427_REG_FAN_MIN); @@ -377,6 +556,393 @@ static const struct attribute_group pc87427_group_fan[8] = { { .attrs = pc87427_attributes_fan[7] }, }; +/* + * Must be called with data->lock held and pc87427_readall_pwm() freshly + * called + */ +static void update_pwm_enable(struct pc87427_data *data, int nr, u8 mode) +{ + int iobase = data->address[LD_FAN]; + data->pwm_enable[nr] &= ~PWM_ENABLE_MODE_MASK; + data->pwm_enable[nr] |= mode; + outb(data->pwm_enable[nr], iobase + PC87427_REG_PWM_ENABLE); +} + +static ssize_t show_pwm_enable(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct pc87427_data *data = pc87427_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + int pwm_enable; + + pwm_enable = pwm_enable_from_reg(data->pwm_enable[nr]); + if (pwm_enable < 0) + return pwm_enable; + return sprintf(buf, "%d\n", pwm_enable); +} + +static ssize_t set_pwm_enable(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct pc87427_data *data = dev_get_drvdata(dev); + int nr = to_sensor_dev_attr(devattr)->index; + unsigned long val; + + if (kstrtoul(buf, 10, &val) < 0 || val > 2) + return -EINVAL; + /* Can't go to automatic mode if it isn't configured */ + if (val == 2 && !(data->pwm_auto_ok & (1 << nr))) + return -EINVAL; + + mutex_lock(&data->lock); + pc87427_readall_pwm(data, nr); + update_pwm_enable(data, nr, pwm_enable_to_reg(val, data->pwm[nr])); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct pc87427_data *data = pc87427_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%d\n", (int)data->pwm[nr]); +} + +static ssize_t set_pwm(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct pc87427_data *data = dev_get_drvdata(dev); + int nr = to_sensor_dev_attr(devattr)->index; + unsigned long val; + int iobase = data->address[LD_FAN]; + u8 mode; + + if (kstrtoul(buf, 10, &val) < 0 || val > 0xff) + return -EINVAL; + + mutex_lock(&data->lock); + pc87427_readall_pwm(data, nr); + mode = data->pwm_enable[nr] & PWM_ENABLE_MODE_MASK; + if (mode != PWM_MODE_MANUAL && mode != PWM_MODE_OFF) { + dev_notice(dev, + "Can't set PWM%d duty cycle while not in manual mode\n", + nr + 1); + mutex_unlock(&data->lock); + return -EPERM; + } + + /* We may have to change the mode */ + if (mode == PWM_MODE_MANUAL && val == 0) { + /* Transition from Manual to Off */ + update_pwm_enable(data, nr, PWM_MODE_OFF); + mode = PWM_MODE_OFF; + dev_dbg(dev, "Switching PWM%d from %s to %s\n", nr + 1, + "manual", "off"); + } else if (mode == PWM_MODE_OFF && val != 0) { + /* Transition from Off to Manual */ + update_pwm_enable(data, nr, PWM_MODE_MANUAL); + mode = PWM_MODE_MANUAL; + dev_dbg(dev, "Switching PWM%d from %s to %s\n", nr + 1, + "off", "manual"); + } + + data->pwm[nr] = val; + if (mode == PWM_MODE_MANUAL) + outb(val, iobase + PC87427_REG_PWM_DUTY); + mutex_unlock(&data->lock); + + return count; +} + +static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + show_pwm_enable, set_pwm_enable, 0); +static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, + show_pwm_enable, set_pwm_enable, 1); +static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, + show_pwm_enable, set_pwm_enable, 2); +static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, + show_pwm_enable, set_pwm_enable, 3); + +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0); +static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1); +static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2); +static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3); + +static struct attribute *pc87427_attributes_pwm[4][3] = { + { + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_pwm2.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_pwm3_enable.dev_attr.attr, + &sensor_dev_attr_pwm3.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_pwm4_enable.dev_attr.attr, + &sensor_dev_attr_pwm4.dev_attr.attr, + NULL + } +}; + +static const struct attribute_group pc87427_group_pwm[4] = { + { .attrs = pc87427_attributes_pwm[0] }, + { .attrs = pc87427_attributes_pwm[1] }, + { .attrs = pc87427_attributes_pwm[2] }, + { .attrs = pc87427_attributes_pwm[3] }, +}; + +static ssize_t show_temp_input(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct pc87427_data *data = pc87427_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%ld\n", temp_from_reg(data->temp[nr])); +} + +static ssize_t show_temp_min(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct pc87427_data *data = pc87427_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%ld\n", temp_from_reg8(data->temp_min[nr])); +} + +static ssize_t show_temp_max(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct pc87427_data *data = pc87427_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%ld\n", temp_from_reg8(data->temp_max[nr])); +} + +static ssize_t show_temp_crit(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct pc87427_data *data = pc87427_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%ld\n", temp_from_reg8(data->temp_crit[nr])); +} + +static ssize_t show_temp_type(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct pc87427_data *data = pc87427_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", temp_type_from_reg(data->temp_type[nr])); +} + +static ssize_t show_temp_min_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct pc87427_data *data = pc87427_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%d\n", !!(data->temp_status[nr] + & TEMP_STATUS_LOWFLG)); +} + +static ssize_t show_temp_max_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct pc87427_data *data = pc87427_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%d\n", !!(data->temp_status[nr] + & TEMP_STATUS_HIGHFLG)); +} + +static ssize_t show_temp_crit_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct pc87427_data *data = pc87427_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%d\n", !!(data->temp_status[nr] + & TEMP_STATUS_CRITFLG)); +} + +static ssize_t show_temp_fault(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct pc87427_data *data = pc87427_update_device(dev); + int nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%d\n", !!(data->temp_status[nr] + & TEMP_STATUS_SENSERR)); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp_input, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp_input, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp_input, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_temp_input, NULL, 5); + +static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp_min, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO, show_temp_min, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO, show_temp_min, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO, show_temp_min, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_min, S_IRUGO, show_temp_min, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_min, S_IRUGO, show_temp_min, NULL, 5); + +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp_max, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp_max, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO, show_temp_max, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_max, S_IRUGO, show_temp_max, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_max, S_IRUGO, show_temp_max, NULL, 5); + +static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp_crit, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_crit, S_IRUGO, show_temp_crit, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_crit, S_IRUGO, show_temp_crit, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_crit, S_IRUGO, show_temp_crit, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_crit, S_IRUGO, show_temp_crit, NULL, 5); + +static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_type, S_IRUGO, show_temp_type, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_type, S_IRUGO, show_temp_type, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_type, S_IRUGO, show_temp_type, NULL, 5); + +static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, + show_temp_min_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, + show_temp_min_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, + show_temp_min_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, + show_temp_min_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_min_alarm, S_IRUGO, + show_temp_min_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_min_alarm, S_IRUGO, + show_temp_min_alarm, NULL, 5); + +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, + show_temp_max_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, + show_temp_max_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, + show_temp_max_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, + show_temp_max_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, + show_temp_max_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_max_alarm, S_IRUGO, + show_temp_max_alarm, NULL, 5); + +static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, + show_temp_crit_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, + show_temp_crit_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, + show_temp_crit_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_crit_alarm, S_IRUGO, + show_temp_crit_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_crit_alarm, S_IRUGO, + show_temp_crit_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_crit_alarm, S_IRUGO, + show_temp_crit_alarm, NULL, 5); + +static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_temp_fault, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_temp_fault, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_fault, S_IRUGO, show_temp_fault, NULL, 5); + +static struct attribute *pc87427_attributes_temp[6][10] = { + { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_type.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_fault.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp2_type.dev_attr.attr, + &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp3_type.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_fault.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp4_min.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp4_crit.dev_attr.attr, + &sensor_dev_attr_temp4_type.dev_attr.attr, + &sensor_dev_attr_temp4_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_fault.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp5_min.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + &sensor_dev_attr_temp5_crit.dev_attr.attr, + &sensor_dev_attr_temp5_type.dev_attr.attr, + &sensor_dev_attr_temp5_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_fault.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_temp6_input.dev_attr.attr, + &sensor_dev_attr_temp6_min.dev_attr.attr, + &sensor_dev_attr_temp6_max.dev_attr.attr, + &sensor_dev_attr_temp6_crit.dev_attr.attr, + &sensor_dev_attr_temp6_type.dev_attr.attr, + &sensor_dev_attr_temp6_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp6_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp6_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp6_fault.dev_attr.attr, + NULL + } +}; + +static const struct attribute_group pc87427_group_temp[6] = { + { .attrs = pc87427_attributes_temp[0] }, + { .attrs = pc87427_attributes_temp[1] }, + { .attrs = pc87427_attributes_temp[2] }, + { .attrs = pc87427_attributes_temp[3] }, + { .attrs = pc87427_attributes_temp[4] }, + { .attrs = pc87427_attributes_temp[5] }, +}; + static ssize_t show_name(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -391,8 +957,33 @@ static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); * Device detection, attach and detach */ -static void __devinit pc87427_init_device(struct device *dev) +static int pc87427_request_regions(struct platform_device *pdev, + int count) +{ + struct resource *res; + int i; + + for (i = 0; i < count; i++) { + res = platform_get_resource(pdev, IORESOURCE_IO, i); + if (!res) { + dev_err(&pdev->dev, "Missing resource #%d\n", i); + return -ENOENT; + } + if (!devm_request_region(&pdev->dev, res->start, + resource_size(res), DRVNAME)) { + dev_err(&pdev->dev, + "Failed to request region 0x%lx-0x%lx\n", + (unsigned long)res->start, + (unsigned long)res->end); + return -EBUSY; + } + } + return 0; +} + +static void pc87427_init_device(struct device *dev) { + struct pc87427_sio_data *sio_data = dev_get_platdata(dev); struct pc87427_data *data = dev_get_drvdata(dev); int i; u8 reg; @@ -400,10 +991,12 @@ static void __devinit pc87427_init_device(struct device *dev) /* The FMC module should be ready */ reg = pc87427_read8(data, LD_FAN, PC87427_REG_BANK); if (!(reg & 0x80)) - dev_warn(dev, "FMC module not ready!\n"); + dev_warn(dev, "%s module not ready!\n", "FMC"); /* Check which fans are enabled */ for (i = 0; i < 8; i++) { + if (!(sio_data->has_fanin & (1 << i))) /* Not wired */ + continue; reg = pc87427_read8_bank(data, LD_FAN, BANK_FM(i), PC87427_REG_FAN_STATUS); if (reg & FAN_STATUS_MONEN) @@ -411,37 +1004,93 @@ static void __devinit pc87427_init_device(struct device *dev) } if (!data->fan_enabled) { - dev_dbg(dev, "Enabling all fan inputs\n"); - for (i = 0; i < 8; i++) + dev_dbg(dev, "Enabling monitoring of all fans\n"); + for (i = 0; i < 8; i++) { + if (!(sio_data->has_fanin & (1 << i))) /* Not wired */ + continue; pc87427_write8_bank(data, LD_FAN, BANK_FM(i), PC87427_REG_FAN_STATUS, FAN_STATUS_MONEN); - data->fan_enabled = 0xff; + } + data->fan_enabled = sio_data->has_fanin; + } + + /* Check which PWM outputs are enabled */ + for (i = 0; i < 4; i++) { + if (!(sio_data->has_fanout & (1 << i))) /* Not wired */ + continue; + reg = pc87427_read8_bank(data, LD_FAN, BANK_FC(i), + PC87427_REG_PWM_ENABLE); + if (reg & PWM_ENABLE_CTLEN) + data->pwm_enabled |= (1 << i); + + /* + * We don't expose an interface to reconfigure the automatic + * fan control mode, so only allow to return to this mode if + * it was originally set. + */ + if ((reg & PWM_ENABLE_MODE_MASK) == PWM_MODE_AUTO) { + dev_dbg(dev, "PWM%d is in automatic control mode\n", + i + 1); + data->pwm_auto_ok |= (1 << i); + } + } + + /* The HMC module should be ready */ + reg = pc87427_read8(data, LD_TEMP, PC87427_REG_BANK); + if (!(reg & 0x80)) + dev_warn(dev, "%s module not ready!\n", "HMC"); + + /* Check which temperature channels are enabled */ + for (i = 0; i < 6; i++) { + reg = pc87427_read8_bank(data, LD_TEMP, BANK_TM(i), + PC87427_REG_TEMP_STATUS); + if (reg & TEMP_STATUS_CHANEN) + data->temp_enabled |= (1 << i); } } -static int __devinit pc87427_probe(struct platform_device *pdev) +static void pc87427_remove_files(struct device *dev) { - struct pc87427_data *data; - struct resource *res; - int i, err; + struct pc87427_data *data = dev_get_drvdata(dev); + int i; - if (!(data = kzalloc(sizeof(struct pc87427_data), GFP_KERNEL))) { - err = -ENOMEM; - printk(KERN_ERR DRVNAME ": Out of memory\n"); - goto exit; + device_remove_file(dev, &dev_attr_name); + for (i = 0; i < 8; i++) { + if (!(data->fan_enabled & (1 << i))) + continue; + sysfs_remove_group(&dev->kobj, &pc87427_group_fan[i]); } - - /* This will need to be revisited when we add support for - temperature and voltage monitoring. */ - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start, resource_size(res), DRVNAME)) { - err = -EBUSY; - dev_err(&pdev->dev, "Failed to request region 0x%lx-0x%lx\n", - (unsigned long)res->start, (unsigned long)res->end); - goto exit_kfree; + for (i = 0; i < 4; i++) { + if (!(data->pwm_enabled & (1 << i))) + continue; + sysfs_remove_group(&dev->kobj, &pc87427_group_pwm[i]); + } + for (i = 0; i < 6; i++) { + if (!(data->temp_enabled & (1 << i))) + continue; + sysfs_remove_group(&dev->kobj, &pc87427_group_temp[i]); } - data->address[0] = res->start; +} + +static int pc87427_probe(struct platform_device *pdev) +{ + struct pc87427_sio_data *sio_data = dev_get_platdata(&pdev->dev); + struct pc87427_data *data; + int i, err, res_count; + + data = devm_kzalloc(&pdev->dev, sizeof(struct pc87427_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->address[0] = sio_data->address[0]; + data->address[1] = sio_data->address[1]; + res_count = (data->address[0] != 0) + (data->address[1] != 0); + + err = pc87427_request_regions(pdev, res_count); + if (err) + return err; mutex_init(&data->lock); data->name = "pc87427"; @@ -449,13 +1098,31 @@ static int __devinit pc87427_probe(struct platform_device *pdev) pc87427_init_device(&pdev->dev); /* Register sysfs hooks */ - if ((err = device_create_file(&pdev->dev, &dev_attr_name))) - goto exit_release_region; + err = device_create_file(&pdev->dev, &dev_attr_name); + if (err) + return err; for (i = 0; i < 8; i++) { if (!(data->fan_enabled & (1 << i))) continue; - if ((err = sysfs_create_group(&pdev->dev.kobj, - &pc87427_group_fan[i]))) + err = sysfs_create_group(&pdev->dev.kobj, + &pc87427_group_fan[i]); + if (err) + goto exit_remove_files; + } + for (i = 0; i < 4; i++) { + if (!(data->pwm_enabled & (1 << i))) + continue; + err = sysfs_create_group(&pdev->dev.kobj, + &pc87427_group_pwm[i]); + if (err) + goto exit_remove_files; + } + for (i = 0; i < 6; i++) { + if (!(data->temp_enabled & (1 << i))) + continue; + err = sysfs_create_group(&pdev->dev.kobj, + &pc87427_group_temp[i]); + if (err) goto exit_remove_files; } @@ -469,38 +1136,16 @@ static int __devinit pc87427_probe(struct platform_device *pdev) return 0; exit_remove_files: - for (i = 0; i < 8; i++) { - if (!(data->fan_enabled & (1 << i))) - continue; - sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]); - } -exit_release_region: - release_region(res->start, resource_size(res)); -exit_kfree: - platform_set_drvdata(pdev, NULL); - kfree(data); -exit: + pc87427_remove_files(&pdev->dev); return err; } -static int __devexit pc87427_remove(struct platform_device *pdev) +static int pc87427_remove(struct platform_device *pdev) { struct pc87427_data *data = platform_get_drvdata(pdev); - struct resource *res; - int i; hwmon_device_unregister(data->hwmon_dev); - device_remove_file(&pdev->dev, &dev_attr_name); - for (i = 0; i < 8; i++) { - if (!(data->fan_enabled & (1 << i))) - continue; - sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]); - } - platform_set_drvdata(pdev, NULL); - kfree(data); - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - release_region(res->start, resource_size(res)); + pc87427_remove_files(&pdev->dev); return 0; } @@ -512,41 +1157,55 @@ static struct platform_driver pc87427_driver = { .name = DRVNAME, }, .probe = pc87427_probe, - .remove = __devexit_p(pc87427_remove), + .remove = pc87427_remove, }; -static int __init pc87427_device_add(unsigned short address) +static int __init pc87427_device_add(const struct pc87427_sio_data *sio_data) { - struct resource res = { - .start = address, - .end = address + REGION_LENGTH - 1, - .name = logdev_str[0], - .flags = IORESOURCE_IO, + struct resource res[2] = { + { .flags = IORESOURCE_IO }, + { .flags = IORESOURCE_IO }, }; - int err; + int err, i, res_count; - err = acpi_check_resource_conflict(&res); - if (err) - goto exit; + res_count = 0; + for (i = 0; i < 2; i++) { + if (!sio_data->address[i]) + continue; + res[res_count].start = sio_data->address[i]; + res[res_count].end = sio_data->address[i] + REGION_LENGTH - 1; + res[res_count].name = logdev_str[i]; + + err = acpi_check_resource_conflict(&res[res_count]); + if (err) + goto exit; - pdev = platform_device_alloc(DRVNAME, address); + res_count++; + } + + pdev = platform_device_alloc(DRVNAME, res[0].start); if (!pdev) { err = -ENOMEM; - printk(KERN_ERR DRVNAME ": Device allocation failed\n"); + pr_err("Device allocation failed\n"); goto exit; } - err = platform_device_add_resources(pdev, &res, 1); + err = platform_device_add_resources(pdev, res, res_count); + if (err) { + pr_err("Device resource addition failed (%d)\n", err); + goto exit_device_put; + } + + err = platform_device_add_data(pdev, sio_data, + sizeof(struct pc87427_sio_data)); if (err) { - printk(KERN_ERR DRVNAME ": Device resource addition failed " - "(%d)\n", err); + pr_err("Platform data allocation failed\n"); goto exit_device_put; } err = platform_device_add(pdev); if (err) { - printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", - err); + pr_err("Device addition failed (%d)\n", err); goto exit_device_put; } @@ -558,9 +1217,10 @@ exit: return err; } -static int __init pc87427_find(int sioaddr, unsigned short *address) +static int __init pc87427_find(int sioaddr, struct pc87427_sio_data *sio_data) { u16 val; + u8 cfg, cfg_b; int i, err = 0; /* Identify device */ @@ -571,34 +1231,83 @@ static int __init pc87427_find(int sioaddr, unsigned short *address) } for (i = 0; i < 2; i++) { - address[i] = 0; + sio_data->address[i] = 0; /* Select logical device */ superio_outb(sioaddr, SIOREG_LDSEL, logdev[i]); val = superio_inb(sioaddr, SIOREG_ACT); if (!(val & 0x01)) { - printk(KERN_INFO DRVNAME ": Logical device 0x%02x " - "not activated\n", logdev[i]); + pr_info("Logical device 0x%02x not activated\n", + logdev[i]); continue; } val = superio_inb(sioaddr, SIOREG_MAP); if (val & 0x01) { - printk(KERN_WARNING DRVNAME ": Logical device 0x%02x " - "is memory-mapped, can't use\n", logdev[i]); + pr_warn("Logical device 0x%02x is memory-mapped, can't use\n", + logdev[i]); continue; } val = (superio_inb(sioaddr, SIOREG_IOBASE) << 8) | superio_inb(sioaddr, SIOREG_IOBASE + 1); if (!val) { - printk(KERN_INFO DRVNAME ": I/O base address not set " - "for logical device 0x%02x\n", logdev[i]); + pr_info("I/O base address not set for logical device 0x%02x\n", + logdev[i]); continue; } - address[i] = val; + sio_data->address[i] = val; + } + + /* No point in loading the driver if everything is disabled */ + if (!sio_data->address[0] && !sio_data->address[1]) { + err = -ENODEV; + goto exit; + } + + /* Check which fan inputs are wired */ + sio_data->has_fanin = (1 << 2) | (1 << 3); /* FANIN2, FANIN3 */ + + cfg = superio_inb(sioaddr, SIOREG_CF2); + if (!(cfg & (1 << 3))) + sio_data->has_fanin |= (1 << 0); /* FANIN0 */ + if (!(cfg & (1 << 2))) + sio_data->has_fanin |= (1 << 4); /* FANIN4 */ + + cfg = superio_inb(sioaddr, SIOREG_CFD); + if (!(cfg & (1 << 0))) + sio_data->has_fanin |= (1 << 1); /* FANIN1 */ + + cfg = superio_inb(sioaddr, SIOREG_CF4); + if (!(cfg & (1 << 0))) + sio_data->has_fanin |= (1 << 7); /* FANIN7 */ + cfg_b = superio_inb(sioaddr, SIOREG_CFB); + if (!(cfg & (1 << 1)) && (cfg_b & (1 << 3))) + sio_data->has_fanin |= (1 << 5); /* FANIN5 */ + cfg = superio_inb(sioaddr, SIOREG_CF3); + if ((cfg & (1 << 3)) && !(cfg_b & (1 << 5))) + sio_data->has_fanin |= (1 << 6); /* FANIN6 */ + + /* Check which fan outputs are wired */ + sio_data->has_fanout = (1 << 0); /* FANOUT0 */ + if (cfg_b & (1 << 0)) + sio_data->has_fanout |= (1 << 3); /* FANOUT3 */ + + cfg = superio_inb(sioaddr, SIOREG_CFC); + if (!(cfg & (1 << 4))) { + if (cfg_b & (1 << 1)) + sio_data->has_fanout |= (1 << 1); /* FANOUT1 */ + if (cfg_b & (1 << 2)) + sio_data->has_fanout |= (1 << 2); /* FANOUT2 */ } + /* FANOUT1 and FANOUT2 can each be routed to 2 different pins */ + cfg = superio_inb(sioaddr, SIOREG_CF5); + if (cfg & (1 << 6)) + sio_data->has_fanout |= (1 << 1); /* FANOUT1 */ + if (cfg & (1 << 5)) + sio_data->has_fanout |= (1 << 2); /* FANOUT2 */ + exit: superio_exit(sioaddr); return err; @@ -607,15 +1316,10 @@ exit: static int __init pc87427_init(void) { int err; - unsigned short address[2]; - - if (pc87427_find(0x2e, address) - && pc87427_find(0x4e, address)) - return -ENODEV; + struct pc87427_sio_data sio_data; - /* For now the driver only handles fans so we only care about the - first address. */ - if (!address[0]) + if (pc87427_find(0x2e, &sio_data) + && pc87427_find(0x4e, &sio_data)) return -ENODEV; err = platform_driver_register(&pc87427_driver); @@ -623,7 +1327,7 @@ static int __init pc87427_init(void) goto exit; /* Sets global pdev as a side effect */ - err = pc87427_device_add(address[0]); + err = pc87427_device_add(&sio_data); if (err) goto exit_driver; @@ -641,7 +1345,7 @@ static void __exit pc87427_exit(void) platform_driver_unregister(&pc87427_driver); } -MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); +MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); MODULE_DESCRIPTION("PC87427 hardware monitoring driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pcf8591.c b/drivers/hwmon/pcf8591.c index d4478794985..5740888c624 100644 --- a/drivers/hwmon/pcf8591.c +++ b/drivers/hwmon/pcf8591.c @@ -1,32 +1,32 @@ /* - Copyright (C) 2001-2004 Aurelien Jarno <aurelien@aurel32.net> - Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with - the help of 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; 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. + * Copyright (C) 2001-2004 Aurelien Jarno <aurelien@aurel32.net> + * Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with + * the help of Jean Delvare <jdelvare@suse.de> + * + * 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. + */ - 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. -*/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/init.h> #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 */ @@ -39,28 +39,34 @@ MODULE_PARM_DESC(input_mode, " 2 = single ended and differential mixed\n" " 3 = two differential inputs\n"); -/* The PCF8591 control byte - 7 6 5 4 3 2 1 0 - | 0 |AOEF| AIP | 0 |AINC| AICH | */ +/* + * The PCF8591 control byte + * 7 6 5 4 3 2 1 0 + * | 0 |AOEF| AIP | 0 |AINC| AICH | + */ /* Analog Output Enable Flag (analog output active if 1) */ #define PCF8591_CONTROL_AOEF 0x40 -/* Analog Input Programming - 0x00 = four single ended inputs - 0x10 = three differential inputs - 0x20 = single ended and differential mixed - 0x30 = two differential inputs */ +/* + * Analog Input Programming + * 0x00 = four single ended inputs + * 0x10 = three differential inputs + * 0x20 = single ended and differential mixed + * 0x30 = two differential inputs + */ #define PCF8591_CONTROL_AIP_MASK 0x30 /* Autoincrement Flag (switch on if 1) */ #define PCF8591_CONTROL_AINC 0x04 -/* Channel selection - 0x00 = channel 0 - 0x01 = channel 1 - 0x02 = channel 2 - 0x03 = channel 3 */ +/* + * Channel selection + * 0x00 = channel 0 + * 0x01 = channel 1 + * 0x02 = channel 2 + * 0x03 = channel 3 + */ #define PCF8591_CONTROL_AICH_MASK 0x03 /* Initial values */ @@ -68,9 +74,10 @@ MODULE_PARM_DESC(input_mode, #define PCF8591_INIT_AOUT 0 /* DAC out = 0 */ /* Conversions */ -#define REG_TO_SIGNED(reg) (((reg) & 0x80)?((reg) - 256):(reg)) +#define REG_TO_SIGNED(reg) (((reg) & 0x80) ? ((reg) - 256) : (reg)) struct pcf8591_data { + struct device *hwmon_dev; struct mutex update_lock; u8 control; @@ -82,7 +89,9 @@ static int pcf8591_read_channel(struct device *dev, int channel); /* following are the sysfs callback functions */ #define show_in_channel(channel) \ -static ssize_t show_in##channel##_input(struct device *dev, struct device_attribute *attr, char *buf) \ +static ssize_t show_in##channel##_input(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ { \ return sprintf(buf, "%d\n", pcf8591_read_channel(dev, channel));\ } \ @@ -94,39 +103,57 @@ show_in_channel(1); show_in_channel(2); show_in_channel(3); -static ssize_t show_out0_ouput(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_out0_ouput(struct device *dev, + struct device_attribute *attr, char *buf) { struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev)); return sprintf(buf, "%d\n", data->aout * 10); } -static ssize_t set_out0_output(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t set_out0_output(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - unsigned int value; + unsigned long val; struct i2c_client *client = to_i2c_client(dev); struct pcf8591_data *data = i2c_get_clientdata(client); - if ((value = (simple_strtoul(buf, NULL, 10) + 5) / 10) <= 255) { - data->aout = value; - i2c_smbus_write_byte_data(client, data->control, data->aout); - return count; - } - return -EINVAL; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + val /= 10; + if (val > 255) + return -EINVAL; + + data->aout = val; + i2c_smbus_write_byte_data(client, data->control, data->aout); + return count; } static DEVICE_ATTR(out0_output, S_IWUSR | S_IRUGO, show_out0_ouput, set_out0_output); -static ssize_t show_out0_enable(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_out0_enable(struct device *dev, + struct device_attribute *attr, char *buf) { struct pcf8591_data *data = i2c_get_clientdata(to_i2c_client(dev)); return sprintf(buf, "%u\n", !(!(data->control & PCF8591_CONTROL_AOEF))); } -static ssize_t set_out0_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t set_out0_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); struct pcf8591_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); if (val) @@ -167,34 +194,16 @@ 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) { struct pcf8591_data *data; int err; - if (!(data = kzalloc(sizeof(struct pcf8591_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&client->dev, sizeof(struct pcf8591_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); mutex_init(&data->update_lock); @@ -205,38 +214,43 @@ static int pcf8591_probe(struct i2c_client *client, /* Register sysfs hooks */ err = sysfs_create_group(&client->dev.kobj, &pcf8591_attr_group); if (err) - goto exit_kfree; + return err; /* Register input2 if not in "two differential inputs" mode */ if (input_mode != 3) { - if ((err = device_create_file(&client->dev, - &dev_attr_in2_input))) + err = device_create_file(&client->dev, &dev_attr_in2_input); + if (err) goto exit_sysfs_remove; } /* Register input3 only in "four single ended inputs" mode */ if (input_mode == 0) { - if ((err = device_create_file(&client->dev, - &dev_attr_in3_input))) + err = device_create_file(&client->dev, &dev_attr_in3_input); + if (err) 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: sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt); sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group); -exit_kfree: - kfree(data); -exit: return err; } 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)); return 0; } @@ -249,8 +263,10 @@ static void pcf8591_init_client(struct i2c_client *client) i2c_smbus_write_byte_data(client, data->control, data->aout); - /* The first byte transmitted contains the conversion code of the - previous read cycle. FLUSH IT! */ + /* + * The first byte transmitted contains the conversion code of the + * previous read cycle. FLUSH IT! + */ i2c_smbus_read_byte(client); } @@ -267,8 +283,10 @@ static int pcf8591_read_channel(struct device *dev, int channel) | channel; i2c_smbus_write_byte(client, data->control); - /* The first byte transmitted contains the conversion code of - the previous read cycle. FLUSH IT! */ + /* + * The first byte transmitted contains the conversion code of + * the previous read cycle. FLUSH IT! + */ i2c_smbus_read_byte(client); } value = i2c_smbus_read_byte(client); @@ -277,9 +295,9 @@ static int pcf8591_read_channel(struct device *dev, int channel) if ((channel == 2 && input_mode == 2) || (channel != 3 && (input_mode == 1 || input_mode == 3))) - return (10 * REG_TO_SIGNED(value)); + return 10 * REG_TO_SIGNED(value); else - return (10 * value); + return 10 * value; } static const struct i2c_device_id pcf8591_id[] = { @@ -295,17 +313,12 @@ 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) { if (input_mode < 0 || input_mode > 3) { - printk(KERN_WARNING "pcf8591: invalid input_mode (%d)\n", - input_mode); + pr_warn("invalid input_mode (%d)\n", input_mode); input_mode = 0; } return i2c_add_driver(&pcf8591_driver); diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig new file mode 100644 index 00000000000..39cc63edfbb --- /dev/null +++ b/drivers/hwmon/pmbus/Kconfig @@ -0,0 +1,124 @@ +# +# PMBus chip drivers configuration +# + +menuconfig PMBUS + tristate "PMBus support" + depends on I2C + default n + help + Say yes here if you want to enable PMBus support. + + This driver can also be built as a module. If so, the module will + be called pmbus_core. + +if PMBUS + +config SENSORS_PMBUS + tristate "Generic PMBus devices" + default y + help + If you say yes here you get hardware monitoring support for generic + PMBus devices, including but not limited to ADP4000, BMR453, BMR454, + MDT040, NCP4200, NCP4208, PDT003, PDT006, PDT012, UDT020, TPS40400, + and TPS40422. + + This driver can also be built as a module. If so, the module will + be called pmbus. + +config SENSORS_ADM1275 + tristate "Analog Devices ADM1275 and compatibles" + default n + help + If you say yes here you get hardware monitoring support for Analog + Devices ADM1075, ADM1275, and ADM1276 Hot-Swap Controller and Digital + Power Monitors. + + This driver can also be built as a module. If so, the module will + be called adm1275. + +config SENSORS_LM25066 + tristate "National Semiconductor LM25066 and compatibles" + default n + help + If you say yes here you get hardware monitoring support for National + Semiconductor LM25056, LM25066, LM5064, and LM5066. + + This driver can also be built as a module. If so, the module will + be called lm25066. + +config SENSORS_LTC2978 + tristate "Linear Technologies LTC2974, LTC2978, LTC3880, and LTC3883" + default n + help + If you say yes here you get hardware monitoring support for Linear + Technology LTC2974, LTC2978, LTC3880, and LTC3883. + + This driver can also be built as a module. If so, the module will + be called ltc2978. + +config SENSORS_MAX16064 + tristate "Maxim MAX16064" + default n + help + If you say yes here you get hardware monitoring support for Maxim + MAX16064. + + This driver can also be built as a module. If so, the module will + be called max16064. + +config SENSORS_MAX34440 + tristate "Maxim MAX34440 and compatibles" + default n + help + If you say yes here you get hardware monitoring support for Maxim + MAX34440, MAX34441, MAX34446, MAX34460, and MAX34461. + + This driver can also be built as a module. If so, the module will + be called max34440. + +config SENSORS_MAX8688 + tristate "Maxim MAX8688" + default n + help + If you say yes here you get hardware monitoring support for Maxim + MAX8688. + + This driver can also be built as a module. If so, the module will + be called max8688. + +config SENSORS_UCD9000 + tristate "TI UCD90120, UCD90124, UCD9090, UCD90910" + default n + help + If you say yes here you get hardware monitoring support for TI + UCD90120, UCD90124, UCD9090, UCD90910 Sequencer and System Health + Controllers. + + This driver can also be built as a module. If so, the module will + be called ucd9000. + +config SENSORS_UCD9200 + tristate "TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, UCD9248" + default n + help + If you say yes here you get hardware monitoring support for TI + UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and UCD9248 + Digital PWM System Controllers. + + This driver can also be built as a module. If so, the module will + be called ucd9200. + +config SENSORS_ZL6100 + tristate "Intersil ZL6100 and compatibles" + default n + help + If you say yes here you get hardware monitoring support for Intersil + ZL2004, ZL2005, ZL2006, ZL2008, ZL2105, ZL2106, ZL6100, ZL6105, + ZL9101M, and ZL9117M Digital DC/DC Controllers, as well as for + Ericsson BMR450, BMR451, BMR462, BMR463, and BMR464. + + This driver can also be built as a module. If so, the module will + be called zl6100. + +endif # PMBUS diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile new file mode 100644 index 00000000000..789376c85db --- /dev/null +++ b/drivers/hwmon/pmbus/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for PMBus chip drivers. +# + +obj-$(CONFIG_PMBUS) += pmbus_core.o +obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o +obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o +obj-$(CONFIG_SENSORS_LM25066) += lm25066.o +obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o +obj-$(CONFIG_SENSORS_MAX16064) += max16064.o +obj-$(CONFIG_SENSORS_MAX34440) += max34440.o +obj-$(CONFIG_SENSORS_MAX8688) += max8688.o +obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o +obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o +obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c new file mode 100644 index 00000000000..60aad9570f0 --- /dev/null +++ b/drivers/hwmon/pmbus/adm1275.c @@ -0,0 +1,397 @@ +/* + * Hardware monitoring driver for Analog Devices ADM1275 Hot-Swap Controller + * and Digital Power Monitor + * + * Copyright (c) 2011 Ericsson AB. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include "pmbus.h" + +enum chips { adm1075, adm1275, adm1276 }; + +#define ADM1275_PEAK_IOUT 0xd0 +#define ADM1275_PEAK_VIN 0xd1 +#define ADM1275_PEAK_VOUT 0xd2 +#define ADM1275_PMON_CONFIG 0xd4 + +#define ADM1275_VIN_VOUT_SELECT (1 << 6) +#define ADM1275_VRANGE (1 << 5) +#define ADM1075_IRANGE_50 (1 << 4) +#define ADM1075_IRANGE_25 (1 << 3) +#define ADM1075_IRANGE_MASK ((1 << 3) | (1 << 4)) + +#define ADM1275_IOUT_WARN2_LIMIT 0xd7 +#define ADM1275_DEVICE_CONFIG 0xd8 + +#define ADM1275_IOUT_WARN2_SELECT (1 << 4) + +#define ADM1276_PEAK_PIN 0xda + +#define ADM1275_MFR_STATUS_IOUT_WARN2 (1 << 0) + +#define ADM1075_READ_VAUX 0xdd +#define ADM1075_VAUX_OV_WARN_LIMIT 0xde +#define ADM1075_VAUX_UV_WARN_LIMIT 0xdf +#define ADM1075_VAUX_STATUS 0xf6 + +#define ADM1075_VAUX_OV_WARN (1<<7) +#define ADM1075_VAUX_UV_WARN (1<<6) + +struct adm1275_data { + int id; + bool have_oc_fault; + struct pmbus_driver_info info; +}; + +#define to_adm1275_data(x) container_of(x, struct adm1275_data, info) + +static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct adm1275_data *data = to_adm1275_data(info); + int ret = 0; + + if (page) + return -ENXIO; + + switch (reg) { + case PMBUS_IOUT_UC_FAULT_LIMIT: + if (data->have_oc_fault) { + ret = -ENXIO; + break; + } + ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT); + break; + case PMBUS_IOUT_OC_FAULT_LIMIT: + if (!data->have_oc_fault) { + ret = -ENXIO; + break; + } + ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT); + break; + case PMBUS_VOUT_OV_WARN_LIMIT: + if (data->id != adm1075) { + ret = -ENODATA; + break; + } + ret = pmbus_read_word_data(client, 0, + ADM1075_VAUX_OV_WARN_LIMIT); + break; + case PMBUS_VOUT_UV_WARN_LIMIT: + if (data->id != adm1075) { + ret = -ENODATA; + break; + } + ret = pmbus_read_word_data(client, 0, + ADM1075_VAUX_UV_WARN_LIMIT); + break; + case PMBUS_READ_VOUT: + if (data->id != adm1075) { + ret = -ENODATA; + break; + } + ret = pmbus_read_word_data(client, 0, ADM1075_READ_VAUX); + break; + case PMBUS_VIRT_READ_IOUT_MAX: + ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_IOUT); + break; + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VOUT); + break; + case PMBUS_VIRT_READ_VIN_MAX: + ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VIN); + break; + case PMBUS_VIRT_READ_PIN_MAX: + if (data->id == adm1275) { + ret = -ENXIO; + break; + } + ret = pmbus_read_word_data(client, 0, ADM1276_PEAK_PIN); + break; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + case PMBUS_VIRT_RESET_VOUT_HISTORY: + case PMBUS_VIRT_RESET_VIN_HISTORY: + break; + case PMBUS_VIRT_RESET_PIN_HISTORY: + if (data->id == adm1275) + ret = -ENXIO; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int adm1275_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + int ret; + + if (page) + return -ENXIO; + + switch (reg) { + case PMBUS_IOUT_UC_FAULT_LIMIT: + case PMBUS_IOUT_OC_FAULT_LIMIT: + ret = pmbus_write_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT, + word); + break; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_IOUT, 0); + break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VOUT, 0); + break; + case PMBUS_VIRT_RESET_VIN_HISTORY: + ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VIN, 0); + break; + case PMBUS_VIRT_RESET_PIN_HISTORY: + ret = pmbus_write_word_data(client, 0, ADM1276_PEAK_PIN, 0); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct adm1275_data *data = to_adm1275_data(info); + int mfr_status, ret; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_STATUS_IOUT: + ret = pmbus_read_byte_data(client, page, PMBUS_STATUS_IOUT); + if (ret < 0) + break; + mfr_status = pmbus_read_byte_data(client, page, + PMBUS_STATUS_MFR_SPECIFIC); + if (mfr_status < 0) { + ret = mfr_status; + break; + } + if (mfr_status & ADM1275_MFR_STATUS_IOUT_WARN2) { + ret |= data->have_oc_fault ? + PB_IOUT_OC_FAULT : PB_IOUT_UC_FAULT; + } + break; + case PMBUS_STATUS_VOUT: + if (data->id != adm1075) { + ret = -ENODATA; + break; + } + ret = 0; + mfr_status = pmbus_read_byte_data(client, 0, + ADM1075_VAUX_STATUS); + if (mfr_status & ADM1075_VAUX_OV_WARN) + ret |= PB_VOLTAGE_OV_WARNING; + if (mfr_status & ADM1075_VAUX_UV_WARN) + ret |= PB_VOLTAGE_UV_WARNING; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static const struct i2c_device_id adm1275_id[] = { + { "adm1075", adm1075 }, + { "adm1275", adm1275 }, + { "adm1276", adm1276 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adm1275_id); + +static int adm1275_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1]; + int config, device_config; + int ret; + struct pmbus_driver_info *info; + struct adm1275_data *data; + const struct i2c_device_id *mid; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA + | I2C_FUNC_SMBUS_BLOCK_DATA)) + return -ENODEV; + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, block_buffer); + if (ret < 0) { + dev_err(&client->dev, "Failed to read Manufacturer ID\n"); + return ret; + } + if (ret != 3 || strncmp(block_buffer, "ADI", 3)) { + dev_err(&client->dev, "Unsupported Manufacturer ID\n"); + return -ENODEV; + } + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, block_buffer); + if (ret < 0) { + dev_err(&client->dev, "Failed to read Manufacturer Model\n"); + return ret; + } + for (mid = adm1275_id; mid->name[0]; mid++) { + if (!strncasecmp(mid->name, block_buffer, strlen(mid->name))) + break; + } + if (!mid->name[0]) { + dev_err(&client->dev, "Unsupported device\n"); + return -ENODEV; + } + + if (id->driver_data != mid->driver_data) + dev_notice(&client->dev, + "Device mismatch: Configured %s, detected %s\n", + id->name, mid->name); + + config = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG); + if (config < 0) + return config; + + device_config = i2c_smbus_read_byte_data(client, ADM1275_DEVICE_CONFIG); + if (device_config < 0) + return device_config; + + data = devm_kzalloc(&client->dev, sizeof(struct adm1275_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->id = mid->driver_data; + + info = &data->info; + + info->pages = 1; + info->format[PSC_VOLTAGE_IN] = direct; + info->format[PSC_VOLTAGE_OUT] = direct; + info->format[PSC_CURRENT_OUT] = direct; + info->m[PSC_CURRENT_OUT] = 807; + info->b[PSC_CURRENT_OUT] = 20475; + info->R[PSC_CURRENT_OUT] = -1; + info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; + + info->read_word_data = adm1275_read_word_data; + info->read_byte_data = adm1275_read_byte_data; + info->write_word_data = adm1275_write_word_data; + + if (data->id == adm1075) { + info->m[PSC_VOLTAGE_IN] = 27169; + info->b[PSC_VOLTAGE_IN] = 0; + info->R[PSC_VOLTAGE_IN] = -1; + info->m[PSC_VOLTAGE_OUT] = 27169; + info->b[PSC_VOLTAGE_OUT] = 0; + info->R[PSC_VOLTAGE_OUT] = -1; + } else if (config & ADM1275_VRANGE) { + info->m[PSC_VOLTAGE_IN] = 19199; + info->b[PSC_VOLTAGE_IN] = 0; + info->R[PSC_VOLTAGE_IN] = -2; + info->m[PSC_VOLTAGE_OUT] = 19199; + info->b[PSC_VOLTAGE_OUT] = 0; + info->R[PSC_VOLTAGE_OUT] = -2; + } else { + info->m[PSC_VOLTAGE_IN] = 6720; + info->b[PSC_VOLTAGE_IN] = 0; + info->R[PSC_VOLTAGE_IN] = -1; + info->m[PSC_VOLTAGE_OUT] = 6720; + info->b[PSC_VOLTAGE_OUT] = 0; + info->R[PSC_VOLTAGE_OUT] = -1; + } + + if (device_config & ADM1275_IOUT_WARN2_SELECT) + data->have_oc_fault = true; + + switch (data->id) { + case adm1075: + info->format[PSC_POWER] = direct; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -1; + switch (config & ADM1075_IRANGE_MASK) { + case ADM1075_IRANGE_25: + info->m[PSC_POWER] = 8549; + info->m[PSC_CURRENT_OUT] = 806; + break; + case ADM1075_IRANGE_50: + info->m[PSC_POWER] = 4279; + info->m[PSC_CURRENT_OUT] = 404; + break; + default: + dev_err(&client->dev, "Invalid input current range"); + info->m[PSC_POWER] = 0; + info->m[PSC_CURRENT_OUT] = 0; + break; + } + info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_PIN + | PMBUS_HAVE_STATUS_INPUT; + if (config & ADM1275_VIN_VOUT_SELECT) + info->func[0] |= + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; + break; + case adm1275: + if (config & ADM1275_VIN_VOUT_SELECT) + info->func[0] |= + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; + else + info->func[0] |= + PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT; + break; + case adm1276: + info->format[PSC_POWER] = direct; + info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_PIN + | PMBUS_HAVE_STATUS_INPUT; + if (config & ADM1275_VIN_VOUT_SELECT) + info->func[0] |= + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; + if (config & ADM1275_VRANGE) { + info->m[PSC_POWER] = 6043; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -2; + } else { + info->m[PSC_POWER] = 2115; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -1; + } + break; + } + + return pmbus_do_probe(client, id, info); +} + +static struct i2c_driver adm1275_driver = { + .driver = { + .name = "adm1275", + }, + .probe = adm1275_probe, + .remove = pmbus_do_remove, + .id_table = adm1275_id, +}; + +module_i2c_driver(adm1275_driver); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275 and compatibles"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c new file mode 100644 index 00000000000..a26b1d1d951 --- /dev/null +++ b/drivers/hwmon/pmbus/lm25066.c @@ -0,0 +1,530 @@ +/* + * Hardware monitoring driver for LM25056 / LM25063 / LM25066 / LM5064 / LM5066 + * + * Copyright (c) 2011 Ericsson AB. + * Copyright (c) 2013 Guenter Roeck + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 "pmbus.h" + +enum chips { lm25056, lm25063, lm25066, lm5064, lm5066 }; + +#define LM25066_READ_VAUX 0xd0 +#define LM25066_MFR_READ_IIN 0xd1 +#define LM25066_MFR_READ_PIN 0xd2 +#define LM25066_MFR_IIN_OC_WARN_LIMIT 0xd3 +#define LM25066_MFR_PIN_OP_WARN_LIMIT 0xd4 +#define LM25066_READ_PIN_PEAK 0xd5 +#define LM25066_CLEAR_PIN_PEAK 0xd6 +#define LM25066_DEVICE_SETUP 0xd9 +#define LM25066_READ_AVG_VIN 0xdc +#define LM25066_READ_AVG_VOUT 0xdd +#define LM25066_READ_AVG_IIN 0xde +#define LM25066_READ_AVG_PIN 0xdf + +#define LM25066_DEV_SETUP_CL (1 << 4) /* Current limit */ + +/* LM25056 only */ + +#define LM25056_VAUX_OV_WARN_LIMIT 0xe3 +#define LM25056_VAUX_UV_WARN_LIMIT 0xe4 + +#define LM25056_MFR_STS_VAUX_OV_WARN (1 << 1) +#define LM25056_MFR_STS_VAUX_UV_WARN (1 << 0) + +/* LM25063 only */ + +#define LM25063_READ_VOUT_MAX 0xe5 +#define LM25063_READ_VOUT_MIN 0xe6 + +struct __coeff { + short m, b, R; +}; + +#define PSC_CURRENT_IN_L (PSC_NUM_CLASSES) +#define PSC_POWER_L (PSC_NUM_CLASSES + 1) + +static struct __coeff lm25066_coeff[5][PSC_NUM_CLASSES + 2] = { + [lm25056] = { + [PSC_VOLTAGE_IN] = { + .m = 16296, + .R = -2, + }, + [PSC_CURRENT_IN] = { + .m = 13797, + .R = -2, + }, + [PSC_CURRENT_IN_L] = { + .m = 6726, + .R = -2, + }, + [PSC_POWER] = { + .m = 5501, + .R = -3, + }, + [PSC_POWER_L] = { + .m = 26882, + .R = -4, + }, + [PSC_TEMPERATURE] = { + .m = 1580, + .b = -14500, + .R = -2, + }, + }, + [lm25066] = { + [PSC_VOLTAGE_IN] = { + .m = 22070, + .R = -2, + }, + [PSC_VOLTAGE_OUT] = { + .m = 22070, + .R = -2, + }, + [PSC_CURRENT_IN] = { + .m = 13661, + .R = -2, + }, + [PSC_CURRENT_IN_L] = { + .m = 6852, + .R = -2, + }, + [PSC_POWER] = { + .m = 736, + .R = -2, + }, + [PSC_POWER_L] = { + .m = 369, + .R = -2, + }, + [PSC_TEMPERATURE] = { + .m = 16, + }, + }, + [lm25063] = { + [PSC_VOLTAGE_IN] = { + .m = 16000, + .R = -2, + }, + [PSC_VOLTAGE_OUT] = { + .m = 16000, + .R = -2, + }, + [PSC_CURRENT_IN] = { + .m = 10000, + .R = -2, + }, + [PSC_CURRENT_IN_L] = { + .m = 10000, + .R = -2, + }, + [PSC_POWER] = { + .m = 5000, + .R = -3, + }, + [PSC_POWER_L] = { + .m = 5000, + .R = -3, + }, + [PSC_TEMPERATURE] = { + .m = 15596, + .R = -3, + }, + }, + [lm5064] = { + [PSC_VOLTAGE_IN] = { + .m = 4611, + .R = -2, + }, + [PSC_VOLTAGE_OUT] = { + .m = 4621, + .R = -2, + }, + [PSC_CURRENT_IN] = { + .m = 10742, + .R = -2, + }, + [PSC_CURRENT_IN_L] = { + .m = 5456, + .R = -2, + }, + [PSC_POWER] = { + .m = 1204, + .R = -3, + }, + [PSC_POWER_L] = { + .m = 612, + .R = -3, + }, + [PSC_TEMPERATURE] = { + .m = 16, + }, + }, + [lm5066] = { + [PSC_VOLTAGE_IN] = { + .m = 4587, + .R = -2, + }, + [PSC_VOLTAGE_OUT] = { + .m = 4587, + .R = -2, + }, + [PSC_CURRENT_IN] = { + .m = 10753, + .R = -2, + }, + [PSC_CURRENT_IN_L] = { + .m = 5405, + .R = -2, + }, + [PSC_POWER] = { + .m = 1204, + .R = -3, + }, + [PSC_POWER_L] = { + .m = 605, + .R = -3, + }, + [PSC_TEMPERATURE] = { + .m = 16, + }, + }, +}; + +struct lm25066_data { + int id; + u16 rlimit; /* Maximum register value */ + struct pmbus_driver_info info; +}; + +#define to_lm25066_data(x) container_of(x, struct lm25066_data, info) + +static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct lm25066_data *data = to_lm25066_data(info); + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VMON: + ret = pmbus_read_word_data(client, 0, LM25066_READ_VAUX); + if (ret < 0) + break; + /* Adjust returned value to match VIN coefficients */ + switch (data->id) { + case lm25056: + /* VIN: 6.14 mV VAUX: 293 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 293, 6140); + break; + case lm25063: + /* VIN: 6.25 mV VAUX: 200.0 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 20, 625); + break; + case lm25066: + /* VIN: 4.54 mV VAUX: 283.2 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 2832, 45400); + break; + case lm5064: + /* VIN: 4.53 mV VAUX: 700 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 70, 453); + break; + case lm5066: + /* VIN: 2.18 mV VAUX: 725 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 725, 2180); + break; + } + break; + case PMBUS_READ_IIN: + ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_IIN); + break; + case PMBUS_READ_PIN: + ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_PIN); + break; + case PMBUS_IIN_OC_WARN_LIMIT: + ret = pmbus_read_word_data(client, 0, + LM25066_MFR_IIN_OC_WARN_LIMIT); + break; + case PMBUS_PIN_OP_WARN_LIMIT: + ret = pmbus_read_word_data(client, 0, + LM25066_MFR_PIN_OP_WARN_LIMIT); + break; + case PMBUS_VIRT_READ_VIN_AVG: + ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VIN); + break; + case PMBUS_VIRT_READ_VOUT_AVG: + ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VOUT); + break; + case PMBUS_VIRT_READ_IIN_AVG: + ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_IIN); + break; + case PMBUS_VIRT_READ_PIN_AVG: + ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_PIN); + break; + case PMBUS_VIRT_READ_PIN_MAX: + ret = pmbus_read_word_data(client, 0, LM25066_READ_PIN_PEAK); + break; + case PMBUS_VIRT_RESET_PIN_HISTORY: + ret = 0; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int lm25063_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, 0, LM25063_READ_VOUT_MAX); + break; + case PMBUS_VIRT_READ_VOUT_MIN: + ret = pmbus_read_word_data(client, 0, LM25063_READ_VOUT_MIN); + break; + default: + ret = lm25066_read_word_data(client, page, reg); + break; + } + return ret; +} + +static int lm25056_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_VMON_UV_WARN_LIMIT: + ret = pmbus_read_word_data(client, 0, + LM25056_VAUX_UV_WARN_LIMIT); + if (ret < 0) + break; + /* Adjust returned value to match VIN coefficients */ + ret = DIV_ROUND_CLOSEST(ret * 293, 6140); + break; + case PMBUS_VIRT_VMON_OV_WARN_LIMIT: + ret = pmbus_read_word_data(client, 0, + LM25056_VAUX_OV_WARN_LIMIT); + if (ret < 0) + break; + /* Adjust returned value to match VIN coefficients */ + ret = DIV_ROUND_CLOSEST(ret * 293, 6140); + break; + default: + ret = lm25066_read_word_data(client, page, reg); + break; + } + return ret; +} + +static int lm25056_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret, s; + + switch (reg) { + case PMBUS_VIRT_STATUS_VMON: + ret = pmbus_read_byte_data(client, 0, + PMBUS_STATUS_MFR_SPECIFIC); + if (ret < 0) + break; + s = 0; + if (ret & LM25056_MFR_STS_VAUX_UV_WARN) + s |= PB_VOLTAGE_UV_WARNING; + if (ret & LM25056_MFR_STS_VAUX_OV_WARN) + s |= PB_VOLTAGE_OV_WARNING; + ret = s; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct lm25066_data *data = to_lm25066_data(info); + int ret; + + switch (reg) { + case PMBUS_POUT_OP_FAULT_LIMIT: + case PMBUS_POUT_OP_WARN_LIMIT: + case PMBUS_VOUT_UV_WARN_LIMIT: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + case PMBUS_IIN_OC_FAULT_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_VIN_OV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); + ret = pmbus_write_word_data(client, 0, reg, word); + pmbus_clear_cache(client); + break; + case PMBUS_IIN_OC_WARN_LIMIT: + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); + ret = pmbus_write_word_data(client, 0, + LM25066_MFR_IIN_OC_WARN_LIMIT, + word); + pmbus_clear_cache(client); + break; + case PMBUS_PIN_OP_WARN_LIMIT: + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); + ret = pmbus_write_word_data(client, 0, + LM25066_MFR_PIN_OP_WARN_LIMIT, + word); + pmbus_clear_cache(client); + break; + case PMBUS_VIRT_VMON_UV_WARN_LIMIT: + /* Adjust from VIN coefficients (for LM25056) */ + word = DIV_ROUND_CLOSEST((int)word * 6140, 293); + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); + ret = pmbus_write_word_data(client, 0, + LM25056_VAUX_UV_WARN_LIMIT, word); + pmbus_clear_cache(client); + break; + case PMBUS_VIRT_VMON_OV_WARN_LIMIT: + /* Adjust from VIN coefficients (for LM25056) */ + word = DIV_ROUND_CLOSEST((int)word * 6140, 293); + word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); + ret = pmbus_write_word_data(client, 0, + LM25056_VAUX_OV_WARN_LIMIT, word); + pmbus_clear_cache(client); + break; + case PMBUS_VIRT_RESET_PIN_HISTORY: + ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int lm25066_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int config; + struct lm25066_data *data; + struct pmbus_driver_info *info; + struct __coeff *coeff; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA)) + return -ENODEV; + + data = devm_kzalloc(&client->dev, sizeof(struct lm25066_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + config = i2c_smbus_read_byte_data(client, LM25066_DEVICE_SETUP); + if (config < 0) + return config; + + data->id = id->driver_data; + info = &data->info; + + info->pages = 1; + info->format[PSC_VOLTAGE_IN] = direct; + info->format[PSC_VOLTAGE_OUT] = direct; + info->format[PSC_CURRENT_IN] = direct; + info->format[PSC_TEMPERATURE] = direct; + info->format[PSC_POWER] = direct; + + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VMON + | PMBUS_HAVE_PIN | PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + + if (data->id == lm25056) { + info->func[0] |= PMBUS_HAVE_STATUS_VMON; + info->read_word_data = lm25056_read_word_data; + info->read_byte_data = lm25056_read_byte_data; + data->rlimit = 0x0fff; + } else if (data->id == lm25063) { + info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_POUT; + info->read_word_data = lm25063_read_word_data; + data->rlimit = 0xffff; + } else { + info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; + info->read_word_data = lm25066_read_word_data; + data->rlimit = 0x0fff; + } + info->write_word_data = lm25066_write_word_data; + + coeff = &lm25066_coeff[data->id][0]; + info->m[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].m; + info->b[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].b; + info->R[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].R; + info->m[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].m; + info->b[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].b; + info->R[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].R; + info->m[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].m; + info->b[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].b; + info->R[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].R; + info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].b; + info->R[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].R; + info->b[PSC_POWER] = coeff[PSC_POWER].b; + info->R[PSC_POWER] = coeff[PSC_POWER].R; + if (config & LM25066_DEV_SETUP_CL) { + info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].m; + info->m[PSC_POWER] = coeff[PSC_POWER_L].m; + } else { + info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].m; + info->m[PSC_POWER] = coeff[PSC_POWER].m; + } + + return pmbus_do_probe(client, id, info); +} + +static const struct i2c_device_id lm25066_id[] = { + {"lm25056", lm25056}, + {"lm25063", lm25063}, + {"lm25066", lm25066}, + {"lm5064", lm5064}, + {"lm5066", lm5066}, + { } +}; + +MODULE_DEVICE_TABLE(i2c, lm25066_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver lm25066_driver = { + .driver = { + .name = "lm25066", + }, + .probe = lm25066_probe, + .remove = pmbus_do_remove, + .id_table = lm25066_id, +}; + +module_i2c_driver(lm25066_driver); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("PMBus driver for LM25066 and compatible chips"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c new file mode 100644 index 00000000000..e24ed521051 --- /dev/null +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -0,0 +1,507 @@ +/* + * Hardware monitoring driver for LTC2974, LTC2977, LTC2978, LTC3880, + * LTC3883, and LTM4676 + * + * Copyright (c) 2011 Ericsson AB. + * Copyright (c) 2013, 2014 Guenter Roeck + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include "pmbus.h" + +enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883, ltm4676 }; + +/* Common for all chips */ +#define LTC2978_MFR_VOUT_PEAK 0xdd +#define LTC2978_MFR_VIN_PEAK 0xde +#define LTC2978_MFR_TEMPERATURE_PEAK 0xdf +#define LTC2978_MFR_SPECIAL_ID 0xe7 + +/* LTC2974, LCT2977, and LTC2978 */ +#define LTC2978_MFR_VOUT_MIN 0xfb +#define LTC2978_MFR_VIN_MIN 0xfc +#define LTC2978_MFR_TEMPERATURE_MIN 0xfd + +/* LTC2974 only */ +#define LTC2974_MFR_IOUT_PEAK 0xd7 +#define LTC2974_MFR_IOUT_MIN 0xd8 + +/* LTC3880, LTC3883, and LTM4676 */ +#define LTC3880_MFR_IOUT_PEAK 0xd7 +#define LTC3880_MFR_CLEAR_PEAKS 0xe3 +#define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4 + +/* LTC3883 only */ +#define LTC3883_MFR_IIN_PEAK 0xe1 + +#define LTC2974_ID_REV1 0x0212 +#define LTC2974_ID_REV2 0x0213 +#define LTC2977_ID 0x0130 +#define LTC2978_ID_REV1 0x0121 +#define LTC2978_ID_REV2 0x0122 +#define LTC2978A_ID 0x0124 +#define LTC3880_ID 0x4000 +#define LTC3880_ID_MASK 0xff00 +#define LTC3883_ID 0x4300 +#define LTC3883_ID_MASK 0xff00 +#define LTM4676_ID 0x4480 /* datasheet claims 0x440X */ +#define LTM4676_ID_MASK 0xfff0 + +#define LTC2974_NUM_PAGES 4 +#define LTC2978_NUM_PAGES 8 +#define LTC3880_NUM_PAGES 2 +#define LTC3883_NUM_PAGES 1 + +/* + * LTC2978 clears peak data whenever the CLEAR_FAULTS command is executed, which + * happens pretty much each time chip data is updated. Raw peak data therefore + * does not provide much value. To be able to provide useful peak data, keep an + * internal cache of measured peak data, which is only cleared if an explicit + * "clear peak" command is executed for the sensor in question. + */ + +struct ltc2978_data { + enum chips id; + u16 vin_min, vin_max; + u16 temp_min[LTC2974_NUM_PAGES], temp_max[LTC2974_NUM_PAGES]; + u16 vout_min[LTC2978_NUM_PAGES], vout_max[LTC2978_NUM_PAGES]; + u16 iout_min[LTC2974_NUM_PAGES], iout_max[LTC2974_NUM_PAGES]; + u16 iin_max; + u16 temp2_max; + struct pmbus_driver_info info; +}; + +#define to_ltc2978_data(x) container_of(x, struct ltc2978_data, info) + +static inline int lin11_to_val(int data) +{ + s16 e = ((s16)data) >> 11; + s32 m = (((s16)(data << 5)) >> 5); + + /* + * mantissa is 10 bit + sign, exponent adds up to 15 bit. + * Add 6 bit to exponent for maximum accuracy (10 + 15 + 6 = 31). + */ + e += 6; + return (e < 0 ? m >> -e : m << e); +} + +static int ltc2978_read_word_data_common(struct i2c_client *client, int page, + int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct ltc2978_data *data = to_ltc2978_data(info); + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VIN_MAX: + ret = pmbus_read_word_data(client, page, LTC2978_MFR_VIN_PEAK); + if (ret >= 0) { + if (lin11_to_val(ret) > lin11_to_val(data->vin_max)) + data->vin_max = ret; + ret = data->vin_max; + } + break; + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_PEAK); + if (ret >= 0) { + /* + * VOUT is 16 bit unsigned with fixed exponent, + * so we can compare it directly + */ + if (ret > data->vout_max[page]) + data->vout_max[page] = ret; + ret = data->vout_max[page]; + } + break; + case PMBUS_VIRT_READ_TEMP_MAX: + ret = pmbus_read_word_data(client, page, + LTC2978_MFR_TEMPERATURE_PEAK); + if (ret >= 0) { + if (lin11_to_val(ret) + > lin11_to_val(data->temp_max[page])) + data->temp_max[page] = ret; + ret = data->temp_max[page]; + } + break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + case PMBUS_VIRT_RESET_VIN_HISTORY: + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = 0; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct ltc2978_data *data = to_ltc2978_data(info); + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VIN_MIN: + ret = pmbus_read_word_data(client, page, LTC2978_MFR_VIN_MIN); + if (ret >= 0) { + if (lin11_to_val(ret) < lin11_to_val(data->vin_min)) + data->vin_min = ret; + ret = data->vin_min; + } + break; + case PMBUS_VIRT_READ_VOUT_MIN: + ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_MIN); + if (ret >= 0) { + /* + * VOUT_MIN is known to not be supported on some lots + * of LTC2978 revision 1, and will return the maximum + * possible voltage if read. If VOUT_MAX is valid and + * lower than the reading of VOUT_MIN, use it instead. + */ + if (data->vout_max[page] && ret > data->vout_max[page]) + ret = data->vout_max[page]; + if (ret < data->vout_min[page]) + data->vout_min[page] = ret; + ret = data->vout_min[page]; + } + break; + case PMBUS_VIRT_READ_TEMP_MIN: + ret = pmbus_read_word_data(client, page, + LTC2978_MFR_TEMPERATURE_MIN); + if (ret >= 0) { + if (lin11_to_val(ret) + < lin11_to_val(data->temp_min[page])) + data->temp_min[page] = ret; + ret = data->temp_min[page]; + } + break; + case PMBUS_VIRT_READ_IOUT_MAX: + case PMBUS_VIRT_RESET_IOUT_HISTORY: + case PMBUS_VIRT_READ_TEMP2_MAX: + case PMBUS_VIRT_RESET_TEMP2_HISTORY: + ret = -ENXIO; + break; + default: + ret = ltc2978_read_word_data_common(client, page, reg); + break; + } + return ret; +} + +static int ltc2974_read_word_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct ltc2978_data *data = to_ltc2978_data(info); + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_IOUT_MAX: + ret = pmbus_read_word_data(client, page, LTC2974_MFR_IOUT_PEAK); + if (ret >= 0) { + if (lin11_to_val(ret) + > lin11_to_val(data->iout_max[page])) + data->iout_max[page] = ret; + ret = data->iout_max[page]; + } + break; + case PMBUS_VIRT_READ_IOUT_MIN: + ret = pmbus_read_word_data(client, page, LTC2974_MFR_IOUT_MIN); + if (ret >= 0) { + if (lin11_to_val(ret) + < lin11_to_val(data->iout_min[page])) + data->iout_min[page] = ret; + ret = data->iout_min[page]; + } + break; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + ret = 0; + break; + default: + ret = ltc2978_read_word_data(client, page, reg); + break; + } + return ret; +} + +static int ltc3880_read_word_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct ltc2978_data *data = to_ltc2978_data(info); + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_IOUT_MAX: + ret = pmbus_read_word_data(client, page, LTC3880_MFR_IOUT_PEAK); + if (ret >= 0) { + if (lin11_to_val(ret) + > lin11_to_val(data->iout_max[page])) + data->iout_max[page] = ret; + ret = data->iout_max[page]; + } + break; + case PMBUS_VIRT_READ_TEMP2_MAX: + ret = pmbus_read_word_data(client, page, + LTC3880_MFR_TEMPERATURE2_PEAK); + if (ret >= 0) { + if (lin11_to_val(ret) > lin11_to_val(data->temp2_max)) + data->temp2_max = ret; + ret = data->temp2_max; + } + break; + case PMBUS_VIRT_READ_VIN_MIN: + case PMBUS_VIRT_READ_VOUT_MIN: + case PMBUS_VIRT_READ_TEMP_MIN: + ret = -ENXIO; + break; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + case PMBUS_VIRT_RESET_TEMP2_HISTORY: + ret = 0; + break; + default: + ret = ltc2978_read_word_data_common(client, page, reg); + break; + } + return ret; +} + +static int ltc3883_read_word_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct ltc2978_data *data = to_ltc2978_data(info); + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_IIN_MAX: + ret = pmbus_read_word_data(client, page, LTC3883_MFR_IIN_PEAK); + if (ret >= 0) { + if (lin11_to_val(ret) + > lin11_to_val(data->iin_max)) + data->iin_max = ret; + ret = data->iin_max; + } + break; + case PMBUS_VIRT_RESET_IIN_HISTORY: + ret = 0; + break; + default: + ret = ltc3880_read_word_data(client, page, reg); + break; + } + return ret; +} + +static int ltc2978_clear_peaks(struct i2c_client *client, int page, + enum chips id) +{ + int ret; + + if (id == ltc3880 || id == ltc3883) + ret = pmbus_write_byte(client, 0, LTC3880_MFR_CLEAR_PEAKS); + else + ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); + + return ret; +} + +static int ltc2978_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct ltc2978_data *data = to_ltc2978_data(info); + int ret; + + switch (reg) { + case PMBUS_VIRT_RESET_IIN_HISTORY: + data->iin_max = 0x7c00; + ret = ltc2978_clear_peaks(client, page, data->id); + break; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + data->iout_max[page] = 0x7c00; + data->iout_min[page] = 0xfbff; + ret = ltc2978_clear_peaks(client, page, data->id); + break; + case PMBUS_VIRT_RESET_TEMP2_HISTORY: + data->temp2_max = 0x7c00; + ret = ltc2978_clear_peaks(client, page, data->id); + break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + data->vout_min[page] = 0xffff; + data->vout_max[page] = 0; + ret = ltc2978_clear_peaks(client, page, data->id); + break; + case PMBUS_VIRT_RESET_VIN_HISTORY: + data->vin_min = 0x7bff; + data->vin_max = 0x7c00; + ret = ltc2978_clear_peaks(client, page, data->id); + break; + case PMBUS_VIRT_RESET_TEMP_HISTORY: + data->temp_min[page] = 0x7bff; + data->temp_max[page] = 0x7c00; + ret = ltc2978_clear_peaks(client, page, data->id); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static const struct i2c_device_id ltc2978_id[] = { + {"ltc2974", ltc2974}, + {"ltc2977", ltc2977}, + {"ltc2978", ltc2978}, + {"ltc3880", ltc3880}, + {"ltc3883", ltc3883}, + {"ltm4676", ltm4676}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ltc2978_id); + +static int ltc2978_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int chip_id, i; + struct ltc2978_data *data; + struct pmbus_driver_info *info; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -ENODEV; + + data = devm_kzalloc(&client->dev, sizeof(struct ltc2978_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + chip_id = i2c_smbus_read_word_data(client, LTC2978_MFR_SPECIAL_ID); + if (chip_id < 0) + return chip_id; + + if (chip_id == LTC2974_ID_REV1 || chip_id == LTC2974_ID_REV2) { + data->id = ltc2974; + } else if (chip_id == LTC2977_ID) { + data->id = ltc2977; + } else if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2 || + chip_id == LTC2978A_ID) { + data->id = ltc2978; + } else if ((chip_id & LTC3880_ID_MASK) == LTC3880_ID) { + data->id = ltc3880; + } else if ((chip_id & LTC3883_ID_MASK) == LTC3883_ID) { + data->id = ltc3883; + } else if ((chip_id & LTM4676_ID_MASK) == LTM4676_ID) { + data->id = ltm4676; + } else { + dev_err(&client->dev, "Unsupported chip ID 0x%x\n", chip_id); + return -ENODEV; + } + if (data->id != id->driver_data) + dev_warn(&client->dev, + "Device mismatch: Configured %s, detected %s\n", + id->name, + ltc2978_id[data->id].name); + + info = &data->info; + info->write_word_data = ltc2978_write_word_data; + + data->vin_min = 0x7bff; + data->vin_max = 0x7c00; + for (i = 0; i < ARRAY_SIZE(data->vout_min); i++) + data->vout_min[i] = 0xffff; + for (i = 0; i < ARRAY_SIZE(data->iout_min); i++) + data->iout_min[i] = 0xfbff; + for (i = 0; i < ARRAY_SIZE(data->iout_max); i++) + data->iout_max[i] = 0x7c00; + for (i = 0; i < ARRAY_SIZE(data->temp_min); i++) + data->temp_min[i] = 0x7bff; + for (i = 0; i < ARRAY_SIZE(data->temp_max); i++) + data->temp_max[i] = 0x7c00; + data->temp2_max = 0x7c00; + + switch (data->id) { + case ltc2974: + info->read_word_data = ltc2974_read_word_data; + info->pages = LTC2974_NUM_PAGES; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP2; + for (i = 0; i < info->pages; i++) { + info->func[i] |= PMBUS_HAVE_VOUT + | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_POUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; + } + break; + case ltc2977: + case ltc2978: + info->read_word_data = ltc2978_read_word_data; + info->pages = LTC2978_NUM_PAGES; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + for (i = 1; i < LTC2978_NUM_PAGES; i++) { + info->func[i] = PMBUS_HAVE_VOUT + | PMBUS_HAVE_STATUS_VOUT; + } + break; + case ltc3880: + case ltm4676: + info->read_word_data = ltc3880_read_word_data; + info->pages = LTC3880_NUM_PAGES; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN + | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP + | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP; + info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_POUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + break; + case ltc3883: + info->read_word_data = ltc3883_read_word_data; + info->pages = LTC3883_NUM_PAGES; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN + | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP + | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP; + break; + default: + return -ENODEV; + } + return pmbus_do_probe(client, id, info); +} + +/* This is the driver that will be inserted */ +static struct i2c_driver ltc2978_driver = { + .driver = { + .name = "ltc2978", + }, + .probe = ltc2978_probe, + .remove = pmbus_do_remove, + .id_table = ltc2978_id, +}; + +module_i2c_driver(ltc2978_driver); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("PMBus driver for LTC2974, LTC2978, LTC3880, LTC3883, and LTM4676"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/max16064.c b/drivers/hwmon/pmbus/max16064.c new file mode 100644 index 00000000000..fa237a3c329 --- /dev/null +++ b/drivers/hwmon/pmbus/max16064.c @@ -0,0 +1,127 @@ +/* + * Hardware monitoring driver for Maxim MAX16064 + * + * Copyright (c) 2011 Ericsson AB. + * + * 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/i2c.h> +#include "pmbus.h" + +#define MAX16064_MFR_VOUT_PEAK 0xd4 +#define MAX16064_MFR_TEMPERATURE_PEAK 0xd6 + +static int max16064_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, page, + MAX16064_MFR_VOUT_PEAK); + break; + case PMBUS_VIRT_READ_TEMP_MAX: + ret = pmbus_read_word_data(client, page, + MAX16064_MFR_TEMPERATURE_PEAK); + break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = 0; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int max16064_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_RESET_VOUT_HISTORY: + ret = pmbus_write_word_data(client, page, + MAX16064_MFR_VOUT_PEAK, 0); + break; + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = pmbus_write_word_data(client, page, + MAX16064_MFR_TEMPERATURE_PEAK, + 0xffff); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static struct pmbus_driver_info max16064_info = { + .pages = 4, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_VOLTAGE_IN] = 19995, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = -1, + .m[PSC_VOLTAGE_OUT] = 19995, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = -1, + .m[PSC_TEMPERATURE] = -7612, + .b[PSC_TEMPERATURE] = 335, + .R[PSC_TEMPERATURE] = -3, + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_TEMP + | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_TEMP, + .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .read_word_data = max16064_read_word_data, + .write_word_data = max16064_write_word_data, +}; + +static int max16064_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + return pmbus_do_probe(client, id, &max16064_info); +} + +static const struct i2c_device_id max16064_id[] = { + {"max16064", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, max16064_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver max16064_driver = { + .driver = { + .name = "max16064", + }, + .probe = max16064_probe, + .remove = pmbus_do_remove, + .id_table = max16064_id, +}; + +module_i2c_driver(max16064_driver); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("PMBus driver for Maxim MAX16064"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c new file mode 100644 index 00000000000..7e930c3ce1a --- /dev/null +++ b/drivers/hwmon/pmbus/max34440.c @@ -0,0 +1,435 @@ +/* + * Hardware monitoring driver for Maxim MAX34440/MAX34441 + * + * Copyright (c) 2011 Ericsson AB. + * Copyright (c) 2012 Guenter Roeck + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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/i2c.h> +#include "pmbus.h" + +enum chips { max34440, max34441, max34446, max34460, max34461 }; + +#define MAX34440_MFR_VOUT_PEAK 0xd4 +#define MAX34440_MFR_IOUT_PEAK 0xd5 +#define MAX34440_MFR_TEMPERATURE_PEAK 0xd6 +#define MAX34440_MFR_VOUT_MIN 0xd7 + +#define MAX34446_MFR_POUT_PEAK 0xe0 +#define MAX34446_MFR_POUT_AVG 0xe1 +#define MAX34446_MFR_IOUT_AVG 0xe2 +#define MAX34446_MFR_TEMPERATURE_AVG 0xe3 + +#define MAX34440_STATUS_OC_WARN (1 << 0) +#define MAX34440_STATUS_OC_FAULT (1 << 1) +#define MAX34440_STATUS_OT_FAULT (1 << 5) +#define MAX34440_STATUS_OT_WARN (1 << 6) + +struct max34440_data { + int id; + struct pmbus_driver_info info; +}; + +#define to_max34440_data(x) container_of(x, struct max34440_data, info) + +static int max34440_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct max34440_data *data = to_max34440_data(info); + + switch (reg) { + case PMBUS_VIRT_READ_VOUT_MIN: + ret = pmbus_read_word_data(client, page, + MAX34440_MFR_VOUT_MIN); + break; + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, page, + MAX34440_MFR_VOUT_PEAK); + break; + case PMBUS_VIRT_READ_IOUT_AVG: + if (data->id != max34446) + return -ENXIO; + ret = pmbus_read_word_data(client, page, + MAX34446_MFR_IOUT_AVG); + break; + case PMBUS_VIRT_READ_IOUT_MAX: + ret = pmbus_read_word_data(client, page, + MAX34440_MFR_IOUT_PEAK); + break; + case PMBUS_VIRT_READ_POUT_AVG: + if (data->id != max34446) + return -ENXIO; + ret = pmbus_read_word_data(client, page, + MAX34446_MFR_POUT_AVG); + break; + case PMBUS_VIRT_READ_POUT_MAX: + if (data->id != max34446) + return -ENXIO; + ret = pmbus_read_word_data(client, page, + MAX34446_MFR_POUT_PEAK); + break; + case PMBUS_VIRT_READ_TEMP_AVG: + if (data->id != max34446 && data->id != max34460 && + data->id != max34461) + return -ENXIO; + ret = pmbus_read_word_data(client, page, + MAX34446_MFR_TEMPERATURE_AVG); + break; + case PMBUS_VIRT_READ_TEMP_MAX: + ret = pmbus_read_word_data(client, page, + MAX34440_MFR_TEMPERATURE_PEAK); + break; + case PMBUS_VIRT_RESET_POUT_HISTORY: + if (data->id != max34446) + return -ENXIO; + ret = 0; + break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + case PMBUS_VIRT_RESET_IOUT_HISTORY: + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = 0; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int max34440_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct max34440_data *data = to_max34440_data(info); + int ret; + + switch (reg) { + case PMBUS_VIRT_RESET_POUT_HISTORY: + ret = pmbus_write_word_data(client, page, + MAX34446_MFR_POUT_PEAK, 0); + if (ret) + break; + ret = pmbus_write_word_data(client, page, + MAX34446_MFR_POUT_AVG, 0); + break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + ret = pmbus_write_word_data(client, page, + MAX34440_MFR_VOUT_MIN, 0x7fff); + if (ret) + break; + ret = pmbus_write_word_data(client, page, + MAX34440_MFR_VOUT_PEAK, 0); + break; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + ret = pmbus_write_word_data(client, page, + MAX34440_MFR_IOUT_PEAK, 0); + if (!ret && data->id == max34446) + ret = pmbus_write_word_data(client, page, + MAX34446_MFR_IOUT_AVG, 0); + + break; + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = pmbus_write_word_data(client, page, + MAX34440_MFR_TEMPERATURE_PEAK, + 0x8000); + if (!ret && data->id == max34446) + ret = pmbus_write_word_data(client, page, + MAX34446_MFR_TEMPERATURE_AVG, 0); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int max34440_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret = 0; + int mfg_status; + + if (page >= 0) { + ret = pmbus_set_page(client, page); + if (ret < 0) + return ret; + } + + switch (reg) { + case PMBUS_STATUS_IOUT: + mfg_status = pmbus_read_word_data(client, 0, + PMBUS_STATUS_MFR_SPECIFIC); + if (mfg_status < 0) + return mfg_status; + if (mfg_status & MAX34440_STATUS_OC_WARN) + ret |= PB_IOUT_OC_WARNING; + if (mfg_status & MAX34440_STATUS_OC_FAULT) + ret |= PB_IOUT_OC_FAULT; + break; + case PMBUS_STATUS_TEMPERATURE: + mfg_status = pmbus_read_word_data(client, 0, + PMBUS_STATUS_MFR_SPECIFIC); + if (mfg_status < 0) + return mfg_status; + if (mfg_status & MAX34440_STATUS_OT_WARN) + ret |= PB_TEMP_OT_WARNING; + if (mfg_status & MAX34440_STATUS_OT_FAULT) + ret |= PB_TEMP_OT_FAULT; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static struct pmbus_driver_info max34440_info[] = { + [max34440] = { + .pages = 14, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_CURRENT_OUT] = direct, + .m[PSC_VOLTAGE_IN] = 1, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = 3, /* R = 0 in datasheet reflects mV */ + .m[PSC_VOLTAGE_OUT] = 1, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 3, /* R = 0 in datasheet reflects mV */ + .m[PSC_CURRENT_OUT] = 1, + .b[PSC_CURRENT_OUT] = 0, + .R[PSC_CURRENT_OUT] = 3, /* R = 0 in datasheet reflects mA */ + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 2, + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[5] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[6] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[7] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[8] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[9] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[10] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[11] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[12] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[13] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_byte_data = max34440_read_byte_data, + .read_word_data = max34440_read_word_data, + .write_word_data = max34440_write_word_data, + }, + [max34441] = { + .pages = 12, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_FAN] = direct, + .m[PSC_VOLTAGE_IN] = 1, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = 3, + .m[PSC_VOLTAGE_OUT] = 1, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 3, + .m[PSC_CURRENT_OUT] = 1, + .b[PSC_CURRENT_OUT] = 0, + .R[PSC_CURRENT_OUT] = 3, + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 2, + .m[PSC_FAN] = 1, + .b[PSC_FAN] = 0, + .R[PSC_FAN] = 0, + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[5] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12, + .func[6] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[7] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[8] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[9] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[10] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[11] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_byte_data = max34440_read_byte_data, + .read_word_data = max34440_read_word_data, + .write_word_data = max34440_write_word_data, + }, + [max34446] = { + .pages = 7, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_POWER] = direct, + .m[PSC_VOLTAGE_IN] = 1, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = 3, + .m[PSC_VOLTAGE_OUT] = 1, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 3, + .m[PSC_CURRENT_OUT] = 1, + .b[PSC_CURRENT_OUT] = 0, + .R[PSC_CURRENT_OUT] = 3, + .m[PSC_POWER] = 1, + .b[PSC_POWER] = 0, + .R[PSC_POWER] = 3, + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 2, + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, + .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, + .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + .func[4] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[5] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[6] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_byte_data = max34440_read_byte_data, + .read_word_data = max34440_read_word_data, + .write_word_data = max34440_write_word_data, + }, + [max34460] = { + .pages = 18, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_VOLTAGE_OUT] = 1, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 3, + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 2, + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[5] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[6] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[7] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[8] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[9] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[10] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[11] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[13] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[14] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[15] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[16] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[17] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_byte_data = max34440_read_byte_data, + .read_word_data = max34440_read_word_data, + .write_word_data = max34440_write_word_data, + }, + [max34461] = { + .pages = 23, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_VOLTAGE_OUT] = 1, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 3, + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 2, + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[5] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[6] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[7] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[8] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[9] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[10] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[11] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[12] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[13] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[14] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .func[15] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + /* page 16 is reserved */ + .func[17] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[19] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[20] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .func[21] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + .read_byte_data = max34440_read_byte_data, + .read_word_data = max34440_read_word_data, + .write_word_data = max34440_write_word_data, + }, +}; + +static int max34440_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max34440_data *data; + + data = devm_kzalloc(&client->dev, sizeof(struct max34440_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + data->id = id->driver_data; + data->info = max34440_info[id->driver_data]; + + return pmbus_do_probe(client, id, &data->info); +} + +static const struct i2c_device_id max34440_id[] = { + {"max34440", max34440}, + {"max34441", max34441}, + {"max34446", max34446}, + {"max34460", max34460}, + {"max34461", max34461}, + {} +}; +MODULE_DEVICE_TABLE(i2c, max34440_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver max34440_driver = { + .driver = { + .name = "max34440", + }, + .probe = max34440_probe, + .remove = pmbus_do_remove, + .id_table = max34440_id, +}; + +module_i2c_driver(max34440_driver); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("PMBus driver for Maxim MAX34440/MAX34441"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/max8688.c b/drivers/hwmon/pmbus/max8688.c new file mode 100644 index 00000000000..f04454a42fd --- /dev/null +++ b/drivers/hwmon/pmbus/max8688.c @@ -0,0 +1,204 @@ +/* + * Hardware monitoring driver for Maxim MAX8688 + * + * Copyright (c) 2011 Ericsson AB. + * + * 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/i2c.h> +#include "pmbus.h" + +#define MAX8688_MFR_VOUT_PEAK 0xd4 +#define MAX8688_MFR_IOUT_PEAK 0xd5 +#define MAX8688_MFR_TEMPERATURE_PEAK 0xd6 +#define MAX8688_MFG_STATUS 0xd8 + +#define MAX8688_STATUS_OC_FAULT (1 << 4) +#define MAX8688_STATUS_OV_FAULT (1 << 5) +#define MAX8688_STATUS_OV_WARNING (1 << 8) +#define MAX8688_STATUS_UV_FAULT (1 << 9) +#define MAX8688_STATUS_UV_WARNING (1 << 10) +#define MAX8688_STATUS_UC_FAULT (1 << 11) +#define MAX8688_STATUS_OC_WARNING (1 << 12) +#define MAX8688_STATUS_OT_FAULT (1 << 13) +#define MAX8688_STATUS_OT_WARNING (1 << 14) + +static int max8688_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + if (page) + return -ENXIO; + + switch (reg) { + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, 0, MAX8688_MFR_VOUT_PEAK); + break; + case PMBUS_VIRT_READ_IOUT_MAX: + ret = pmbus_read_word_data(client, 0, MAX8688_MFR_IOUT_PEAK); + break; + case PMBUS_VIRT_READ_TEMP_MAX: + ret = pmbus_read_word_data(client, 0, + MAX8688_MFR_TEMPERATURE_PEAK); + break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + case PMBUS_VIRT_RESET_IOUT_HISTORY: + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = 0; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int max8688_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_RESET_VOUT_HISTORY: + ret = pmbus_write_word_data(client, 0, MAX8688_MFR_VOUT_PEAK, + 0); + break; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + ret = pmbus_write_word_data(client, 0, MAX8688_MFR_IOUT_PEAK, + 0); + break; + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = pmbus_write_word_data(client, 0, + MAX8688_MFR_TEMPERATURE_PEAK, + 0xffff); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int max8688_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret = 0; + int mfg_status; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_STATUS_VOUT: + mfg_status = pmbus_read_word_data(client, 0, + MAX8688_MFG_STATUS); + if (mfg_status < 0) + return mfg_status; + if (mfg_status & MAX8688_STATUS_UV_WARNING) + ret |= PB_VOLTAGE_UV_WARNING; + if (mfg_status & MAX8688_STATUS_UV_FAULT) + ret |= PB_VOLTAGE_UV_FAULT; + if (mfg_status & MAX8688_STATUS_OV_WARNING) + ret |= PB_VOLTAGE_OV_WARNING; + if (mfg_status & MAX8688_STATUS_OV_FAULT) + ret |= PB_VOLTAGE_OV_FAULT; + break; + case PMBUS_STATUS_IOUT: + mfg_status = pmbus_read_word_data(client, 0, + MAX8688_MFG_STATUS); + if (mfg_status < 0) + return mfg_status; + if (mfg_status & MAX8688_STATUS_UC_FAULT) + ret |= PB_IOUT_UC_FAULT; + if (mfg_status & MAX8688_STATUS_OC_WARNING) + ret |= PB_IOUT_OC_WARNING; + if (mfg_status & MAX8688_STATUS_OC_FAULT) + ret |= PB_IOUT_OC_FAULT; + break; + case PMBUS_STATUS_TEMPERATURE: + mfg_status = pmbus_read_word_data(client, 0, + MAX8688_MFG_STATUS); + if (mfg_status < 0) + return mfg_status; + if (mfg_status & MAX8688_STATUS_OT_WARNING) + ret |= PB_TEMP_OT_WARNING; + if (mfg_status & MAX8688_STATUS_OT_FAULT) + ret |= PB_TEMP_OT_FAULT; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static struct pmbus_driver_info max8688_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_CURRENT_OUT] = direct, + .m[PSC_VOLTAGE_IN] = 19995, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = -1, + .m[PSC_VOLTAGE_OUT] = 19995, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = -1, + .m[PSC_CURRENT_OUT] = 23109, + .b[PSC_CURRENT_OUT] = 0, + .R[PSC_CURRENT_OUT] = -2, + .m[PSC_TEMPERATURE] = -7612, + .b[PSC_TEMPERATURE] = 335, + .R[PSC_TEMPERATURE] = -3, + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP + | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_STATUS_TEMP, + .read_byte_data = max8688_read_byte_data, + .read_word_data = max8688_read_word_data, + .write_word_data = max8688_write_word_data, +}; + +static int max8688_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + return pmbus_do_probe(client, id, &max8688_info); +} + +static const struct i2c_device_id max8688_id[] = { + {"max8688", 0}, + { } +}; + +MODULE_DEVICE_TABLE(i2c, max8688_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver max8688_driver = { + .driver = { + .name = "max8688", + }, + .probe = max8688_probe, + .remove = pmbus_do_remove, + .id_table = max8688_id, +}; + +module_i2c_driver(max8688_driver); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("PMBus driver for Maxim MAX8688"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c new file mode 100644 index 00000000000..7e91700131a --- /dev/null +++ b/drivers/hwmon/pmbus/pmbus.c @@ -0,0 +1,217 @@ +/* + * Hardware monitoring driver for PMBus devices + * + * Copyright (c) 2010, 2011 Ericsson AB. + * + * 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/mutex.h> +#include <linux/i2c.h> +#include "pmbus.h" + +/* + * Find sensor groups and status registers on each page. + */ +static void pmbus_find_sensor_groups(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + int page; + + /* Sensors detected on page 0 only */ + if (pmbus_check_word_register(client, 0, PMBUS_READ_VIN)) + info->func[0] |= PMBUS_HAVE_VIN; + if (pmbus_check_word_register(client, 0, PMBUS_READ_VCAP)) + info->func[0] |= PMBUS_HAVE_VCAP; + if (pmbus_check_word_register(client, 0, PMBUS_READ_IIN)) + info->func[0] |= PMBUS_HAVE_IIN; + if (pmbus_check_word_register(client, 0, PMBUS_READ_PIN)) + info->func[0] |= PMBUS_HAVE_PIN; + if (info->func[0] + && pmbus_check_byte_register(client, 0, PMBUS_STATUS_INPUT)) + info->func[0] |= PMBUS_HAVE_STATUS_INPUT; + if (pmbus_check_byte_register(client, 0, PMBUS_FAN_CONFIG_12) && + pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_1)) { + info->func[0] |= PMBUS_HAVE_FAN12; + if (pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_12)) + info->func[0] |= PMBUS_HAVE_STATUS_FAN12; + } + if (pmbus_check_byte_register(client, 0, PMBUS_FAN_CONFIG_34) && + pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_3)) { + info->func[0] |= PMBUS_HAVE_FAN34; + if (pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_34)) + info->func[0] |= PMBUS_HAVE_STATUS_FAN34; + } + if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_1)) + info->func[0] |= PMBUS_HAVE_TEMP; + if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_2)) + info->func[0] |= PMBUS_HAVE_TEMP2; + if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_3)) + info->func[0] |= PMBUS_HAVE_TEMP3; + if (info->func[0] & (PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 + | PMBUS_HAVE_TEMP3) + && pmbus_check_byte_register(client, 0, + PMBUS_STATUS_TEMPERATURE)) + info->func[0] |= PMBUS_HAVE_STATUS_TEMP; + + /* Sensors detected on all pages */ + for (page = 0; page < info->pages; page++) { + if (pmbus_check_word_register(client, page, PMBUS_READ_VOUT)) { + info->func[page] |= PMBUS_HAVE_VOUT; + if (pmbus_check_byte_register(client, page, + PMBUS_STATUS_VOUT)) + info->func[page] |= PMBUS_HAVE_STATUS_VOUT; + } + if (pmbus_check_word_register(client, page, PMBUS_READ_IOUT)) { + info->func[page] |= PMBUS_HAVE_IOUT; + if (pmbus_check_byte_register(client, 0, + PMBUS_STATUS_IOUT)) + info->func[page] |= PMBUS_HAVE_STATUS_IOUT; + } + if (pmbus_check_word_register(client, page, PMBUS_READ_POUT)) + info->func[page] |= PMBUS_HAVE_POUT; + } +} + +/* + * Identify chip parameters. + */ +static int pmbus_identify(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + int ret = 0; + + if (!info->pages) { + /* + * Check if the PAGE command is supported. If it is, + * keep setting the page number until it fails or until the + * maximum number of pages has been reached. Assume that + * this is the number of pages supported by the chip. + */ + if (pmbus_check_byte_register(client, 0, PMBUS_PAGE)) { + int page; + + for (page = 1; page < PMBUS_PAGES; page++) { + if (pmbus_set_page(client, page) < 0) + break; + } + pmbus_set_page(client, 0); + info->pages = page; + } else { + info->pages = 1; + } + } + + if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) { + int vout_mode; + + vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); + if (vout_mode >= 0 && vout_mode != 0xff) { + switch (vout_mode >> 5) { + case 0: + break; + case 1: + info->format[PSC_VOLTAGE_OUT] = vid; + break; + case 2: + info->format[PSC_VOLTAGE_OUT] = direct; + break; + default: + ret = -ENODEV; + goto abort; + } + } + } + + /* + * We should check if the COEFFICIENTS register is supported. + * If it is, and the chip is configured for direct mode, we can read + * the coefficients from the chip, one set per group of sensor + * registers. + * + * To do this, we will need access to a chip which actually supports the + * COEFFICIENTS command, since the command is too complex to implement + * without testing it. Until then, abort if a chip configured for direct + * mode was detected. + */ + if (info->format[PSC_VOLTAGE_OUT] == direct) { + ret = -ENODEV; + goto abort; + } + + /* Try to find sensor groups */ + pmbus_find_sensor_groups(client, info); +abort: + return ret; +} + +static int pmbus_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pmbus_driver_info *info; + + info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->pages = id->driver_data; + info->identify = pmbus_identify; + + return pmbus_do_probe(client, id, info); +} + +/* + * Use driver_data to set the number of pages supported by the chip. + */ +static const struct i2c_device_id pmbus_id[] = { + {"adp4000", 1}, + {"bmr453", 1}, + {"bmr454", 1}, + {"mdt040", 1}, + {"ncp4200", 1}, + {"ncp4208", 1}, + {"pdt003", 1}, + {"pdt006", 1}, + {"pdt012", 1}, + {"pmbus", 0}, + {"tps40400", 1}, + {"tps40422", 2}, + {"udt020", 1}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, pmbus_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver pmbus_driver = { + .driver = { + .name = "pmbus", + }, + .probe = pmbus_probe, + .remove = pmbus_do_remove, + .id_table = pmbus_id, +}; + +module_i2c_driver(pmbus_driver); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("Generic PMBus driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h new file mode 100644 index 00000000000..fa9beb3eb60 --- /dev/null +++ b/drivers/hwmon/pmbus/pmbus.h @@ -0,0 +1,387 @@ +/* + * pmbus.h - Common defines and structures for PMBus devices + * + * Copyright (c) 2010, 2011 Ericsson AB. + * Copyright (c) 2012 Guenter Roeck + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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. + */ + +#ifndef PMBUS_H +#define PMBUS_H + +/* + * Registers + */ +#define PMBUS_PAGE 0x00 +#define PMBUS_OPERATION 0x01 +#define PMBUS_ON_OFF_CONFIG 0x02 +#define PMBUS_CLEAR_FAULTS 0x03 +#define PMBUS_PHASE 0x04 + +#define PMBUS_CAPABILITY 0x19 +#define PMBUS_QUERY 0x1A + +#define PMBUS_VOUT_MODE 0x20 +#define PMBUS_VOUT_COMMAND 0x21 +#define PMBUS_VOUT_TRIM 0x22 +#define PMBUS_VOUT_CAL_OFFSET 0x23 +#define PMBUS_VOUT_MAX 0x24 +#define PMBUS_VOUT_MARGIN_HIGH 0x25 +#define PMBUS_VOUT_MARGIN_LOW 0x26 +#define PMBUS_VOUT_TRANSITION_RATE 0x27 +#define PMBUS_VOUT_DROOP 0x28 +#define PMBUS_VOUT_SCALE_LOOP 0x29 +#define PMBUS_VOUT_SCALE_MONITOR 0x2A + +#define PMBUS_COEFFICIENTS 0x30 +#define PMBUS_POUT_MAX 0x31 + +#define PMBUS_FAN_CONFIG_12 0x3A +#define PMBUS_FAN_COMMAND_1 0x3B +#define PMBUS_FAN_COMMAND_2 0x3C +#define PMBUS_FAN_CONFIG_34 0x3D +#define PMBUS_FAN_COMMAND_3 0x3E +#define PMBUS_FAN_COMMAND_4 0x3F + +#define PMBUS_VOUT_OV_FAULT_LIMIT 0x40 +#define PMBUS_VOUT_OV_FAULT_RESPONSE 0x41 +#define PMBUS_VOUT_OV_WARN_LIMIT 0x42 +#define PMBUS_VOUT_UV_WARN_LIMIT 0x43 +#define PMBUS_VOUT_UV_FAULT_LIMIT 0x44 +#define PMBUS_VOUT_UV_FAULT_RESPONSE 0x45 +#define PMBUS_IOUT_OC_FAULT_LIMIT 0x46 +#define PMBUS_IOUT_OC_FAULT_RESPONSE 0x47 +#define PMBUS_IOUT_OC_LV_FAULT_LIMIT 0x48 +#define PMBUS_IOUT_OC_LV_FAULT_RESPONSE 0x49 +#define PMBUS_IOUT_OC_WARN_LIMIT 0x4A +#define PMBUS_IOUT_UC_FAULT_LIMIT 0x4B +#define PMBUS_IOUT_UC_FAULT_RESPONSE 0x4C + +#define PMBUS_OT_FAULT_LIMIT 0x4F +#define PMBUS_OT_FAULT_RESPONSE 0x50 +#define PMBUS_OT_WARN_LIMIT 0x51 +#define PMBUS_UT_WARN_LIMIT 0x52 +#define PMBUS_UT_FAULT_LIMIT 0x53 +#define PMBUS_UT_FAULT_RESPONSE 0x54 +#define PMBUS_VIN_OV_FAULT_LIMIT 0x55 +#define PMBUS_VIN_OV_FAULT_RESPONSE 0x56 +#define PMBUS_VIN_OV_WARN_LIMIT 0x57 +#define PMBUS_VIN_UV_WARN_LIMIT 0x58 +#define PMBUS_VIN_UV_FAULT_LIMIT 0x59 + +#define PMBUS_IIN_OC_FAULT_LIMIT 0x5B +#define PMBUS_IIN_OC_WARN_LIMIT 0x5D + +#define PMBUS_POUT_OP_FAULT_LIMIT 0x68 +#define PMBUS_POUT_OP_WARN_LIMIT 0x6A +#define PMBUS_PIN_OP_WARN_LIMIT 0x6B + +#define PMBUS_STATUS_BYTE 0x78 +#define PMBUS_STATUS_WORD 0x79 +#define PMBUS_STATUS_VOUT 0x7A +#define PMBUS_STATUS_IOUT 0x7B +#define PMBUS_STATUS_INPUT 0x7C +#define PMBUS_STATUS_TEMPERATURE 0x7D +#define PMBUS_STATUS_CML 0x7E +#define PMBUS_STATUS_OTHER 0x7F +#define PMBUS_STATUS_MFR_SPECIFIC 0x80 +#define PMBUS_STATUS_FAN_12 0x81 +#define PMBUS_STATUS_FAN_34 0x82 + +#define PMBUS_READ_VIN 0x88 +#define PMBUS_READ_IIN 0x89 +#define PMBUS_READ_VCAP 0x8A +#define PMBUS_READ_VOUT 0x8B +#define PMBUS_READ_IOUT 0x8C +#define PMBUS_READ_TEMPERATURE_1 0x8D +#define PMBUS_READ_TEMPERATURE_2 0x8E +#define PMBUS_READ_TEMPERATURE_3 0x8F +#define PMBUS_READ_FAN_SPEED_1 0x90 +#define PMBUS_READ_FAN_SPEED_2 0x91 +#define PMBUS_READ_FAN_SPEED_3 0x92 +#define PMBUS_READ_FAN_SPEED_4 0x93 +#define PMBUS_READ_DUTY_CYCLE 0x94 +#define PMBUS_READ_FREQUENCY 0x95 +#define PMBUS_READ_POUT 0x96 +#define PMBUS_READ_PIN 0x97 + +#define PMBUS_REVISION 0x98 +#define PMBUS_MFR_ID 0x99 +#define PMBUS_MFR_MODEL 0x9A +#define PMBUS_MFR_REVISION 0x9B +#define PMBUS_MFR_LOCATION 0x9C +#define PMBUS_MFR_DATE 0x9D +#define PMBUS_MFR_SERIAL 0x9E + +/* + * Virtual registers. + * Useful to support attributes which are not supported by standard PMBus + * registers but exist as manufacturer specific registers on individual chips. + * Must be mapped to real registers in device specific code. + * + * Semantics: + * Virtual registers are all word size. + * READ registers are read-only; writes are either ignored or return an error. + * RESET registers are read/write. Reading reset registers returns zero + * (used for detection), writing any value causes the associated history to be + * reset. + * Virtual registers have to be handled in device specific driver code. Chip + * driver code returns non-negative register values if a virtual register is + * supported, or a negative error code if not. The chip driver may return + * -ENODATA or any other error code in this case, though an error code other + * than -ENODATA is handled more efficiently and thus preferred. Either case, + * the calling PMBus core code will abort if the chip driver returns an error + * code when reading or writing virtual registers. + */ +#define PMBUS_VIRT_BASE 0x100 +#define PMBUS_VIRT_READ_TEMP_AVG (PMBUS_VIRT_BASE + 0) +#define PMBUS_VIRT_READ_TEMP_MIN (PMBUS_VIRT_BASE + 1) +#define PMBUS_VIRT_READ_TEMP_MAX (PMBUS_VIRT_BASE + 2) +#define PMBUS_VIRT_RESET_TEMP_HISTORY (PMBUS_VIRT_BASE + 3) +#define PMBUS_VIRT_READ_VIN_AVG (PMBUS_VIRT_BASE + 4) +#define PMBUS_VIRT_READ_VIN_MIN (PMBUS_VIRT_BASE + 5) +#define PMBUS_VIRT_READ_VIN_MAX (PMBUS_VIRT_BASE + 6) +#define PMBUS_VIRT_RESET_VIN_HISTORY (PMBUS_VIRT_BASE + 7) +#define PMBUS_VIRT_READ_IIN_AVG (PMBUS_VIRT_BASE + 8) +#define PMBUS_VIRT_READ_IIN_MIN (PMBUS_VIRT_BASE + 9) +#define PMBUS_VIRT_READ_IIN_MAX (PMBUS_VIRT_BASE + 10) +#define PMBUS_VIRT_RESET_IIN_HISTORY (PMBUS_VIRT_BASE + 11) +#define PMBUS_VIRT_READ_PIN_AVG (PMBUS_VIRT_BASE + 12) +#define PMBUS_VIRT_READ_PIN_MAX (PMBUS_VIRT_BASE + 13) +#define PMBUS_VIRT_RESET_PIN_HISTORY (PMBUS_VIRT_BASE + 14) +#define PMBUS_VIRT_READ_POUT_AVG (PMBUS_VIRT_BASE + 15) +#define PMBUS_VIRT_READ_POUT_MAX (PMBUS_VIRT_BASE + 16) +#define PMBUS_VIRT_RESET_POUT_HISTORY (PMBUS_VIRT_BASE + 17) +#define PMBUS_VIRT_READ_VOUT_AVG (PMBUS_VIRT_BASE + 18) +#define PMBUS_VIRT_READ_VOUT_MIN (PMBUS_VIRT_BASE + 19) +#define PMBUS_VIRT_READ_VOUT_MAX (PMBUS_VIRT_BASE + 20) +#define PMBUS_VIRT_RESET_VOUT_HISTORY (PMBUS_VIRT_BASE + 21) +#define PMBUS_VIRT_READ_IOUT_AVG (PMBUS_VIRT_BASE + 22) +#define PMBUS_VIRT_READ_IOUT_MIN (PMBUS_VIRT_BASE + 23) +#define PMBUS_VIRT_READ_IOUT_MAX (PMBUS_VIRT_BASE + 24) +#define PMBUS_VIRT_RESET_IOUT_HISTORY (PMBUS_VIRT_BASE + 25) +#define PMBUS_VIRT_READ_TEMP2_AVG (PMBUS_VIRT_BASE + 26) +#define PMBUS_VIRT_READ_TEMP2_MIN (PMBUS_VIRT_BASE + 27) +#define PMBUS_VIRT_READ_TEMP2_MAX (PMBUS_VIRT_BASE + 28) +#define PMBUS_VIRT_RESET_TEMP2_HISTORY (PMBUS_VIRT_BASE + 29) + +#define PMBUS_VIRT_READ_VMON (PMBUS_VIRT_BASE + 30) +#define PMBUS_VIRT_VMON_UV_WARN_LIMIT (PMBUS_VIRT_BASE + 31) +#define PMBUS_VIRT_VMON_OV_WARN_LIMIT (PMBUS_VIRT_BASE + 32) +#define PMBUS_VIRT_VMON_UV_FAULT_LIMIT (PMBUS_VIRT_BASE + 33) +#define PMBUS_VIRT_VMON_OV_FAULT_LIMIT (PMBUS_VIRT_BASE + 34) +#define PMBUS_VIRT_STATUS_VMON (PMBUS_VIRT_BASE + 35) + +/* + * CAPABILITY + */ +#define PB_CAPABILITY_SMBALERT (1<<4) +#define PB_CAPABILITY_ERROR_CHECK (1<<7) + +/* + * VOUT_MODE + */ +#define PB_VOUT_MODE_MODE_MASK 0xe0 +#define PB_VOUT_MODE_PARAM_MASK 0x1f + +#define PB_VOUT_MODE_LINEAR 0x00 +#define PB_VOUT_MODE_VID 0x20 +#define PB_VOUT_MODE_DIRECT 0x40 + +/* + * Fan configuration + */ +#define PB_FAN_2_PULSE_MASK ((1 << 0) | (1 << 1)) +#define PB_FAN_2_RPM (1 << 2) +#define PB_FAN_2_INSTALLED (1 << 3) +#define PB_FAN_1_PULSE_MASK ((1 << 4) | (1 << 5)) +#define PB_FAN_1_RPM (1 << 6) +#define PB_FAN_1_INSTALLED (1 << 7) + +/* + * STATUS_BYTE, STATUS_WORD (lower) + */ +#define PB_STATUS_NONE_ABOVE (1<<0) +#define PB_STATUS_CML (1<<1) +#define PB_STATUS_TEMPERATURE (1<<2) +#define PB_STATUS_VIN_UV (1<<3) +#define PB_STATUS_IOUT_OC (1<<4) +#define PB_STATUS_VOUT_OV (1<<5) +#define PB_STATUS_OFF (1<<6) +#define PB_STATUS_BUSY (1<<7) + +/* + * STATUS_WORD (upper) + */ +#define PB_STATUS_UNKNOWN (1<<8) +#define PB_STATUS_OTHER (1<<9) +#define PB_STATUS_FANS (1<<10) +#define PB_STATUS_POWER_GOOD_N (1<<11) +#define PB_STATUS_WORD_MFR (1<<12) +#define PB_STATUS_INPUT (1<<13) +#define PB_STATUS_IOUT_POUT (1<<14) +#define PB_STATUS_VOUT (1<<15) + +/* + * STATUS_IOUT + */ +#define PB_POUT_OP_WARNING (1<<0) +#define PB_POUT_OP_FAULT (1<<1) +#define PB_POWER_LIMITING (1<<2) +#define PB_CURRENT_SHARE_FAULT (1<<3) +#define PB_IOUT_UC_FAULT (1<<4) +#define PB_IOUT_OC_WARNING (1<<5) +#define PB_IOUT_OC_LV_FAULT (1<<6) +#define PB_IOUT_OC_FAULT (1<<7) + +/* + * STATUS_VOUT, STATUS_INPUT + */ +#define PB_VOLTAGE_UV_FAULT (1<<4) +#define PB_VOLTAGE_UV_WARNING (1<<5) +#define PB_VOLTAGE_OV_WARNING (1<<6) +#define PB_VOLTAGE_OV_FAULT (1<<7) + +/* + * STATUS_INPUT + */ +#define PB_PIN_OP_WARNING (1<<0) +#define PB_IIN_OC_WARNING (1<<1) +#define PB_IIN_OC_FAULT (1<<2) + +/* + * STATUS_TEMPERATURE + */ +#define PB_TEMP_UT_FAULT (1<<4) +#define PB_TEMP_UT_WARNING (1<<5) +#define PB_TEMP_OT_WARNING (1<<6) +#define PB_TEMP_OT_FAULT (1<<7) + +/* + * STATUS_FAN + */ +#define PB_FAN_AIRFLOW_WARNING (1<<0) +#define PB_FAN_AIRFLOW_FAULT (1<<1) +#define PB_FAN_FAN2_SPEED_OVERRIDE (1<<2) +#define PB_FAN_FAN1_SPEED_OVERRIDE (1<<3) +#define PB_FAN_FAN2_WARNING (1<<4) +#define PB_FAN_FAN1_WARNING (1<<5) +#define PB_FAN_FAN2_FAULT (1<<6) +#define PB_FAN_FAN1_FAULT (1<<7) + +/* + * CML_FAULT_STATUS + */ +#define PB_CML_FAULT_OTHER_MEM_LOGIC (1<<0) +#define PB_CML_FAULT_OTHER_COMM (1<<1) +#define PB_CML_FAULT_PROCESSOR (1<<3) +#define PB_CML_FAULT_MEMORY (1<<4) +#define PB_CML_FAULT_PACKET_ERROR (1<<5) +#define PB_CML_FAULT_INVALID_DATA (1<<6) +#define PB_CML_FAULT_INVALID_COMMAND (1<<7) + +enum pmbus_sensor_classes { + PSC_VOLTAGE_IN = 0, + PSC_VOLTAGE_OUT, + PSC_CURRENT_IN, + PSC_CURRENT_OUT, + PSC_POWER, + PSC_TEMPERATURE, + PSC_FAN, + PSC_NUM_CLASSES /* Number of power sensor classes */ +}; + +#define PMBUS_PAGES 32 /* Per PMBus specification */ + +/* Functionality bit mask */ +#define PMBUS_HAVE_VIN (1 << 0) +#define PMBUS_HAVE_VCAP (1 << 1) +#define PMBUS_HAVE_VOUT (1 << 2) +#define PMBUS_HAVE_IIN (1 << 3) +#define PMBUS_HAVE_IOUT (1 << 4) +#define PMBUS_HAVE_PIN (1 << 5) +#define PMBUS_HAVE_POUT (1 << 6) +#define PMBUS_HAVE_FAN12 (1 << 7) +#define PMBUS_HAVE_FAN34 (1 << 8) +#define PMBUS_HAVE_TEMP (1 << 9) +#define PMBUS_HAVE_TEMP2 (1 << 10) +#define PMBUS_HAVE_TEMP3 (1 << 11) +#define PMBUS_HAVE_STATUS_VOUT (1 << 12) +#define PMBUS_HAVE_STATUS_IOUT (1 << 13) +#define PMBUS_HAVE_STATUS_INPUT (1 << 14) +#define PMBUS_HAVE_STATUS_TEMP (1 << 15) +#define PMBUS_HAVE_STATUS_FAN12 (1 << 16) +#define PMBUS_HAVE_STATUS_FAN34 (1 << 17) +#define PMBUS_HAVE_VMON (1 << 18) +#define PMBUS_HAVE_STATUS_VMON (1 << 19) + +enum pmbus_data_format { linear = 0, direct, vid }; + +struct pmbus_driver_info { + int pages; /* Total number of pages */ + enum pmbus_data_format format[PSC_NUM_CLASSES]; + /* + * Support one set of coefficients for each sensor type + * Used for chips providing data in direct mode. + */ + int m[PSC_NUM_CLASSES]; /* mantissa for direct data format */ + int b[PSC_NUM_CLASSES]; /* offset */ + int R[PSC_NUM_CLASSES]; /* exponent */ + + u32 func[PMBUS_PAGES]; /* Functionality, per page */ + /* + * The following functions map manufacturing specific register values + * to PMBus standard register values. Specify only if mapping is + * necessary. + * Functions return the register value (read) or zero (write) if + * successful. A return value of -ENODATA indicates that there is no + * manufacturer specific register, but that a standard PMBus register + * may exist. Any other negative return value indicates that the + * register does not exist, and that no attempt should be made to read + * the standard register. + */ + int (*read_byte_data)(struct i2c_client *client, int page, int reg); + int (*read_word_data)(struct i2c_client *client, int page, int reg); + int (*write_word_data)(struct i2c_client *client, int page, int reg, + u16 word); + int (*write_byte)(struct i2c_client *client, int page, u8 value); + /* + * The identify function determines supported PMBus functionality. + * This function is only necessary if a chip driver supports multiple + * chips, and the chip functionality is not pre-determined. + */ + int (*identify)(struct i2c_client *client, + struct pmbus_driver_info *info); +}; + +/* Function declarations */ + +void pmbus_clear_cache(struct i2c_client *client); +int pmbus_set_page(struct i2c_client *client, u8 page); +int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg); +int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word); +int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg); +int pmbus_write_byte(struct i2c_client *client, int page, u8 value); +void pmbus_clear_faults(struct i2c_client *client); +bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg); +bool pmbus_check_word_register(struct i2c_client *client, int page, int reg); +int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, + struct pmbus_driver_info *info); +int pmbus_do_remove(struct i2c_client *client); +const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client + *client); + +#endif /* PMBUS_H */ diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c new file mode 100644 index 00000000000..291d11fe93e --- /dev/null +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -0,0 +1,1803 @@ +/* + * Hardware monitoring driver for PMBus devices + * + * Copyright (c) 2010, 2011 Ericsson AB. + * Copyright (c) 2012 Guenter Roeck + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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> +#include <linux/jiffies.h> +#include <linux/i2c/pmbus.h> +#include "pmbus.h" + +/* + * Number of additional attribute pointers to allocate + * with each call to krealloc + */ +#define PMBUS_ATTR_ALLOC_SIZE 32 + +/* + * Index into status register array, per status register group + */ +#define PB_STATUS_BASE 0 +#define PB_STATUS_VOUT_BASE (PB_STATUS_BASE + PMBUS_PAGES) +#define PB_STATUS_IOUT_BASE (PB_STATUS_VOUT_BASE + PMBUS_PAGES) +#define PB_STATUS_FAN_BASE (PB_STATUS_IOUT_BASE + PMBUS_PAGES) +#define PB_STATUS_FAN34_BASE (PB_STATUS_FAN_BASE + PMBUS_PAGES) +#define PB_STATUS_TEMP_BASE (PB_STATUS_FAN34_BASE + PMBUS_PAGES) +#define PB_STATUS_INPUT_BASE (PB_STATUS_TEMP_BASE + PMBUS_PAGES) +#define PB_STATUS_VMON_BASE (PB_STATUS_INPUT_BASE + 1) + +#define PB_NUM_STATUS_REG (PB_STATUS_VMON_BASE + 1) + +#define PMBUS_NAME_SIZE 24 + +struct pmbus_sensor { + struct pmbus_sensor *next; + char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */ + struct device_attribute attribute; + u8 page; /* page number */ + u16 reg; /* register */ + enum pmbus_sensor_classes class; /* sensor class */ + bool update; /* runtime sensor update needed */ + int data; /* Sensor data. + Negative if there was a read error */ +}; +#define to_pmbus_sensor(_attr) \ + container_of(_attr, struct pmbus_sensor, attribute) + +struct pmbus_boolean { + char name[PMBUS_NAME_SIZE]; /* sysfs boolean name */ + struct sensor_device_attribute attribute; + struct pmbus_sensor *s1; + struct pmbus_sensor *s2; +}; +#define to_pmbus_boolean(_attr) \ + container_of(_attr, struct pmbus_boolean, attribute) + +struct pmbus_label { + char name[PMBUS_NAME_SIZE]; /* sysfs label name */ + struct device_attribute attribute; + char label[PMBUS_NAME_SIZE]; /* label */ +}; +#define to_pmbus_label(_attr) \ + container_of(_attr, struct pmbus_label, attribute) + +struct pmbus_data { + struct device *dev; + struct device *hwmon_dev; + + u32 flags; /* from platform data */ + + int exponent[PMBUS_PAGES]; + /* linear mode: exponent for output voltages */ + + const struct pmbus_driver_info *info; + + int max_attributes; + int num_attributes; + struct attribute_group group; + const struct attribute_group *groups[2]; + + struct pmbus_sensor *sensors; + + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + + /* + * A single status register covers multiple attributes, + * so we keep them all together. + */ + u8 status[PB_NUM_STATUS_REG]; + u8 status_register; + + u8 currpage; +}; + +void pmbus_clear_cache(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + + data->valid = false; +} +EXPORT_SYMBOL_GPL(pmbus_clear_cache); + +int pmbus_set_page(struct i2c_client *client, u8 page) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + int rv = 0; + int newpage; + + if (page != data->currpage) { + rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + newpage = i2c_smbus_read_byte_data(client, PMBUS_PAGE); + if (newpage != page) + rv = -EIO; + else + data->currpage = page; + } + return rv; +} +EXPORT_SYMBOL_GPL(pmbus_set_page); + +int pmbus_write_byte(struct i2c_client *client, int page, u8 value) +{ + int rv; + + if (page >= 0) { + rv = pmbus_set_page(client, page); + if (rv < 0) + return rv; + } + + return i2c_smbus_write_byte(client, value); +} +EXPORT_SYMBOL_GPL(pmbus_write_byte); + +/* + * _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_write_byte(struct i2c_client *client, int page, u8 value) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->write_byte) { + status = info->write_byte(client, page, value); + if (status != -ENODATA) + return status; + } + return pmbus_write_byte(client, page, value); +} + +int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word) +{ + int rv; + + rv = pmbus_set_page(client, page); + if (rv < 0) + return rv; + + return i2c_smbus_write_word_data(client, reg, word); +} +EXPORT_SYMBOL_GPL(pmbus_write_word_data); + +/* + * _pmbus_write_word_data() is similar to pmbus_write_word_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->write_word_data) { + status = info->write_word_data(client, page, reg, word); + if (status != -ENODATA) + return status; + } + if (reg >= PMBUS_VIRT_BASE) + return -ENXIO; + return pmbus_write_word_data(client, page, reg, word); +} + +int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg) +{ + int rv; + + rv = pmbus_set_page(client, page); + if (rv < 0) + return rv; + + return i2c_smbus_read_word_data(client, reg); +} +EXPORT_SYMBOL_GPL(pmbus_read_word_data); + +/* + * _pmbus_read_word_data() is similar to pmbus_read_word_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_read_word_data(struct i2c_client *client, int page, int reg) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->read_word_data) { + status = info->read_word_data(client, page, reg); + if (status != -ENODATA) + return status; + } + if (reg >= PMBUS_VIRT_BASE) + return -ENXIO; + return pmbus_read_word_data(client, page, reg); +} + +int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg) +{ + int rv; + + if (page >= 0) { + rv = pmbus_set_page(client, page); + if (rv < 0) + return rv; + } + + return i2c_smbus_read_byte_data(client, reg); +} +EXPORT_SYMBOL_GPL(pmbus_read_byte_data); + +/* + * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->read_byte_data) { + status = info->read_byte_data(client, page, reg); + if (status != -ENODATA) + return status; + } + return pmbus_read_byte_data(client, page, reg); +} + +static void pmbus_clear_fault_page(struct i2c_client *client, int page) +{ + _pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); +} + +void pmbus_clear_faults(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + int i; + + for (i = 0; i < data->info->pages; i++) + pmbus_clear_fault_page(client, i); +} +EXPORT_SYMBOL_GPL(pmbus_clear_faults); + +static int pmbus_check_status_cml(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + int status, status2; + + status = _pmbus_read_byte_data(client, -1, data->status_register); + if (status < 0 || (status & PB_STATUS_CML)) { + status2 = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML); + if (status2 < 0 || (status2 & PB_CML_FAULT_INVALID_COMMAND)) + return -EIO; + } + return 0; +} + +static bool pmbus_check_register(struct i2c_client *client, + int (*func)(struct i2c_client *client, + int page, int reg), + int page, int reg) +{ + int rv; + struct pmbus_data *data = i2c_get_clientdata(client); + + rv = func(client, page, reg); + if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK)) + rv = pmbus_check_status_cml(client); + pmbus_clear_fault_page(client, -1); + return rv >= 0; +} + +bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg) +{ + return pmbus_check_register(client, _pmbus_read_byte_data, page, reg); +} +EXPORT_SYMBOL_GPL(pmbus_check_byte_register); + +bool pmbus_check_word_register(struct i2c_client *client, int page, int reg) +{ + return pmbus_check_register(client, _pmbus_read_word_data, page, reg); +} +EXPORT_SYMBOL_GPL(pmbus_check_word_register); + +const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + + return data->info; +} +EXPORT_SYMBOL_GPL(pmbus_get_driver_info); + +static struct _pmbus_status { + u32 func; + u16 base; + u16 reg; +} pmbus_status[] = { + { PMBUS_HAVE_STATUS_VOUT, PB_STATUS_VOUT_BASE, PMBUS_STATUS_VOUT }, + { PMBUS_HAVE_STATUS_IOUT, PB_STATUS_IOUT_BASE, PMBUS_STATUS_IOUT }, + { PMBUS_HAVE_STATUS_TEMP, PB_STATUS_TEMP_BASE, + PMBUS_STATUS_TEMPERATURE }, + { PMBUS_HAVE_STATUS_FAN12, PB_STATUS_FAN_BASE, PMBUS_STATUS_FAN_12 }, + { PMBUS_HAVE_STATUS_FAN34, PB_STATUS_FAN34_BASE, PMBUS_STATUS_FAN_34 }, +}; + +static struct pmbus_data *pmbus_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + struct pmbus_sensor *sensor; + + mutex_lock(&data->update_lock); + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + int i, j; + + for (i = 0; i < info->pages; i++) { + data->status[PB_STATUS_BASE + i] + = _pmbus_read_byte_data(client, i, + data->status_register); + for (j = 0; j < ARRAY_SIZE(pmbus_status); j++) { + struct _pmbus_status *s = &pmbus_status[j]; + + if (!(info->func[i] & s->func)) + continue; + data->status[s->base + i] + = _pmbus_read_byte_data(client, i, + s->reg); + } + } + + if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) + data->status[PB_STATUS_INPUT_BASE] + = _pmbus_read_byte_data(client, 0, + PMBUS_STATUS_INPUT); + + if (info->func[0] & PMBUS_HAVE_STATUS_VMON) + data->status[PB_STATUS_VMON_BASE] + = _pmbus_read_byte_data(client, 0, + PMBUS_VIRT_STATUS_VMON); + + for (sensor = data->sensors; sensor; sensor = sensor->next) { + if (!data->valid || sensor->update) + sensor->data + = _pmbus_read_word_data(client, + sensor->page, + sensor->reg); + } + pmbus_clear_faults(client); + data->last_updated = jiffies; + data->valid = 1; + } + mutex_unlock(&data->update_lock); + return data; +} + +/* + * Convert linear sensor values to milli- or micro-units + * depending on sensor type. + */ +static long pmbus_reg2data_linear(struct pmbus_data *data, + struct pmbus_sensor *sensor) +{ + s16 exponent; + s32 mantissa; + long val; + + if (sensor->class == PSC_VOLTAGE_OUT) { /* LINEAR16 */ + exponent = data->exponent[sensor->page]; + mantissa = (u16) sensor->data; + } else { /* LINEAR11 */ + exponent = ((s16)sensor->data) >> 11; + mantissa = ((s16)((sensor->data & 0x7ff) << 5)) >> 5; + } + + val = mantissa; + + /* scale result to milli-units for all sensors except fans */ + if (sensor->class != PSC_FAN) + val = val * 1000L; + + /* scale result to micro-units for power sensors */ + if (sensor->class == PSC_POWER) + val = val * 1000L; + + if (exponent >= 0) + val <<= exponent; + else + val >>= -exponent; + + return val; +} + +/* + * Convert direct sensor values to milli- or micro-units + * depending on sensor type. + */ +static long pmbus_reg2data_direct(struct pmbus_data *data, + struct pmbus_sensor *sensor) +{ + long val = (s16) sensor->data; + long m, b, R; + + m = data->info->m[sensor->class]; + b = data->info->b[sensor->class]; + R = data->info->R[sensor->class]; + + if (m == 0) + return 0; + + /* X = 1/m * (Y * 10^-R - b) */ + R = -R; + /* scale result to milli-units for everything but fans */ + if (sensor->class != PSC_FAN) { + R += 3; + b *= 1000; + } + + /* scale result to micro-units for power sensors */ + if (sensor->class == PSC_POWER) { + R += 3; + b *= 1000; + } + + while (R > 0) { + val *= 10; + R--; + } + while (R < 0) { + val = DIV_ROUND_CLOSEST(val, 10); + R++; + } + + return (val - b) / m; +} + +/* + * Convert VID sensor values to milli- or micro-units + * depending on sensor type. + * We currently only support VR11. + */ +static long pmbus_reg2data_vid(struct pmbus_data *data, + struct pmbus_sensor *sensor) +{ + long val = sensor->data; + + if (val < 0x02 || val > 0xb2) + return 0; + return DIV_ROUND_CLOSEST(160000 - (val - 2) * 625, 100); +} + +static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) +{ + long val; + + switch (data->info->format[sensor->class]) { + case direct: + val = pmbus_reg2data_direct(data, sensor); + break; + case vid: + val = pmbus_reg2data_vid(data, sensor); + break; + case linear: + default: + val = pmbus_reg2data_linear(data, sensor); + break; + } + return val; +} + +#define MAX_MANTISSA (1023 * 1000) +#define MIN_MANTISSA (511 * 1000) + +static u16 pmbus_data2reg_linear(struct pmbus_data *data, + struct pmbus_sensor *sensor, long val) +{ + s16 exponent = 0, mantissa; + bool negative = false; + + /* simple case */ + if (val == 0) + return 0; + + if (sensor->class == PSC_VOLTAGE_OUT) { + /* LINEAR16 does not support negative voltages */ + if (val < 0) + return 0; + + /* + * For a static exponents, we don't have a choice + * but to adjust the value to it. + */ + if (data->exponent[sensor->page] < 0) + val <<= -data->exponent[sensor->page]; + else + val >>= data->exponent[sensor->page]; + val = DIV_ROUND_CLOSEST(val, 1000); + return val & 0xffff; + } + + if (val < 0) { + negative = true; + val = -val; + } + + /* Power is in uW. Convert to mW before converting. */ + if (sensor->class == PSC_POWER) + val = DIV_ROUND_CLOSEST(val, 1000L); + + /* + * For simplicity, convert fan data to milli-units + * before calculating the exponent. + */ + if (sensor->class == PSC_FAN) + val = val * 1000; + + /* Reduce large mantissa until it fits into 10 bit */ + while (val >= MAX_MANTISSA && exponent < 15) { + exponent++; + val >>= 1; + } + /* Increase small mantissa to improve precision */ + while (val < MIN_MANTISSA && exponent > -15) { + exponent--; + val <<= 1; + } + + /* Convert mantissa from milli-units to units */ + mantissa = DIV_ROUND_CLOSEST(val, 1000); + + /* Ensure that resulting number is within range */ + if (mantissa > 0x3ff) + mantissa = 0x3ff; + + /* restore sign */ + if (negative) + mantissa = -mantissa; + + /* Convert to 5 bit exponent, 11 bit mantissa */ + return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800); +} + +static u16 pmbus_data2reg_direct(struct pmbus_data *data, + struct pmbus_sensor *sensor, long val) +{ + long m, b, R; + + m = data->info->m[sensor->class]; + b = data->info->b[sensor->class]; + R = data->info->R[sensor->class]; + + /* Power is in uW. Adjust R and b. */ + if (sensor->class == PSC_POWER) { + R -= 3; + b *= 1000; + } + + /* Calculate Y = (m * X + b) * 10^R */ + if (sensor->class != PSC_FAN) { + R -= 3; /* Adjust R and b for data in milli-units */ + b *= 1000; + } + val = val * m + b; + + while (R > 0) { + val *= 10; + R--; + } + while (R < 0) { + val = DIV_ROUND_CLOSEST(val, 10); + R++; + } + + return val; +} + +static u16 pmbus_data2reg_vid(struct pmbus_data *data, + struct pmbus_sensor *sensor, long val) +{ + val = clamp_val(val, 500, 1600); + + return 2 + DIV_ROUND_CLOSEST((1600 - val) * 100, 625); +} + +static u16 pmbus_data2reg(struct pmbus_data *data, + struct pmbus_sensor *sensor, long val) +{ + u16 regval; + + switch (data->info->format[sensor->class]) { + case direct: + regval = pmbus_data2reg_direct(data, sensor, val); + break; + case vid: + regval = pmbus_data2reg_vid(data, sensor, val); + break; + case linear: + default: + regval = pmbus_data2reg_linear(data, sensor, val); + break; + } + return regval; +} + +/* + * Return boolean calculated from converted data. + * <index> defines a status register index and mask. + * The mask is in the lower 8 bits, the register index is in bits 8..23. + * + * The associated pmbus_boolean structure contains optional pointers to two + * sensor attributes. If specified, those attributes are compared against each + * other to determine if a limit has been exceeded. + * + * If the sensor attribute pointers are NULL, the function returns true if + * (status[reg] & mask) is true. + * + * If sensor attribute pointers are provided, a comparison against a specified + * limit has to be performed to determine the boolean result. + * In this case, the function returns true if v1 >= v2 (where v1 and v2 are + * sensor values referenced by sensor attribute pointers s1 and s2). + * + * To determine if an object exceeds upper limits, specify <s1,s2> = <v,limit>. + * To determine if an object exceeds lower limits, specify <s1,s2> = <limit,v>. + * + * If a negative value is stored in any of the referenced registers, this value + * reflects an error code which will be returned. + */ +static int pmbus_get_boolean(struct pmbus_data *data, struct pmbus_boolean *b, + int index) +{ + struct pmbus_sensor *s1 = b->s1; + struct pmbus_sensor *s2 = b->s2; + u16 reg = (index >> 8) & 0xffff; + u8 mask = index & 0xff; + int ret, status; + u8 regval; + + status = data->status[reg]; + if (status < 0) + return status; + + regval = status & mask; + if (!s1 && !s2) { + ret = !!regval; + } else if (!s1 || !s2) { + WARN(1, "Bad boolean descriptor %p: s1=%p, s2=%p\n", b, s1, s2); + return 0; + } else { + long v1, v2; + + if (s1->data < 0) + return s1->data; + if (s2->data < 0) + return s2->data; + + v1 = pmbus_reg2data(data, s1); + v2 = pmbus_reg2data(data, s2); + ret = !!(regval && v1 >= v2); + } + return ret; +} + +static ssize_t pmbus_show_boolean(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct pmbus_boolean *boolean = to_pmbus_boolean(attr); + struct pmbus_data *data = pmbus_update_device(dev); + int val; + + val = pmbus_get_boolean(data, boolean, attr->index); + if (val < 0) + return val; + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t pmbus_show_sensor(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pmbus_data *data = pmbus_update_device(dev); + struct pmbus_sensor *sensor = to_pmbus_sensor(devattr); + + if (sensor->data < 0) + return sensor->data; + + return snprintf(buf, PAGE_SIZE, "%ld\n", pmbus_reg2data(data, sensor)); +} + +static ssize_t pmbus_set_sensor(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor *sensor = to_pmbus_sensor(devattr); + ssize_t rv = count; + long val = 0; + int ret; + u16 regval; + + if (kstrtol(buf, 10, &val) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + regval = pmbus_data2reg(data, sensor, val); + ret = _pmbus_write_word_data(client, sensor->page, sensor->reg, regval); + if (ret < 0) + rv = ret; + else + sensor->data = regval; + mutex_unlock(&data->update_lock); + return rv; +} + +static ssize_t pmbus_show_label(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct pmbus_label *label = to_pmbus_label(da); + + return snprintf(buf, PAGE_SIZE, "%s\n", label->label); +} + +static int pmbus_add_attribute(struct pmbus_data *data, struct attribute *attr) +{ + if (data->num_attributes >= data->max_attributes - 1) { + int new_max_attrs = data->max_attributes + PMBUS_ATTR_ALLOC_SIZE; + void *new_attrs = krealloc(data->group.attrs, + new_max_attrs * sizeof(void *), + GFP_KERNEL); + if (!new_attrs) + return -ENOMEM; + data->group.attrs = new_attrs; + data->max_attributes = new_max_attrs; + } + + data->group.attrs[data->num_attributes++] = attr; + data->group.attrs[data->num_attributes] = NULL; + return 0; +} + +static void pmbus_dev_attr_init(struct device_attribute *dev_attr, + const char *name, + umode_t mode, + ssize_t (*show)(struct device *dev, + struct device_attribute *attr, + char *buf), + ssize_t (*store)(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count)) +{ + sysfs_attr_init(&dev_attr->attr); + dev_attr->attr.name = name; + dev_attr->attr.mode = mode; + dev_attr->show = show; + dev_attr->store = store; +} + +static void pmbus_attr_init(struct sensor_device_attribute *a, + const char *name, + umode_t mode, + ssize_t (*show)(struct device *dev, + struct device_attribute *attr, + char *buf), + ssize_t (*store)(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count), + int idx) +{ + pmbus_dev_attr_init(&a->dev_attr, name, mode, show, store); + a->index = idx; +} + +static int pmbus_add_boolean(struct pmbus_data *data, + const char *name, const char *type, int seq, + struct pmbus_sensor *s1, + struct pmbus_sensor *s2, + u16 reg, u8 mask) +{ + struct pmbus_boolean *boolean; + struct sensor_device_attribute *a; + + boolean = devm_kzalloc(data->dev, sizeof(*boolean), GFP_KERNEL); + if (!boolean) + return -ENOMEM; + + a = &boolean->attribute; + + snprintf(boolean->name, sizeof(boolean->name), "%s%d_%s", + name, seq, type); + boolean->s1 = s1; + boolean->s2 = s2; + pmbus_attr_init(a, boolean->name, S_IRUGO, pmbus_show_boolean, NULL, + (reg << 8) | mask); + + return pmbus_add_attribute(data, &a->dev_attr.attr); +} + +static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data, + const char *name, const char *type, + int seq, int page, int reg, + enum pmbus_sensor_classes class, + bool update, bool readonly) +{ + struct pmbus_sensor *sensor; + struct device_attribute *a; + + sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return NULL; + a = &sensor->attribute; + + snprintf(sensor->name, sizeof(sensor->name), "%s%d_%s", + name, seq, type); + sensor->page = page; + sensor->reg = reg; + sensor->class = class; + sensor->update = update; + pmbus_dev_attr_init(a, sensor->name, + readonly ? S_IRUGO : S_IRUGO | S_IWUSR, + pmbus_show_sensor, pmbus_set_sensor); + + if (pmbus_add_attribute(data, &a->attr)) + return NULL; + + sensor->next = data->sensors; + data->sensors = sensor; + + return sensor; +} + +static int pmbus_add_label(struct pmbus_data *data, + const char *name, int seq, + const char *lstring, int index) +{ + struct pmbus_label *label; + struct device_attribute *a; + + label = devm_kzalloc(data->dev, sizeof(*label), GFP_KERNEL); + if (!label) + return -ENOMEM; + + a = &label->attribute; + + snprintf(label->name, sizeof(label->name), "%s%d_label", name, seq); + if (!index) + strncpy(label->label, lstring, sizeof(label->label) - 1); + else + snprintf(label->label, sizeof(label->label), "%s%d", lstring, + index); + + pmbus_dev_attr_init(a, label->name, S_IRUGO, pmbus_show_label, NULL); + return pmbus_add_attribute(data, &a->attr); +} + +/* + * Search for attributes. Allocate sensors, booleans, and labels as needed. + */ + +/* + * The pmbus_limit_attr structure describes a single limit attribute + * and its associated alarm attribute. + */ +struct pmbus_limit_attr { + u16 reg; /* Limit register */ + u16 sbit; /* Alarm attribute status bit */ + bool update; /* True if register needs updates */ + bool low; /* True if low limit; for limits with compare + functions only */ + const char *attr; /* Attribute name */ + const char *alarm; /* Alarm attribute name */ +}; + +/* + * The pmbus_sensor_attr structure describes one sensor attribute. This + * description includes a reference to the associated limit attributes. + */ +struct pmbus_sensor_attr { + u16 reg; /* sensor register */ + u8 gbit; /* generic status bit */ + u8 nlimit; /* # of limit registers */ + enum pmbus_sensor_classes class;/* sensor class */ + const char *label; /* sensor label */ + bool paged; /* true if paged sensor */ + bool update; /* true if update needed */ + bool compare; /* true if compare function needed */ + u32 func; /* sensor mask */ + u32 sfunc; /* sensor status mask */ + int sbase; /* status base register */ + const struct pmbus_limit_attr *limit;/* limit registers */ +}; + +/* + * Add a set of limit attributes and, if supported, the associated + * alarm attributes. + * returns 0 if no alarm register found, 1 if an alarm register was found, + * < 0 on errors. + */ +static int pmbus_add_limit_attrs(struct i2c_client *client, + struct pmbus_data *data, + const struct pmbus_driver_info *info, + const char *name, int index, int page, + struct pmbus_sensor *base, + const struct pmbus_sensor_attr *attr) +{ + const struct pmbus_limit_attr *l = attr->limit; + int nlimit = attr->nlimit; + int have_alarm = 0; + int i, ret; + struct pmbus_sensor *curr; + + for (i = 0; i < nlimit; i++) { + if (pmbus_check_word_register(client, page, l->reg)) { + curr = pmbus_add_sensor(data, name, l->attr, index, + page, l->reg, attr->class, + attr->update || l->update, + false); + if (!curr) + return -ENOMEM; + if (l->sbit && (info->func[page] & attr->sfunc)) { + ret = pmbus_add_boolean(data, name, + l->alarm, index, + attr->compare ? l->low ? curr : base + : NULL, + attr->compare ? l->low ? base : curr + : NULL, + attr->sbase + page, l->sbit); + if (ret) + return ret; + have_alarm = 1; + } + } + l++; + } + return have_alarm; +} + +static int pmbus_add_sensor_attrs_one(struct i2c_client *client, + struct pmbus_data *data, + const struct pmbus_driver_info *info, + const char *name, + int index, int page, + const struct pmbus_sensor_attr *attr) +{ + struct pmbus_sensor *base; + int ret; + + if (attr->label) { + ret = pmbus_add_label(data, name, index, attr->label, + attr->paged ? page + 1 : 0); + if (ret) + return ret; + } + base = pmbus_add_sensor(data, name, "input", index, page, attr->reg, + attr->class, true, true); + if (!base) + return -ENOMEM; + if (attr->sfunc) { + ret = pmbus_add_limit_attrs(client, data, info, name, + index, page, base, attr); + if (ret < 0) + return ret; + /* + * Add generic alarm attribute only if there are no individual + * alarm attributes, if there is a global alarm bit, and if + * the generic status register for this page is accessible. + */ + if (!ret && attr->gbit && + pmbus_check_byte_register(client, page, + data->status_register)) { + ret = pmbus_add_boolean(data, name, "alarm", index, + NULL, NULL, + PB_STATUS_BASE + page, + attr->gbit); + if (ret) + return ret; + } + } + return 0; +} + +static int pmbus_add_sensor_attrs(struct i2c_client *client, + struct pmbus_data *data, + const char *name, + const struct pmbus_sensor_attr *attrs, + int nattrs) +{ + const struct pmbus_driver_info *info = data->info; + int index, i; + int ret; + + index = 1; + for (i = 0; i < nattrs; i++) { + int page, pages; + + pages = attrs->paged ? info->pages : 1; + for (page = 0; page < pages; page++) { + if (!(info->func[page] & attrs->func)) + continue; + ret = pmbus_add_sensor_attrs_one(client, data, info, + name, index, page, + attrs); + if (ret) + return ret; + index++; + } + attrs++; + } + return 0; +} + +static const struct pmbus_limit_attr vin_limit_attrs[] = { + { + .reg = PMBUS_VIN_UV_WARN_LIMIT, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_VOLTAGE_UV_WARNING, + }, { + .reg = PMBUS_VIN_UV_FAULT_LIMIT, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_VOLTAGE_UV_FAULT, + }, { + .reg = PMBUS_VIN_OV_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_VOLTAGE_OV_WARNING, + }, { + .reg = PMBUS_VIN_OV_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_VOLTAGE_OV_FAULT, + }, { + .reg = PMBUS_VIRT_READ_VIN_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_VIN_MIN, + .update = true, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_VIN_MAX, + .update = true, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_VIN_HISTORY, + .attr = "reset_history", + }, +}; + +static const struct pmbus_limit_attr vmon_limit_attrs[] = { + { + .reg = PMBUS_VIRT_VMON_UV_WARN_LIMIT, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_VOLTAGE_UV_WARNING, + }, { + .reg = PMBUS_VIRT_VMON_UV_FAULT_LIMIT, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_VOLTAGE_UV_FAULT, + }, { + .reg = PMBUS_VIRT_VMON_OV_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_VOLTAGE_OV_WARNING, + }, { + .reg = PMBUS_VIRT_VMON_OV_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_VOLTAGE_OV_FAULT, + } +}; + +static const struct pmbus_limit_attr vout_limit_attrs[] = { + { + .reg = PMBUS_VOUT_UV_WARN_LIMIT, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_VOLTAGE_UV_WARNING, + }, { + .reg = PMBUS_VOUT_UV_FAULT_LIMIT, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_VOLTAGE_UV_FAULT, + }, { + .reg = PMBUS_VOUT_OV_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_VOLTAGE_OV_WARNING, + }, { + .reg = PMBUS_VOUT_OV_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_VOLTAGE_OV_FAULT, + }, { + .reg = PMBUS_VIRT_READ_VOUT_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_VOUT_MIN, + .update = true, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_VOUT_MAX, + .update = true, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_VOUT_HISTORY, + .attr = "reset_history", + } +}; + +static const struct pmbus_sensor_attr voltage_attributes[] = { + { + .reg = PMBUS_READ_VIN, + .class = PSC_VOLTAGE_IN, + .label = "vin", + .func = PMBUS_HAVE_VIN, + .sfunc = PMBUS_HAVE_STATUS_INPUT, + .sbase = PB_STATUS_INPUT_BASE, + .gbit = PB_STATUS_VIN_UV, + .limit = vin_limit_attrs, + .nlimit = ARRAY_SIZE(vin_limit_attrs), + }, { + .reg = PMBUS_VIRT_READ_VMON, + .class = PSC_VOLTAGE_IN, + .label = "vmon", + .func = PMBUS_HAVE_VMON, + .sfunc = PMBUS_HAVE_STATUS_VMON, + .sbase = PB_STATUS_VMON_BASE, + .limit = vmon_limit_attrs, + .nlimit = ARRAY_SIZE(vmon_limit_attrs), + }, { + .reg = PMBUS_READ_VCAP, + .class = PSC_VOLTAGE_IN, + .label = "vcap", + .func = PMBUS_HAVE_VCAP, + }, { + .reg = PMBUS_READ_VOUT, + .class = PSC_VOLTAGE_OUT, + .label = "vout", + .paged = true, + .func = PMBUS_HAVE_VOUT, + .sfunc = PMBUS_HAVE_STATUS_VOUT, + .sbase = PB_STATUS_VOUT_BASE, + .gbit = PB_STATUS_VOUT_OV, + .limit = vout_limit_attrs, + .nlimit = ARRAY_SIZE(vout_limit_attrs), + } +}; + +/* Current attributes */ + +static const struct pmbus_limit_attr iin_limit_attrs[] = { + { + .reg = PMBUS_IIN_OC_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_IIN_OC_WARNING, + }, { + .reg = PMBUS_IIN_OC_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_IIN_OC_FAULT, + }, { + .reg = PMBUS_VIRT_READ_IIN_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_IIN_MIN, + .update = true, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_IIN_MAX, + .update = true, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_IIN_HISTORY, + .attr = "reset_history", + } +}; + +static const struct pmbus_limit_attr iout_limit_attrs[] = { + { + .reg = PMBUS_IOUT_OC_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_IOUT_OC_WARNING, + }, { + .reg = PMBUS_IOUT_UC_FAULT_LIMIT, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_IOUT_UC_FAULT, + }, { + .reg = PMBUS_IOUT_OC_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_IOUT_OC_FAULT, + }, { + .reg = PMBUS_VIRT_READ_IOUT_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_IOUT_MIN, + .update = true, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_IOUT_MAX, + .update = true, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_IOUT_HISTORY, + .attr = "reset_history", + } +}; + +static const struct pmbus_sensor_attr current_attributes[] = { + { + .reg = PMBUS_READ_IIN, + .class = PSC_CURRENT_IN, + .label = "iin", + .func = PMBUS_HAVE_IIN, + .sfunc = PMBUS_HAVE_STATUS_INPUT, + .sbase = PB_STATUS_INPUT_BASE, + .limit = iin_limit_attrs, + .nlimit = ARRAY_SIZE(iin_limit_attrs), + }, { + .reg = PMBUS_READ_IOUT, + .class = PSC_CURRENT_OUT, + .label = "iout", + .paged = true, + .func = PMBUS_HAVE_IOUT, + .sfunc = PMBUS_HAVE_STATUS_IOUT, + .sbase = PB_STATUS_IOUT_BASE, + .gbit = PB_STATUS_IOUT_OC, + .limit = iout_limit_attrs, + .nlimit = ARRAY_SIZE(iout_limit_attrs), + } +}; + +/* Power attributes */ + +static const struct pmbus_limit_attr pin_limit_attrs[] = { + { + .reg = PMBUS_PIN_OP_WARN_LIMIT, + .attr = "max", + .alarm = "alarm", + .sbit = PB_PIN_OP_WARNING, + }, { + .reg = PMBUS_VIRT_READ_PIN_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_PIN_MAX, + .update = true, + .attr = "input_highest", + }, { + .reg = PMBUS_VIRT_RESET_PIN_HISTORY, + .attr = "reset_history", + } +}; + +static const struct pmbus_limit_attr pout_limit_attrs[] = { + { + .reg = PMBUS_POUT_MAX, + .attr = "cap", + .alarm = "cap_alarm", + .sbit = PB_POWER_LIMITING, + }, { + .reg = PMBUS_POUT_OP_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_POUT_OP_WARNING, + }, { + .reg = PMBUS_POUT_OP_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_POUT_OP_FAULT, + }, { + .reg = PMBUS_VIRT_READ_POUT_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_POUT_MAX, + .update = true, + .attr = "input_highest", + }, { + .reg = PMBUS_VIRT_RESET_POUT_HISTORY, + .attr = "reset_history", + } +}; + +static const struct pmbus_sensor_attr power_attributes[] = { + { + .reg = PMBUS_READ_PIN, + .class = PSC_POWER, + .label = "pin", + .func = PMBUS_HAVE_PIN, + .sfunc = PMBUS_HAVE_STATUS_INPUT, + .sbase = PB_STATUS_INPUT_BASE, + .limit = pin_limit_attrs, + .nlimit = ARRAY_SIZE(pin_limit_attrs), + }, { + .reg = PMBUS_READ_POUT, + .class = PSC_POWER, + .label = "pout", + .paged = true, + .func = PMBUS_HAVE_POUT, + .sfunc = PMBUS_HAVE_STATUS_IOUT, + .sbase = PB_STATUS_IOUT_BASE, + .limit = pout_limit_attrs, + .nlimit = ARRAY_SIZE(pout_limit_attrs), + } +}; + +/* Temperature atributes */ + +static const struct pmbus_limit_attr temp_limit_attrs[] = { + { + .reg = PMBUS_UT_WARN_LIMIT, + .low = true, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_TEMP_UT_WARNING, + }, { + .reg = PMBUS_UT_FAULT_LIMIT, + .low = true, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_TEMP_UT_FAULT, + }, { + .reg = PMBUS_OT_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_TEMP_OT_WARNING, + }, { + .reg = PMBUS_OT_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_TEMP_OT_FAULT, + }, { + .reg = PMBUS_VIRT_READ_TEMP_MIN, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_TEMP_AVG, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_TEMP_MAX, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_TEMP_HISTORY, + .attr = "reset_history", + } +}; + +static const struct pmbus_limit_attr temp_limit_attrs2[] = { + { + .reg = PMBUS_UT_WARN_LIMIT, + .low = true, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_TEMP_UT_WARNING, + }, { + .reg = PMBUS_UT_FAULT_LIMIT, + .low = true, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_TEMP_UT_FAULT, + }, { + .reg = PMBUS_OT_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_TEMP_OT_WARNING, + }, { + .reg = PMBUS_OT_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_TEMP_OT_FAULT, + }, { + .reg = PMBUS_VIRT_READ_TEMP2_MIN, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_TEMP2_AVG, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_TEMP2_MAX, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_TEMP2_HISTORY, + .attr = "reset_history", + } +}; + +static const struct pmbus_limit_attr temp_limit_attrs3[] = { + { + .reg = PMBUS_UT_WARN_LIMIT, + .low = true, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_TEMP_UT_WARNING, + }, { + .reg = PMBUS_UT_FAULT_LIMIT, + .low = true, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_TEMP_UT_FAULT, + }, { + .reg = PMBUS_OT_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_TEMP_OT_WARNING, + }, { + .reg = PMBUS_OT_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_TEMP_OT_FAULT, + } +}; + +static const struct pmbus_sensor_attr temp_attributes[] = { + { + .reg = PMBUS_READ_TEMPERATURE_1, + .class = PSC_TEMPERATURE, + .paged = true, + .update = true, + .compare = true, + .func = PMBUS_HAVE_TEMP, + .sfunc = PMBUS_HAVE_STATUS_TEMP, + .sbase = PB_STATUS_TEMP_BASE, + .gbit = PB_STATUS_TEMPERATURE, + .limit = temp_limit_attrs, + .nlimit = ARRAY_SIZE(temp_limit_attrs), + }, { + .reg = PMBUS_READ_TEMPERATURE_2, + .class = PSC_TEMPERATURE, + .paged = true, + .update = true, + .compare = true, + .func = PMBUS_HAVE_TEMP2, + .sfunc = PMBUS_HAVE_STATUS_TEMP, + .sbase = PB_STATUS_TEMP_BASE, + .gbit = PB_STATUS_TEMPERATURE, + .limit = temp_limit_attrs2, + .nlimit = ARRAY_SIZE(temp_limit_attrs2), + }, { + .reg = PMBUS_READ_TEMPERATURE_3, + .class = PSC_TEMPERATURE, + .paged = true, + .update = true, + .compare = true, + .func = PMBUS_HAVE_TEMP3, + .sfunc = PMBUS_HAVE_STATUS_TEMP, + .sbase = PB_STATUS_TEMP_BASE, + .gbit = PB_STATUS_TEMPERATURE, + .limit = temp_limit_attrs3, + .nlimit = ARRAY_SIZE(temp_limit_attrs3), + } +}; + +static const int pmbus_fan_registers[] = { + PMBUS_READ_FAN_SPEED_1, + PMBUS_READ_FAN_SPEED_2, + PMBUS_READ_FAN_SPEED_3, + PMBUS_READ_FAN_SPEED_4 +}; + +static const int pmbus_fan_config_registers[] = { + PMBUS_FAN_CONFIG_12, + PMBUS_FAN_CONFIG_12, + PMBUS_FAN_CONFIG_34, + PMBUS_FAN_CONFIG_34 +}; + +static const int pmbus_fan_status_registers[] = { + PMBUS_STATUS_FAN_12, + PMBUS_STATUS_FAN_12, + PMBUS_STATUS_FAN_34, + PMBUS_STATUS_FAN_34 +}; + +static const u32 pmbus_fan_flags[] = { + PMBUS_HAVE_FAN12, + PMBUS_HAVE_FAN12, + PMBUS_HAVE_FAN34, + PMBUS_HAVE_FAN34 +}; + +static const u32 pmbus_fan_status_flags[] = { + PMBUS_HAVE_STATUS_FAN12, + PMBUS_HAVE_STATUS_FAN12, + PMBUS_HAVE_STATUS_FAN34, + PMBUS_HAVE_STATUS_FAN34 +}; + +/* Fans */ +static int pmbus_add_fan_attributes(struct i2c_client *client, + struct pmbus_data *data) +{ + const struct pmbus_driver_info *info = data->info; + int index = 1; + int page; + int ret; + + for (page = 0; page < info->pages; page++) { + int f; + + for (f = 0; f < ARRAY_SIZE(pmbus_fan_registers); f++) { + int regval; + + if (!(info->func[page] & pmbus_fan_flags[f])) + break; + + if (!pmbus_check_word_register(client, page, + pmbus_fan_registers[f])) + break; + + /* + * Skip fan if not installed. + * Each fan configuration register covers multiple fans, + * so we have to do some magic. + */ + regval = _pmbus_read_byte_data(client, page, + pmbus_fan_config_registers[f]); + if (regval < 0 || + (!(regval & (PB_FAN_1_INSTALLED >> ((f & 1) * 4))))) + continue; + + if (pmbus_add_sensor(data, "fan", "input", index, + page, pmbus_fan_registers[f], + PSC_FAN, true, true) == NULL) + return -ENOMEM; + + /* + * Each fan status register covers multiple fans, + * so we have to do some magic. + */ + if ((info->func[page] & pmbus_fan_status_flags[f]) && + pmbus_check_byte_register(client, + page, pmbus_fan_status_registers[f])) { + int base; + + if (f > 1) /* fan 3, 4 */ + base = PB_STATUS_FAN34_BASE + page; + else + base = PB_STATUS_FAN_BASE + page; + ret = pmbus_add_boolean(data, "fan", + "alarm", index, NULL, NULL, base, + PB_FAN_FAN1_WARNING >> (f & 1)); + if (ret) + return ret; + ret = pmbus_add_boolean(data, "fan", + "fault", index, NULL, NULL, base, + PB_FAN_FAN1_FAULT >> (f & 1)); + if (ret) + return ret; + } + index++; + } + } + return 0; +} + +static int pmbus_find_attributes(struct i2c_client *client, + struct pmbus_data *data) +{ + int ret; + + /* Voltage sensors */ + ret = pmbus_add_sensor_attrs(client, data, "in", voltage_attributes, + ARRAY_SIZE(voltage_attributes)); + if (ret) + return ret; + + /* Current sensors */ + ret = pmbus_add_sensor_attrs(client, data, "curr", current_attributes, + ARRAY_SIZE(current_attributes)); + if (ret) + return ret; + + /* Power sensors */ + ret = pmbus_add_sensor_attrs(client, data, "power", power_attributes, + ARRAY_SIZE(power_attributes)); + if (ret) + return ret; + + /* Temperature sensors */ + ret = pmbus_add_sensor_attrs(client, data, "temp", temp_attributes, + ARRAY_SIZE(temp_attributes)); + if (ret) + return ret; + + /* Fans */ + ret = pmbus_add_fan_attributes(client, data); + return ret; +} + +/* + * Identify chip parameters. + * This function is called for all chips. + */ +static int pmbus_identify_common(struct i2c_client *client, + struct pmbus_data *data, int page) +{ + int vout_mode = -1; + + if (pmbus_check_byte_register(client, page, PMBUS_VOUT_MODE)) + vout_mode = _pmbus_read_byte_data(client, page, + PMBUS_VOUT_MODE); + if (vout_mode >= 0 && vout_mode != 0xff) { + /* + * Not all chips support the VOUT_MODE command, + * so a failure to read it is not an error. + */ + switch (vout_mode >> 5) { + case 0: /* linear mode */ + if (data->info->format[PSC_VOLTAGE_OUT] != linear) + return -ENODEV; + + data->exponent[page] = ((s8)(vout_mode << 3)) >> 3; + break; + case 1: /* VID mode */ + if (data->info->format[PSC_VOLTAGE_OUT] != vid) + return -ENODEV; + break; + case 2: /* direct mode */ + if (data->info->format[PSC_VOLTAGE_OUT] != direct) + return -ENODEV; + break; + default: + return -ENODEV; + } + } + + pmbus_clear_fault_page(client, page); + return 0; +} + +static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, + struct pmbus_driver_info *info) +{ + struct device *dev = &client->dev; + int page, ret; + + /* + * Some PMBus chips don't support PMBUS_STATUS_BYTE, so try + * to use PMBUS_STATUS_WORD instead if that is the case. + * Bail out if both registers are not supported. + */ + data->status_register = PMBUS_STATUS_BYTE; + ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE); + if (ret < 0 || ret == 0xff) { + data->status_register = PMBUS_STATUS_WORD; + ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD); + if (ret < 0 || ret == 0xffff) { + dev_err(dev, "PMBus status register not found\n"); + return -ENODEV; + } + } + + pmbus_clear_faults(client); + + if (info->identify) { + ret = (*info->identify)(client, info); + if (ret < 0) { + dev_err(dev, "Chip identification failed\n"); + return ret; + } + } + + if (info->pages <= 0 || info->pages > PMBUS_PAGES) { + dev_err(dev, "Bad number of PMBus pages: %d\n", info->pages); + return -ENODEV; + } + + for (page = 0; page < info->pages; page++) { + ret = pmbus_identify_common(client, data, page); + if (ret < 0) { + dev_err(dev, "Failed to identify chip capabilities\n"); + return ret; + } + } + return 0; +} + +int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, + struct pmbus_driver_info *info) +{ + struct device *dev = &client->dev; + const struct pmbus_platform_data *pdata = dev_get_platdata(dev); + struct pmbus_data *data; + int ret; + + if (!info) + return -ENODEV; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE + | I2C_FUNC_SMBUS_BYTE_DATA + | I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + data->dev = dev; + + if (pdata) + data->flags = pdata->flags; + data->info = info; + + ret = pmbus_init_common(client, data, info); + if (ret < 0) + return ret; + + ret = pmbus_find_attributes(client, data); + if (ret) + goto out_kfree; + + /* + * If there are no attributes, something is wrong. + * Bail out instead of trying to register nothing. + */ + if (!data->num_attributes) { + dev_err(dev, "No attributes found\n"); + ret = -ENODEV; + goto out_kfree; + } + + data->groups[0] = &data->group; + data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name, + data, data->groups); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + dev_err(dev, "Failed to register hwmon device\n"); + goto out_kfree; + } + return 0; + +out_kfree: + kfree(data->group.attrs); + return ret; +} +EXPORT_SYMBOL_GPL(pmbus_do_probe); + +int pmbus_do_remove(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + hwmon_device_unregister(data->hwmon_dev); + kfree(data->group.attrs); + return 0; +} +EXPORT_SYMBOL_GPL(pmbus_do_remove); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("PMBus core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c new file mode 100644 index 00000000000..fbb1479d3ad --- /dev/null +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -0,0 +1,246 @@ +/* + * Hardware monitoring driver for UCD90xxx Sequencer and System Health + * Controller series + * + * Copyright (C) 2011 Ericsson AB. + * + * 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/i2c/pmbus.h> +#include "pmbus.h" + +enum chips { ucd9000, ucd90120, ucd90124, ucd9090, ucd90910 }; + +#define UCD9000_MONITOR_CONFIG 0xd5 +#define UCD9000_NUM_PAGES 0xd6 +#define UCD9000_FAN_CONFIG_INDEX 0xe7 +#define UCD9000_FAN_CONFIG 0xe8 +#define UCD9000_DEVICE_ID 0xfd + +#define UCD9000_MON_TYPE(x) (((x) >> 5) & 0x07) +#define UCD9000_MON_PAGE(x) ((x) & 0x0f) + +#define UCD9000_MON_VOLTAGE 1 +#define UCD9000_MON_TEMPERATURE 2 +#define UCD9000_MON_CURRENT 3 +#define UCD9000_MON_VOLTAGE_HW 4 + +#define UCD9000_NUM_FAN 4 + +struct ucd9000_data { + u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX]; + struct pmbus_driver_info info; +}; +#define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info) + +static int ucd9000_get_fan_config(struct i2c_client *client, int fan) +{ + int fan_config = 0; + struct ucd9000_data *data + = to_ucd9000_data(pmbus_get_driver_info(client)); + + if (data->fan_data[fan][3] & 1) + fan_config |= PB_FAN_2_INSTALLED; /* Use lower bit position */ + + /* Pulses/revolution */ + fan_config |= (data->fan_data[fan][3] & 0x06) >> 1; + + return fan_config; +} + +static int ucd9000_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret = 0; + int fan_config; + + switch (reg) { + case PMBUS_FAN_CONFIG_12: + if (page > 0) + return -ENXIO; + + ret = ucd9000_get_fan_config(client, 0); + if (ret < 0) + return ret; + fan_config = ret << 4; + ret = ucd9000_get_fan_config(client, 1); + if (ret < 0) + return ret; + fan_config |= ret; + ret = fan_config; + break; + case PMBUS_FAN_CONFIG_34: + if (page > 0) + return -ENXIO; + + ret = ucd9000_get_fan_config(client, 2); + if (ret < 0) + return ret; + fan_config = ret << 4; + ret = ucd9000_get_fan_config(client, 3); + if (ret < 0) + return ret; + fan_config |= ret; + ret = fan_config; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static const struct i2c_device_id ucd9000_id[] = { + {"ucd9000", ucd9000}, + {"ucd90120", ucd90120}, + {"ucd90124", ucd90124}, + {"ucd9090", ucd9090}, + {"ucd90910", ucd90910}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ucd9000_id); + +static int ucd9000_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1]; + struct ucd9000_data *data; + struct pmbus_driver_info *info; + const struct i2c_device_id *mid; + int i, ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA)) + return -ENODEV; + + ret = i2c_smbus_read_block_data(client, UCD9000_DEVICE_ID, + block_buffer); + if (ret < 0) { + dev_err(&client->dev, "Failed to read device ID\n"); + return ret; + } + block_buffer[ret] = '\0'; + dev_info(&client->dev, "Device ID %s\n", block_buffer); + + for (mid = ucd9000_id; mid->name[0]; mid++) { + if (!strncasecmp(mid->name, block_buffer, strlen(mid->name))) + break; + } + if (!mid->name[0]) { + dev_err(&client->dev, "Unsupported device\n"); + return -ENODEV; + } + + if (id->driver_data != ucd9000 && id->driver_data != mid->driver_data) + dev_notice(&client->dev, + "Device mismatch: Configured %s, detected %s\n", + id->name, mid->name); + + data = devm_kzalloc(&client->dev, sizeof(struct ucd9000_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + info = &data->info; + + ret = i2c_smbus_read_byte_data(client, UCD9000_NUM_PAGES); + if (ret < 0) { + dev_err(&client->dev, + "Failed to read number of active pages\n"); + return ret; + } + info->pages = ret; + if (!info->pages) { + dev_err(&client->dev, "No pages configured\n"); + return -ENODEV; + } + + /* The internal temperature sensor is always active */ + info->func[0] = PMBUS_HAVE_TEMP; + + /* Everything else is configurable */ + ret = i2c_smbus_read_block_data(client, UCD9000_MONITOR_CONFIG, + block_buffer); + if (ret <= 0) { + dev_err(&client->dev, "Failed to read configuration data\n"); + return -ENODEV; + } + for (i = 0; i < ret; i++) { + int page = UCD9000_MON_PAGE(block_buffer[i]); + + if (page >= info->pages) + continue; + + switch (UCD9000_MON_TYPE(block_buffer[i])) { + case UCD9000_MON_VOLTAGE: + case UCD9000_MON_VOLTAGE_HW: + info->func[page] |= PMBUS_HAVE_VOUT + | PMBUS_HAVE_STATUS_VOUT; + break; + case UCD9000_MON_TEMPERATURE: + info->func[page] |= PMBUS_HAVE_TEMP2 + | PMBUS_HAVE_STATUS_TEMP; + break; + case UCD9000_MON_CURRENT: + info->func[page] |= PMBUS_HAVE_IOUT + | PMBUS_HAVE_STATUS_IOUT; + break; + default: + break; + } + } + + /* Fan configuration */ + if (mid->driver_data == ucd90124) { + for (i = 0; i < UCD9000_NUM_FAN; i++) { + i2c_smbus_write_byte_data(client, + UCD9000_FAN_CONFIG_INDEX, i); + ret = i2c_smbus_read_block_data(client, + UCD9000_FAN_CONFIG, + data->fan_data[i]); + if (ret < 0) + return ret; + } + i2c_smbus_write_byte_data(client, UCD9000_FAN_CONFIG_INDEX, 0); + + info->read_byte_data = ucd9000_read_byte_data; + info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 + | PMBUS_HAVE_FAN34 | PMBUS_HAVE_STATUS_FAN34; + } + + return pmbus_do_probe(client, mid, info); +} + +/* This is the driver that will be inserted */ +static struct i2c_driver ucd9000_driver = { + .driver = { + .name = "ucd9000", + }, + .probe = ucd9000_probe, + .remove = pmbus_do_remove, + .id_table = ucd9000_id, +}; + +module_i2c_driver(ucd9000_driver); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("PMBus driver for TI UCD90xxx"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/ucd9200.c b/drivers/hwmon/pmbus/ucd9200.c new file mode 100644 index 00000000000..033d6aca47d --- /dev/null +++ b/drivers/hwmon/pmbus/ucd9200.c @@ -0,0 +1,180 @@ +/* + * Hardware monitoring driver for ucd9200 series Digital PWM System Controllers + * + * Copyright (C) 2011 Ericsson AB. + * + * 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/i2c/pmbus.h> +#include "pmbus.h" + +#define UCD9200_PHASE_INFO 0xd2 +#define UCD9200_DEVICE_ID 0xfd + +enum chips { ucd9200, ucd9220, ucd9222, ucd9224, ucd9240, ucd9244, ucd9246, + ucd9248 }; + +static const struct i2c_device_id ucd9200_id[] = { + {"ucd9200", ucd9200}, + {"ucd9220", ucd9220}, + {"ucd9222", ucd9222}, + {"ucd9224", ucd9224}, + {"ucd9240", ucd9240}, + {"ucd9244", ucd9244}, + {"ucd9246", ucd9246}, + {"ucd9248", ucd9248}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ucd9200_id); + +static int ucd9200_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1]; + struct pmbus_driver_info *info; + const struct i2c_device_id *mid; + int i, j, ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA)) + return -ENODEV; + + ret = i2c_smbus_read_block_data(client, UCD9200_DEVICE_ID, + block_buffer); + if (ret < 0) { + dev_err(&client->dev, "Failed to read device ID\n"); + return ret; + } + block_buffer[ret] = '\0'; + dev_info(&client->dev, "Device ID %s\n", block_buffer); + + for (mid = ucd9200_id; mid->name[0]; mid++) { + if (!strncasecmp(mid->name, block_buffer, strlen(mid->name))) + break; + } + if (!mid->name[0]) { + dev_err(&client->dev, "Unsupported device\n"); + return -ENODEV; + } + if (id->driver_data != ucd9200 && id->driver_data != mid->driver_data) + dev_notice(&client->dev, + "Device mismatch: Configured %s, detected %s\n", + id->name, mid->name); + + info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + ret = i2c_smbus_read_block_data(client, UCD9200_PHASE_INFO, + block_buffer); + if (ret < 0) { + dev_err(&client->dev, "Failed to read phase information\n"); + return ret; + } + + /* + * Calculate number of configured pages (rails) from PHASE_INFO + * register. + * Rails have to be sequential, so we can abort after finding + * the first unconfigured rail. + */ + info->pages = 0; + for (i = 0; i < ret; i++) { + if (!block_buffer[i]) + break; + info->pages++; + } + if (!info->pages) { + dev_err(&client->dev, "No rails configured\n"); + return -ENODEV; + } + dev_info(&client->dev, "%d rails configured\n", info->pages); + + /* + * Set PHASE registers on all pages to 0xff to ensure that phase + * specific commands will apply to all phases of a given page (rail). + * This only affects the READ_IOUT and READ_TEMPERATURE2 registers. + * READ_IOUT will return the sum of currents of all phases of a rail, + * and READ_TEMPERATURE2 will return the maximum temperature detected + * for the the phases of the rail. + */ + for (i = 0; i < info->pages; i++) { + /* + * Setting PAGE & PHASE fails once in a while for no obvious + * reason, so we need to retry a couple of times. + */ + for (j = 0; j < 3; j++) { + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i); + if (ret < 0) + continue; + ret = i2c_smbus_write_byte_data(client, PMBUS_PHASE, + 0xff); + if (ret < 0) + continue; + break; + } + if (ret < 0) { + dev_err(&client->dev, + "Failed to initialize PHASE registers\n"); + return ret; + } + } + if (info->pages > 1) + i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | + PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP; + + for (i = 1; i < info->pages; i++) + info->func[i] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_POUT | + PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP; + + /* ucd9240 supports a single fan */ + if (mid->driver_data == ucd9240) + info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12; + + return pmbus_do_probe(client, mid, info); +} + +/* This is the driver that will be inserted */ +static struct i2c_driver ucd9200_driver = { + .driver = { + .name = "ucd9200", + }, + .probe = ucd9200_probe, + .remove = pmbus_do_remove, + .id_table = ucd9200_id, +}; + +module_i2c_driver(ucd9200_driver); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("PMBus driver for TI UCD922x, UCD924x"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/zl6100.c b/drivers/hwmon/pmbus/zl6100.c new file mode 100644 index 00000000000..81964412125 --- /dev/null +++ b/drivers/hwmon/pmbus/zl6100.c @@ -0,0 +1,419 @@ +/* + * Hardware monitoring driver for ZL6100 and compatibles + * + * Copyright (c) 2011 Ericsson AB. + * Copyright (c) 2012 Guenter Roeck + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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/ktime.h> +#include <linux/delay.h> +#include "pmbus.h" + +enum chips { zl2004, zl2005, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105, + zl9101, zl9117 }; + +struct zl6100_data { + int id; + ktime_t access; /* chip access time */ + int delay; /* Delay between chip accesses in uS */ + struct pmbus_driver_info info; +}; + +#define to_zl6100_data(x) container_of(x, struct zl6100_data, info) + +#define ZL6100_MFR_CONFIG 0xd0 +#define ZL6100_DEVICE_ID 0xe4 + +#define ZL6100_MFR_XTEMP_ENABLE (1 << 7) + +#define MFR_VMON_OV_FAULT_LIMIT 0xf5 +#define MFR_VMON_UV_FAULT_LIMIT 0xf6 +#define MFR_READ_VMON 0xf7 + +#define VMON_UV_WARNING (1 << 5) +#define VMON_OV_WARNING (1 << 4) +#define VMON_UV_FAULT (1 << 1) +#define VMON_OV_FAULT (1 << 0) + +#define ZL6100_WAIT_TIME 1000 /* uS */ + +static ushort delay = ZL6100_WAIT_TIME; +module_param(delay, ushort, 0644); +MODULE_PARM_DESC(delay, "Delay between chip accesses in uS"); + +/* Convert linear sensor value to milli-units */ +static long zl6100_l2d(s16 l) +{ + s16 exponent; + s32 mantissa; + long val; + + exponent = l >> 11; + mantissa = ((s16)((l & 0x7ff) << 5)) >> 5; + + val = mantissa; + + /* scale result to milli-units */ + val = val * 1000L; + + if (exponent >= 0) + val <<= exponent; + else + val >>= -exponent; + + return val; +} + +#define MAX_MANTISSA (1023 * 1000) +#define MIN_MANTISSA (511 * 1000) + +static u16 zl6100_d2l(long val) +{ + s16 exponent = 0, mantissa; + bool negative = false; + + /* simple case */ + if (val == 0) + return 0; + + if (val < 0) { + negative = true; + val = -val; + } + + /* Reduce large mantissa until it fits into 10 bit */ + while (val >= MAX_MANTISSA && exponent < 15) { + exponent++; + val >>= 1; + } + /* Increase small mantissa to improve precision */ + while (val < MIN_MANTISSA && exponent > -15) { + exponent--; + val <<= 1; + } + + /* Convert mantissa from milli-units to units */ + mantissa = DIV_ROUND_CLOSEST(val, 1000); + + /* Ensure that resulting number is within range */ + if (mantissa > 0x3ff) + mantissa = 0x3ff; + + /* restore sign */ + if (negative) + mantissa = -mantissa; + + /* Convert to 5 bit exponent, 11 bit mantissa */ + return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800); +} + +/* Some chips need a delay between accesses */ +static inline void zl6100_wait(const struct zl6100_data *data) +{ + if (data->delay) { + s64 delta = ktime_us_delta(ktime_get(), data->access); + if (delta < data->delay) + udelay(data->delay - delta); + } +} + +static int zl6100_read_word_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct zl6100_data *data = to_zl6100_data(info); + int ret, vreg; + + if (page > 0) + return -ENXIO; + + if (data->id == zl2005) { + /* + * Limit register detection is not reliable on ZL2005. + * Make sure registers are not erroneously detected. + */ + switch (reg) { + case PMBUS_VOUT_OV_WARN_LIMIT: + case PMBUS_VOUT_UV_WARN_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + return -ENXIO; + } + } + + switch (reg) { + case PMBUS_VIRT_READ_VMON: + vreg = MFR_READ_VMON; + break; + case PMBUS_VIRT_VMON_OV_WARN_LIMIT: + case PMBUS_VIRT_VMON_OV_FAULT_LIMIT: + vreg = MFR_VMON_OV_FAULT_LIMIT; + break; + case PMBUS_VIRT_VMON_UV_WARN_LIMIT: + case PMBUS_VIRT_VMON_UV_FAULT_LIMIT: + vreg = MFR_VMON_UV_FAULT_LIMIT; + break; + default: + if (reg >= PMBUS_VIRT_BASE) + return -ENXIO; + vreg = reg; + break; + } + + zl6100_wait(data); + ret = pmbus_read_word_data(client, page, vreg); + data->access = ktime_get(); + if (ret < 0) + return ret; + + switch (reg) { + case PMBUS_VIRT_VMON_OV_WARN_LIMIT: + ret = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(ret) * 9, 10)); + break; + case PMBUS_VIRT_VMON_UV_WARN_LIMIT: + ret = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(ret) * 11, 10)); + break; + } + + return ret; +} + +static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct zl6100_data *data = to_zl6100_data(info); + int ret, status; + + if (page > 0) + return -ENXIO; + + zl6100_wait(data); + + switch (reg) { + case PMBUS_VIRT_STATUS_VMON: + ret = pmbus_read_byte_data(client, 0, + PMBUS_STATUS_MFR_SPECIFIC); + if (ret < 0) + break; + + status = 0; + if (ret & VMON_UV_WARNING) + status |= PB_VOLTAGE_UV_WARNING; + if (ret & VMON_OV_WARNING) + status |= PB_VOLTAGE_OV_WARNING; + if (ret & VMON_UV_FAULT) + status |= PB_VOLTAGE_UV_FAULT; + if (ret & VMON_OV_FAULT) + status |= PB_VOLTAGE_OV_FAULT; + ret = status; + break; + default: + ret = pmbus_read_byte_data(client, page, reg); + break; + } + data->access = ktime_get(); + + return ret; +} + +static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct zl6100_data *data = to_zl6100_data(info); + int ret, vreg; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_VIRT_VMON_OV_WARN_LIMIT: + word = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(word) * 10, 9)); + vreg = MFR_VMON_OV_FAULT_LIMIT; + pmbus_clear_cache(client); + break; + case PMBUS_VIRT_VMON_OV_FAULT_LIMIT: + vreg = MFR_VMON_OV_FAULT_LIMIT; + pmbus_clear_cache(client); + break; + case PMBUS_VIRT_VMON_UV_WARN_LIMIT: + word = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(word) * 10, 11)); + vreg = MFR_VMON_UV_FAULT_LIMIT; + pmbus_clear_cache(client); + break; + case PMBUS_VIRT_VMON_UV_FAULT_LIMIT: + vreg = MFR_VMON_UV_FAULT_LIMIT; + pmbus_clear_cache(client); + break; + default: + if (reg >= PMBUS_VIRT_BASE) + return -ENXIO; + vreg = reg; + } + + zl6100_wait(data); + ret = pmbus_write_word_data(client, page, vreg, word); + data->access = ktime_get(); + + return ret; +} + +static int zl6100_write_byte(struct i2c_client *client, int page, u8 value) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct zl6100_data *data = to_zl6100_data(info); + int ret; + + if (page > 0) + return -ENXIO; + + zl6100_wait(data); + ret = pmbus_write_byte(client, page, value); + data->access = ktime_get(); + + return ret; +} + +static const struct i2c_device_id zl6100_id[] = { + {"bmr450", zl2005}, + {"bmr451", zl2005}, + {"bmr462", zl2008}, + {"bmr463", zl2008}, + {"bmr464", zl2008}, + {"zl2004", zl2004}, + {"zl2005", zl2005}, + {"zl2006", zl2006}, + {"zl2008", zl2008}, + {"zl2105", zl2105}, + {"zl2106", zl2106}, + {"zl6100", zl6100}, + {"zl6105", zl6105}, + {"zl9101", zl9101}, + {"zl9117", zl9117}, + { } +}; +MODULE_DEVICE_TABLE(i2c, zl6100_id); + +static int zl6100_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct zl6100_data *data; + struct pmbus_driver_info *info; + u8 device_id[I2C_SMBUS_BLOCK_MAX + 1]; + const struct i2c_device_id *mid; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA + | I2C_FUNC_SMBUS_READ_BLOCK_DATA)) + return -ENODEV; + + ret = i2c_smbus_read_block_data(client, ZL6100_DEVICE_ID, + device_id); + if (ret < 0) { + dev_err(&client->dev, "Failed to read device ID\n"); + return ret; + } + device_id[ret] = '\0'; + dev_info(&client->dev, "Device ID %s\n", device_id); + + mid = NULL; + for (mid = zl6100_id; mid->name[0]; mid++) { + if (!strncasecmp(mid->name, device_id, strlen(mid->name))) + break; + } + if (!mid->name[0]) { + dev_err(&client->dev, "Unsupported device\n"); + return -ENODEV; + } + if (id->driver_data != mid->driver_data) + dev_notice(&client->dev, + "Device mismatch: Configured %s, detected %s\n", + id->name, mid->name); + + data = devm_kzalloc(&client->dev, sizeof(struct zl6100_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->id = mid->driver_data; + + /* + * According to information from the chip vendor, all currently + * supported chips are known to require a wait time between I2C + * accesses. + */ + data->delay = delay; + + /* + * Since there was a direct I2C device access above, wait before + * accessing the chip again. + */ + data->access = ktime_get(); + zl6100_wait(data); + + info = &data->info; + + info->pages = 1; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + + /* + * ZL2004, ZL9101M, and ZL9117M support monitoring an extra voltage + * (VMON for ZL2004, VDRV for ZL9101M and ZL9117M). Report it as vmon. + */ + if (data->id == zl2004 || data->id == zl9101 || data->id == zl9117) + info->func[0] |= PMBUS_HAVE_VMON | PMBUS_HAVE_STATUS_VMON; + + ret = i2c_smbus_read_word_data(client, ZL6100_MFR_CONFIG); + if (ret < 0) + return ret; + + if (ret & ZL6100_MFR_XTEMP_ENABLE) + info->func[0] |= PMBUS_HAVE_TEMP2; + + data->access = ktime_get(); + zl6100_wait(data); + + info->read_word_data = zl6100_read_word_data; + info->read_byte_data = zl6100_read_byte_data; + info->write_word_data = zl6100_write_word_data; + info->write_byte = zl6100_write_byte; + + return pmbus_do_probe(client, mid, info); +} + +static struct i2c_driver zl6100_driver = { + .driver = { + .name = "zl6100", + }, + .probe = zl6100_probe, + .remove = pmbus_do_remove, + .id_table = zl6100_id, +}; + +module_i2c_driver(zl6100_driver); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("PMBus driver for ZL6100 and compatibles"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/s3c-hwmon.c b/drivers/hwmon/s3c-hwmon.c index 3f3f9a47acf..0674c13bbd4 100644 --- a/drivers/hwmon/s3c-hwmon.c +++ b/drivers/hwmon/s3c-hwmon.c @@ -22,7 +22,6 @@ #include <linux/module.h> #include <linux/slab.h> -#include <linux/delay.h> #include <linux/io.h> #include <linux/init.h> #include <linux/err.h> @@ -34,7 +33,7 @@ #include <linux/hwmon-sysfs.h> #include <plat/adc.h> -#include <plat/hwmon.h> +#include <linux/platform_data/hwmon-s3c.h> struct s3c_hwmon_attr { struct sensor_device_attribute in; @@ -51,7 +50,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 +72,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; } @@ -108,17 +107,14 @@ static ssize_t s3c_hwmon_show_raw(struct device *dev, return (ret < 0) ? ret : snprintf(buf, PAGE_SIZE, "%d\n", ret); } -#define DEF_ADC_ATTR(x) \ - static SENSOR_DEVICE_ATTR(adc##x##_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, x) - -DEF_ADC_ATTR(0); -DEF_ADC_ATTR(1); -DEF_ADC_ATTR(2); -DEF_ADC_ATTR(3); -DEF_ADC_ATTR(4); -DEF_ADC_ATTR(5); -DEF_ADC_ATTR(6); -DEF_ADC_ATTR(7); +static SENSOR_DEVICE_ATTR(adc0_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 0); +static SENSOR_DEVICE_ATTR(adc1_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 1); +static SENSOR_DEVICE_ATTR(adc2_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 2); +static SENSOR_DEVICE_ATTR(adc3_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 3); +static SENSOR_DEVICE_ATTR(adc4_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 4); +static SENSOR_DEVICE_ATTR(adc5_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 5); +static SENSOR_DEVICE_ATTR(adc6_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 6); +static SENSOR_DEVICE_ATTR(adc7_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 7); static struct attribute *s3c_hwmon_attrs[9] = { &sensor_dev_attr_adc0_raw.dev_attr.attr, @@ -169,7 +165,7 @@ static ssize_t s3c_hwmon_ch_show(struct device *dev, { struct sensor_device_attribute *sen_attr = to_sensor_dev_attr(attr); struct s3c_hwmon *hwmon = platform_get_drvdata(to_platform_device(dev)); - struct s3c_hwmon_pdata *pdata = dev->platform_data; + struct s3c_hwmon_pdata *pdata = dev_get_platdata(dev); struct s3c_hwmon_chcfg *cfg; int ret; @@ -198,7 +194,7 @@ static ssize_t s3c_hwmon_label_show(struct device *dev, char *buf) { struct sensor_device_attribute *sen_attr = to_sensor_dev_attr(attr); - struct s3c_hwmon_pdata *pdata = dev->platform_data; + struct s3c_hwmon_pdata *pdata = dev_get_platdata(dev); struct s3c_hwmon_chcfg *cfg; cfg = pdata->in[sen_attr->index]; @@ -232,9 +228,9 @@ static int s3c_hwmon_create_attr(struct device *dev, attr = &attrs->in; attr->index = channel; + sysfs_attr_init(&attr->dev_attr.attr); attr->dev_attr.attr.name = attrs->in_name; attr->dev_attr.attr.mode = S_IRUGO; - attr->dev_attr.attr.owner = THIS_MODULE; attr->dev_attr.show = s3c_hwmon_ch_show; ret = device_create_file(dev, &attr->dev_attr); @@ -250,9 +246,9 @@ static int s3c_hwmon_create_attr(struct device *dev, attr = &attrs->label; attr->index = channel; + sysfs_attr_init(&attr->dev_attr.attr); attr->dev_attr.attr.name = attrs->label_name; attr->dev_attr.attr.mode = S_IRUGO; - attr->dev_attr.attr.owner = THIS_MODULE; attr->dev_attr.show = s3c_hwmon_label_show; ret = device_create_file(dev, &attr->dev_attr); @@ -276,9 +272,9 @@ static void s3c_hwmon_remove_attr(struct device *dev, * s3c_hwmon_probe - device probe entry. * @dev: The device being probed. */ -static int __devinit s3c_hwmon_probe(struct platform_device *dev) +static int s3c_hwmon_probe(struct platform_device *dev) { - struct s3c_hwmon_pdata *pdata = dev->dev.platform_data; + struct s3c_hwmon_pdata *pdata = dev_get_platdata(&dev->dev); struct s3c_hwmon *hwmon; int ret = 0; int i; @@ -288,23 +284,20 @@ static int __devinit s3c_hwmon_probe(struct platform_device *dev) return -EINVAL; } - hwmon = kzalloc(sizeof(struct s3c_hwmon), GFP_KERNEL); - if (hwmon == NULL) { - dev_err(&dev->dev, "no memory\n"); + hwmon = devm_kzalloc(&dev->dev, sizeof(struct s3c_hwmon), GFP_KERNEL); + if (hwmon == NULL) return -ENOMEM; - } platform_set_drvdata(dev, hwmon); - init_MUTEX(&hwmon->lock); + mutex_init(&hwmon->lock); /* Register with the core ADC driver. */ hwmon->client = s3c_adc_register(dev, NULL, NULL, 0); if (IS_ERR(hwmon->client)) { dev_err(&dev->dev, "cannot register adc\n"); - ret = PTR_ERR(hwmon->client); - goto err_mem; + return PTR_ERR(hwmon->client); } /* add attributes for our adc devices. */ @@ -363,12 +356,10 @@ static int __devinit s3c_hwmon_probe(struct platform_device *dev) err_registered: s3c_adc_release(hwmon->client); - err_mem: - kfree(hwmon); return ret; } -static int __devexit s3c_hwmon_remove(struct platform_device *dev) +static int s3c_hwmon_remove(struct platform_device *dev) { struct s3c_hwmon *hwmon = platform_get_drvdata(dev); int i; @@ -390,21 +381,10 @@ static struct platform_driver s3c_hwmon_driver = { .owner = THIS_MODULE, }, .probe = s3c_hwmon_probe, - .remove = __devexit_p(s3c_hwmon_remove), + .remove = s3c_hwmon_remove, }; -static int __init s3c_hwmon_init(void) -{ - return platform_driver_register(&s3c_hwmon_driver); -} - -static void __exit s3c_hwmon_exit(void) -{ - platform_driver_unregister(&s3c_hwmon_driver); -} - -module_init(s3c_hwmon_init); -module_exit(s3c_hwmon_exit); +module_platform_driver(s3c_hwmon_driver); MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); MODULE_DESCRIPTION("S3C ADC HWMon driver"); diff --git a/drivers/hwmon/sch5627.c b/drivers/hwmon/sch5627.c new file mode 100644 index 00000000000..0cc99fd83e8 --- /dev/null +++ b/drivers/hwmon/sch5627.c @@ -0,0 +1,605 @@ +/*************************************************************************** + * Copyright (C) 2010-2012 Hans de Goede <hdegoede@redhat.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. * + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/platform_device.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include "sch56xx-common.h" + +#define DRVNAME "sch5627" +#define DEVNAME DRVNAME /* We only support one model */ + +#define SCH5627_HWMON_ID 0xa5 +#define SCH5627_COMPANY_ID 0x5c +#define SCH5627_PRIMARY_ID 0xa0 + +#define SCH5627_REG_BUILD_CODE 0x39 +#define SCH5627_REG_BUILD_ID 0x3a +#define SCH5627_REG_HWMON_ID 0x3c +#define SCH5627_REG_HWMON_REV 0x3d +#define SCH5627_REG_COMPANY_ID 0x3e +#define SCH5627_REG_PRIMARY_ID 0x3f +#define SCH5627_REG_CTRL 0x40 + +#define SCH5627_NO_TEMPS 8 +#define SCH5627_NO_FANS 4 +#define SCH5627_NO_IN 5 + +static const u16 SCH5627_REG_TEMP_MSB[SCH5627_NO_TEMPS] = { + 0x2B, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x180, 0x181 }; +static const u16 SCH5627_REG_TEMP_LSN[SCH5627_NO_TEMPS] = { + 0xE2, 0xE1, 0xE1, 0xE5, 0xE5, 0xE6, 0x182, 0x182 }; +static const u16 SCH5627_REG_TEMP_HIGH_NIBBLE[SCH5627_NO_TEMPS] = { + 0, 0, 1, 1, 0, 0, 0, 1 }; +static const u16 SCH5627_REG_TEMP_HIGH[SCH5627_NO_TEMPS] = { + 0x61, 0x57, 0x59, 0x5B, 0x5D, 0x5F, 0x184, 0x186 }; +static const u16 SCH5627_REG_TEMP_ABS[SCH5627_NO_TEMPS] = { + 0x9B, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x1A8, 0x1A9 }; + +static const u16 SCH5627_REG_FAN[SCH5627_NO_FANS] = { + 0x2C, 0x2E, 0x30, 0x32 }; +static const u16 SCH5627_REG_FAN_MIN[SCH5627_NO_FANS] = { + 0x62, 0x64, 0x66, 0x68 }; + +static const u16 SCH5627_REG_IN_MSB[SCH5627_NO_IN] = { + 0x22, 0x23, 0x24, 0x25, 0x189 }; +static const u16 SCH5627_REG_IN_LSN[SCH5627_NO_IN] = { + 0xE4, 0xE4, 0xE3, 0xE3, 0x18A }; +static const u16 SCH5627_REG_IN_HIGH_NIBBLE[SCH5627_NO_IN] = { + 1, 0, 1, 0, 1 }; +static const u16 SCH5627_REG_IN_FACTOR[SCH5627_NO_IN] = { + 10745, 3660, 9765, 10745, 3660 }; +static const char * const SCH5627_IN_LABELS[SCH5627_NO_IN] = { + "VCC", "VTT", "VBAT", "VTR", "V_IN" }; + +struct sch5627_data { + unsigned short addr; + struct device *hwmon_dev; + struct sch56xx_watchdog_data *watchdog; + u8 control; + u8 temp_max[SCH5627_NO_TEMPS]; + u8 temp_crit[SCH5627_NO_TEMPS]; + u16 fan_min[SCH5627_NO_FANS]; + + struct mutex update_lock; + unsigned long last_battery; /* In jiffies */ + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + u16 temp[SCH5627_NO_TEMPS]; + u16 fan[SCH5627_NO_FANS]; + u16 in[SCH5627_NO_IN]; +}; + +static struct sch5627_data *sch5627_update_device(struct device *dev) +{ + struct sch5627_data *data = dev_get_drvdata(dev); + struct sch5627_data *ret = data; + int i, val; + + mutex_lock(&data->update_lock); + + /* Trigger a Vbat voltage measurement every 5 minutes */ + if (time_after(jiffies, data->last_battery + 300 * HZ)) { + sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, + data->control | 0x10); + data->last_battery = jiffies; + } + + /* Cache the values for 1 second */ + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + for (i = 0; i < SCH5627_NO_TEMPS; i++) { + val = sch56xx_read_virtual_reg12(data->addr, + SCH5627_REG_TEMP_MSB[i], + SCH5627_REG_TEMP_LSN[i], + SCH5627_REG_TEMP_HIGH_NIBBLE[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp[i] = val; + } + + for (i = 0; i < SCH5627_NO_FANS; i++) { + val = sch56xx_read_virtual_reg16(data->addr, + SCH5627_REG_FAN[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->fan[i] = val; + } + + for (i = 0; i < SCH5627_NO_IN; i++) { + val = sch56xx_read_virtual_reg12(data->addr, + SCH5627_REG_IN_MSB[i], + SCH5627_REG_IN_LSN[i], + SCH5627_REG_IN_HIGH_NIBBLE[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->in[i] = val; + } + + data->last_updated = jiffies; + data->valid = 1; + } +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +static int sch5627_read_limits(struct sch5627_data *data) +{ + int i, val; + + for (i = 0; i < SCH5627_NO_TEMPS; i++) { + /* + * Note what SMSC calls ABS, is what lm_sensors calls max + * (aka high), and HIGH is what lm_sensors calls crit. + */ + val = sch56xx_read_virtual_reg(data->addr, + SCH5627_REG_TEMP_ABS[i]); + if (val < 0) + return val; + data->temp_max[i] = val; + + val = sch56xx_read_virtual_reg(data->addr, + SCH5627_REG_TEMP_HIGH[i]); + if (val < 0) + return val; + data->temp_crit[i] = val; + } + for (i = 0; i < SCH5627_NO_FANS; i++) { + val = sch56xx_read_virtual_reg16(data->addr, + SCH5627_REG_FAN_MIN[i]); + if (val < 0) + return val; + data->fan_min[i] = val; + } + + return 0; +} + +static int reg_to_temp(u16 reg) +{ + return (reg * 625) / 10 - 64000; +} + +static int reg_to_temp_limit(u8 reg) +{ + return (reg - 64) * 1000; +} + +static int reg_to_rpm(u16 reg) +{ + if (reg == 0) + return -EIO; + if (reg == 0xffff) + return 0; + + return 5400540 / reg; +} + +static ssize_t show_name(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", DEVNAME); +} + +static ssize_t show_temp(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5627_data *data = sch5627_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = reg_to_temp(data->temp[attr->index]); + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_temp_fault(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5627_data *data = sch5627_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return snprintf(buf, PAGE_SIZE, "%d\n", data->temp[attr->index] == 0); +} + +static ssize_t show_temp_max(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5627_data *data = dev_get_drvdata(dev); + int val; + + val = reg_to_temp_limit(data->temp_max[attr->index]); + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_temp_crit(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5627_data *data = dev_get_drvdata(dev); + int val; + + val = reg_to_temp_limit(data->temp_crit[attr->index]); + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_fan(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5627_data *data = sch5627_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = reg_to_rpm(data->fan[attr->index]); + if (val < 0) + return val; + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_fan_fault(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5627_data *data = sch5627_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return snprintf(buf, PAGE_SIZE, "%d\n", + data->fan[attr->index] == 0xffff); +} + +static ssize_t show_fan_min(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5627_data *data = dev_get_drvdata(dev); + int val = reg_to_rpm(data->fan_min[attr->index]); + if (val < 0) + return val; + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_in(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5627_data *data = sch5627_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = DIV_ROUND_CLOSEST( + data->in[attr->index] * SCH5627_REG_IN_FACTOR[attr->index], + 10000); + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_in_label(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + + return snprintf(buf, PAGE_SIZE, "%s\n", + SCH5627_IN_LABELS[attr->index]); +} + +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_temp, NULL, 5); +static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, show_temp, NULL, 6); +static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, show_temp, NULL, 7); +static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_temp_fault, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_temp_fault, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_fault, S_IRUGO, show_temp_fault, NULL, 5); +static SENSOR_DEVICE_ATTR(temp7_fault, S_IRUGO, show_temp_fault, NULL, 6); +static SENSOR_DEVICE_ATTR(temp8_fault, S_IRUGO, show_temp_fault, NULL, 7); +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp_max, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp_max, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO, show_temp_max, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_max, S_IRUGO, show_temp_max, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_max, S_IRUGO, show_temp_max, NULL, 5); +static SENSOR_DEVICE_ATTR(temp7_max, S_IRUGO, show_temp_max, NULL, 6); +static SENSOR_DEVICE_ATTR(temp8_max, S_IRUGO, show_temp_max, NULL, 7); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp_crit, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_crit, S_IRUGO, show_temp_crit, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_crit, S_IRUGO, show_temp_crit, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_crit, S_IRUGO, show_temp_crit, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_crit, S_IRUGO, show_temp_crit, NULL, 5); +static SENSOR_DEVICE_ATTR(temp7_crit, S_IRUGO, show_temp_crit, NULL, 6); +static SENSOR_DEVICE_ATTR(temp8_crit, S_IRUGO, show_temp_crit, NULL, 7); + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3); +static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, show_fan_fault, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, show_fan_fault, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_fault, S_IRUGO, show_fan_fault, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_fault, S_IRUGO, show_fan_fault, NULL, 3); +static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO, show_fan_min, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO, show_fan_min, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_min, S_IRUGO, show_fan_min, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_min, S_IRUGO, show_fan_min, NULL, 3); + +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_in, NULL, 4); +static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_in_label, NULL, 0); +static SENSOR_DEVICE_ATTR(in1_label, S_IRUGO, show_in_label, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_label, S_IRUGO, show_in_label, NULL, 2); +static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_in_label, NULL, 3); + +static struct attribute *sch5627_attributes[] = { + &dev_attr_name.attr, + + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp6_input.dev_attr.attr, + &sensor_dev_attr_temp7_input.dev_attr.attr, + &sensor_dev_attr_temp8_input.dev_attr.attr, + &sensor_dev_attr_temp1_fault.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, + &sensor_dev_attr_temp4_fault.dev_attr.attr, + &sensor_dev_attr_temp5_fault.dev_attr.attr, + &sensor_dev_attr_temp6_fault.dev_attr.attr, + &sensor_dev_attr_temp7_fault.dev_attr.attr, + &sensor_dev_attr_temp8_fault.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + &sensor_dev_attr_temp6_max.dev_attr.attr, + &sensor_dev_attr_temp7_max.dev_attr.attr, + &sensor_dev_attr_temp8_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp4_crit.dev_attr.attr, + &sensor_dev_attr_temp5_crit.dev_attr.attr, + &sensor_dev_attr_temp6_crit.dev_attr.attr, + &sensor_dev_attr_temp7_crit.dev_attr.attr, + &sensor_dev_attr_temp8_crit.dev_attr.attr, + + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan1_fault.dev_attr.attr, + &sensor_dev_attr_fan2_fault.dev_attr.attr, + &sensor_dev_attr_fan3_fault.dev_attr.attr, + &sensor_dev_attr_fan4_fault.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan3_min.dev_attr.attr, + &sensor_dev_attr_fan4_min.dev_attr.attr, + + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in0_label.dev_attr.attr, + &sensor_dev_attr_in1_label.dev_attr.attr, + &sensor_dev_attr_in2_label.dev_attr.attr, + &sensor_dev_attr_in3_label.dev_attr.attr, + /* No in4_label as in4 is a generic input pin */ + + NULL +}; + +static const struct attribute_group sch5627_group = { + .attrs = sch5627_attributes, +}; + +static int sch5627_remove(struct platform_device *pdev) +{ + struct sch5627_data *data = platform_get_drvdata(pdev); + + if (data->watchdog) + sch56xx_watchdog_unregister(data->watchdog); + + if (data->hwmon_dev) + hwmon_device_unregister(data->hwmon_dev); + + sysfs_remove_group(&pdev->dev.kobj, &sch5627_group); + + return 0; +} + +static int sch5627_probe(struct platform_device *pdev) +{ + struct sch5627_data *data; + int err, build_code, build_id, hwmon_rev, val; + + data = devm_kzalloc(&pdev->dev, sizeof(struct sch5627_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start; + mutex_init(&data->update_lock); + platform_set_drvdata(pdev, data); + + val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_HWMON_ID); + if (val < 0) { + err = val; + goto error; + } + if (val != SCH5627_HWMON_ID) { + pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "hwmon", + val, SCH5627_HWMON_ID); + err = -ENODEV; + goto error; + } + + val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_COMPANY_ID); + if (val < 0) { + err = val; + goto error; + } + if (val != SCH5627_COMPANY_ID) { + pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "company", + val, SCH5627_COMPANY_ID); + err = -ENODEV; + goto error; + } + + val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_PRIMARY_ID); + if (val < 0) { + err = val; + goto error; + } + if (val != SCH5627_PRIMARY_ID) { + pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "primary", + val, SCH5627_PRIMARY_ID); + err = -ENODEV; + goto error; + } + + build_code = sch56xx_read_virtual_reg(data->addr, + SCH5627_REG_BUILD_CODE); + if (build_code < 0) { + err = build_code; + goto error; + } + + build_id = sch56xx_read_virtual_reg16(data->addr, + SCH5627_REG_BUILD_ID); + if (build_id < 0) { + err = build_id; + goto error; + } + + hwmon_rev = sch56xx_read_virtual_reg(data->addr, + SCH5627_REG_HWMON_REV); + if (hwmon_rev < 0) { + err = hwmon_rev; + goto error; + } + + val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_CTRL); + if (val < 0) { + err = val; + goto error; + } + data->control = val; + if (!(data->control & 0x01)) { + pr_err("hardware monitoring not enabled\n"); + err = -ENODEV; + goto error; + } + /* Trigger a Vbat voltage measurement, so that we get a valid reading + the first time we read Vbat */ + sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, + data->control | 0x10); + data->last_battery = jiffies; + + /* + * Read limits, we do this only once as reading a register on + * the sch5627 is quite expensive (and they don't change). + */ + err = sch5627_read_limits(data); + if (err) + goto error; + + pr_info("found %s chip at %#hx\n", DEVNAME, data->addr); + pr_info("firmware build: code 0x%02X, id 0x%04X, hwmon: rev 0x%02X\n", + build_code, build_id, hwmon_rev); + + /* Register sysfs interface files */ + err = sysfs_create_group(&pdev->dev.kobj, &sch5627_group); + if (err) + goto error; + + data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + data->hwmon_dev = NULL; + goto error; + } + + /* Note failing to register the watchdog is not a fatal error */ + data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr, + (build_code << 24) | (build_id << 8) | hwmon_rev, + &data->update_lock, 1); + + return 0; + +error: + sch5627_remove(pdev); + return err; +} + +static struct platform_driver sch5627_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, + }, + .probe = sch5627_probe, + .remove = sch5627_remove, +}; + +module_platform_driver(sch5627_driver); + +MODULE_DESCRIPTION("SMSC SCH5627 Hardware Monitoring Driver"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/sch5636.c b/drivers/hwmon/sch5636.c new file mode 100644 index 00000000000..547b5c952ef --- /dev/null +++ b/drivers/hwmon/sch5636.c @@ -0,0 +1,535 @@ +/*************************************************************************** + * Copyright (C) 2011-2012 Hans de Goede <hdegoede@redhat.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. * + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/platform_device.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include "sch56xx-common.h" + +#define DRVNAME "sch5636" +#define DEVNAME "theseus" /* We only support one model for now */ + +#define SCH5636_REG_FUJITSU_ID 0x780 +#define SCH5636_REG_FUJITSU_REV 0x783 + +#define SCH5636_NO_INS 5 +#define SCH5636_NO_TEMPS 16 +#define SCH5636_NO_FANS 8 + +static const u16 SCH5636_REG_IN_VAL[SCH5636_NO_INS] = { + 0x22, 0x23, 0x24, 0x25, 0x189 }; +static const u16 SCH5636_REG_IN_FACTORS[SCH5636_NO_INS] = { + 4400, 1500, 4000, 4400, 16000 }; +static const char * const SCH5636_IN_LABELS[SCH5636_NO_INS] = { + "3.3V", "VREF", "VBAT", "3.3AUX", "12V" }; + +static const u16 SCH5636_REG_TEMP_VAL[SCH5636_NO_TEMPS] = { + 0x2B, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x180, 0x181, + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C }; +#define SCH5636_REG_TEMP_CTRL(i) (0x790 + (i)) +#define SCH5636_TEMP_WORKING 0x01 +#define SCH5636_TEMP_ALARM 0x02 +#define SCH5636_TEMP_DEACTIVATED 0x80 + +static const u16 SCH5636_REG_FAN_VAL[SCH5636_NO_FANS] = { + 0x2C, 0x2E, 0x30, 0x32, 0x62, 0x64, 0x66, 0x68 }; +#define SCH5636_REG_FAN_CTRL(i) (0x880 + (i)) +/* FAULT in datasheet, but acts as an alarm */ +#define SCH5636_FAN_ALARM 0x04 +#define SCH5636_FAN_NOT_PRESENT 0x08 +#define SCH5636_FAN_DEACTIVATED 0x80 + + +struct sch5636_data { + unsigned short addr; + struct device *hwmon_dev; + struct sch56xx_watchdog_data *watchdog; + + struct mutex update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + u8 in[SCH5636_NO_INS]; + u8 temp_val[SCH5636_NO_TEMPS]; + u8 temp_ctrl[SCH5636_NO_TEMPS]; + u16 fan_val[SCH5636_NO_FANS]; + u8 fan_ctrl[SCH5636_NO_FANS]; +}; + +static struct sch5636_data *sch5636_update_device(struct device *dev) +{ + struct sch5636_data *data = dev_get_drvdata(dev); + struct sch5636_data *ret = data; + int i, val; + + mutex_lock(&data->update_lock); + + /* Cache the values for 1 second */ + if (data->valid && !time_after(jiffies, data->last_updated + HZ)) + goto abort; + + for (i = 0; i < SCH5636_NO_INS; i++) { + val = sch56xx_read_virtual_reg(data->addr, + SCH5636_REG_IN_VAL[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->in[i] = val; + } + + for (i = 0; i < SCH5636_NO_TEMPS; i++) { + if (data->temp_ctrl[i] & SCH5636_TEMP_DEACTIVATED) + continue; + + val = sch56xx_read_virtual_reg(data->addr, + SCH5636_REG_TEMP_VAL[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp_val[i] = val; + + val = sch56xx_read_virtual_reg(data->addr, + SCH5636_REG_TEMP_CTRL(i)); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp_ctrl[i] = val; + /* Alarms need to be explicitly write-cleared */ + if (val & SCH5636_TEMP_ALARM) { + sch56xx_write_virtual_reg(data->addr, + SCH5636_REG_TEMP_CTRL(i), val); + } + } + + for (i = 0; i < SCH5636_NO_FANS; i++) { + if (data->fan_ctrl[i] & SCH5636_FAN_DEACTIVATED) + continue; + + val = sch56xx_read_virtual_reg16(data->addr, + SCH5636_REG_FAN_VAL[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->fan_val[i] = val; + + val = sch56xx_read_virtual_reg(data->addr, + SCH5636_REG_FAN_CTRL(i)); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->fan_ctrl[i] = val; + /* Alarms need to be explicitly write-cleared */ + if (val & SCH5636_FAN_ALARM) { + sch56xx_write_virtual_reg(data->addr, + SCH5636_REG_FAN_CTRL(i), val); + } + } + + data->last_updated = jiffies; + data->valid = 1; +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +static int reg_to_rpm(u16 reg) +{ + if (reg == 0) + return -EIO; + if (reg == 0xffff) + return 0; + + return 5400540 / reg; +} + +static ssize_t show_name(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", DEVNAME); +} + +static ssize_t show_in_value(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5636_data *data = sch5636_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = DIV_ROUND_CLOSEST( + data->in[attr->index] * SCH5636_REG_IN_FACTORS[attr->index], + 255); + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_in_label(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + + return snprintf(buf, PAGE_SIZE, "%s\n", + SCH5636_IN_LABELS[attr->index]); +} + +static ssize_t show_temp_value(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5636_data *data = sch5636_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = (data->temp_val[attr->index] - 64) * 1000; + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_temp_fault(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5636_data *data = sch5636_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_WORKING) ? 0 : 1; + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_temp_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5636_data *data = sch5636_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_ALARM) ? 1 : 0; + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_fan_value(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5636_data *data = sch5636_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = reg_to_rpm(data->fan_val[attr->index]); + if (val < 0) + return val; + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_fan_fault(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5636_data *data = sch5636_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = (data->fan_ctrl[attr->index] & SCH5636_FAN_NOT_PRESENT) ? 1 : 0; + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_fan_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5636_data *data = sch5636_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = (data->fan_ctrl[attr->index] & SCH5636_FAN_ALARM) ? 1 : 0; + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static struct sensor_device_attribute sch5636_attr[] = { + SENSOR_ATTR(name, 0444, show_name, NULL, 0), + SENSOR_ATTR(in0_input, 0444, show_in_value, NULL, 0), + SENSOR_ATTR(in0_label, 0444, show_in_label, NULL, 0), + SENSOR_ATTR(in1_input, 0444, show_in_value, NULL, 1), + SENSOR_ATTR(in1_label, 0444, show_in_label, NULL, 1), + SENSOR_ATTR(in2_input, 0444, show_in_value, NULL, 2), + SENSOR_ATTR(in2_label, 0444, show_in_label, NULL, 2), + SENSOR_ATTR(in3_input, 0444, show_in_value, NULL, 3), + SENSOR_ATTR(in3_label, 0444, show_in_label, NULL, 3), + SENSOR_ATTR(in4_input, 0444, show_in_value, NULL, 4), + SENSOR_ATTR(in4_label, 0444, show_in_label, NULL, 4), +}; + +static struct sensor_device_attribute sch5636_temp_attr[] = { + SENSOR_ATTR(temp1_input, 0444, show_temp_value, NULL, 0), + SENSOR_ATTR(temp1_fault, 0444, show_temp_fault, NULL, 0), + SENSOR_ATTR(temp1_alarm, 0444, show_temp_alarm, NULL, 0), + SENSOR_ATTR(temp2_input, 0444, show_temp_value, NULL, 1), + SENSOR_ATTR(temp2_fault, 0444, show_temp_fault, NULL, 1), + SENSOR_ATTR(temp2_alarm, 0444, show_temp_alarm, NULL, 1), + SENSOR_ATTR(temp3_input, 0444, show_temp_value, NULL, 2), + SENSOR_ATTR(temp3_fault, 0444, show_temp_fault, NULL, 2), + SENSOR_ATTR(temp3_alarm, 0444, show_temp_alarm, NULL, 2), + SENSOR_ATTR(temp4_input, 0444, show_temp_value, NULL, 3), + SENSOR_ATTR(temp4_fault, 0444, show_temp_fault, NULL, 3), + SENSOR_ATTR(temp4_alarm, 0444, show_temp_alarm, NULL, 3), + SENSOR_ATTR(temp5_input, 0444, show_temp_value, NULL, 4), + SENSOR_ATTR(temp5_fault, 0444, show_temp_fault, NULL, 4), + SENSOR_ATTR(temp5_alarm, 0444, show_temp_alarm, NULL, 4), + SENSOR_ATTR(temp6_input, 0444, show_temp_value, NULL, 5), + SENSOR_ATTR(temp6_fault, 0444, show_temp_fault, NULL, 5), + SENSOR_ATTR(temp6_alarm, 0444, show_temp_alarm, NULL, 5), + SENSOR_ATTR(temp7_input, 0444, show_temp_value, NULL, 6), + SENSOR_ATTR(temp7_fault, 0444, show_temp_fault, NULL, 6), + SENSOR_ATTR(temp7_alarm, 0444, show_temp_alarm, NULL, 6), + SENSOR_ATTR(temp8_input, 0444, show_temp_value, NULL, 7), + SENSOR_ATTR(temp8_fault, 0444, show_temp_fault, NULL, 7), + SENSOR_ATTR(temp8_alarm, 0444, show_temp_alarm, NULL, 7), + SENSOR_ATTR(temp9_input, 0444, show_temp_value, NULL, 8), + SENSOR_ATTR(temp9_fault, 0444, show_temp_fault, NULL, 8), + SENSOR_ATTR(temp9_alarm, 0444, show_temp_alarm, NULL, 8), + SENSOR_ATTR(temp10_input, 0444, show_temp_value, NULL, 9), + SENSOR_ATTR(temp10_fault, 0444, show_temp_fault, NULL, 9), + SENSOR_ATTR(temp10_alarm, 0444, show_temp_alarm, NULL, 9), + SENSOR_ATTR(temp11_input, 0444, show_temp_value, NULL, 10), + SENSOR_ATTR(temp11_fault, 0444, show_temp_fault, NULL, 10), + SENSOR_ATTR(temp11_alarm, 0444, show_temp_alarm, NULL, 10), + SENSOR_ATTR(temp12_input, 0444, show_temp_value, NULL, 11), + SENSOR_ATTR(temp12_fault, 0444, show_temp_fault, NULL, 11), + SENSOR_ATTR(temp12_alarm, 0444, show_temp_alarm, NULL, 11), + SENSOR_ATTR(temp13_input, 0444, show_temp_value, NULL, 12), + SENSOR_ATTR(temp13_fault, 0444, show_temp_fault, NULL, 12), + SENSOR_ATTR(temp13_alarm, 0444, show_temp_alarm, NULL, 12), + SENSOR_ATTR(temp14_input, 0444, show_temp_value, NULL, 13), + SENSOR_ATTR(temp14_fault, 0444, show_temp_fault, NULL, 13), + SENSOR_ATTR(temp14_alarm, 0444, show_temp_alarm, NULL, 13), + SENSOR_ATTR(temp15_input, 0444, show_temp_value, NULL, 14), + SENSOR_ATTR(temp15_fault, 0444, show_temp_fault, NULL, 14), + SENSOR_ATTR(temp15_alarm, 0444, show_temp_alarm, NULL, 14), + SENSOR_ATTR(temp16_input, 0444, show_temp_value, NULL, 15), + SENSOR_ATTR(temp16_fault, 0444, show_temp_fault, NULL, 15), + SENSOR_ATTR(temp16_alarm, 0444, show_temp_alarm, NULL, 15), +}; + +static struct sensor_device_attribute sch5636_fan_attr[] = { + SENSOR_ATTR(fan1_input, 0444, show_fan_value, NULL, 0), + SENSOR_ATTR(fan1_fault, 0444, show_fan_fault, NULL, 0), + SENSOR_ATTR(fan1_alarm, 0444, show_fan_alarm, NULL, 0), + SENSOR_ATTR(fan2_input, 0444, show_fan_value, NULL, 1), + SENSOR_ATTR(fan2_fault, 0444, show_fan_fault, NULL, 1), + SENSOR_ATTR(fan2_alarm, 0444, show_fan_alarm, NULL, 1), + SENSOR_ATTR(fan3_input, 0444, show_fan_value, NULL, 2), + SENSOR_ATTR(fan3_fault, 0444, show_fan_fault, NULL, 2), + SENSOR_ATTR(fan3_alarm, 0444, show_fan_alarm, NULL, 2), + SENSOR_ATTR(fan4_input, 0444, show_fan_value, NULL, 3), + SENSOR_ATTR(fan4_fault, 0444, show_fan_fault, NULL, 3), + SENSOR_ATTR(fan4_alarm, 0444, show_fan_alarm, NULL, 3), + SENSOR_ATTR(fan5_input, 0444, show_fan_value, NULL, 4), + SENSOR_ATTR(fan5_fault, 0444, show_fan_fault, NULL, 4), + SENSOR_ATTR(fan5_alarm, 0444, show_fan_alarm, NULL, 4), + SENSOR_ATTR(fan6_input, 0444, show_fan_value, NULL, 5), + SENSOR_ATTR(fan6_fault, 0444, show_fan_fault, NULL, 5), + SENSOR_ATTR(fan6_alarm, 0444, show_fan_alarm, NULL, 5), + SENSOR_ATTR(fan7_input, 0444, show_fan_value, NULL, 6), + SENSOR_ATTR(fan7_fault, 0444, show_fan_fault, NULL, 6), + SENSOR_ATTR(fan7_alarm, 0444, show_fan_alarm, NULL, 6), + SENSOR_ATTR(fan8_input, 0444, show_fan_value, NULL, 7), + SENSOR_ATTR(fan8_fault, 0444, show_fan_fault, NULL, 7), + SENSOR_ATTR(fan8_alarm, 0444, show_fan_alarm, NULL, 7), +}; + +static int sch5636_remove(struct platform_device *pdev) +{ + struct sch5636_data *data = platform_get_drvdata(pdev); + int i; + + if (data->watchdog) + sch56xx_watchdog_unregister(data->watchdog); + + if (data->hwmon_dev) + hwmon_device_unregister(data->hwmon_dev); + + for (i = 0; i < ARRAY_SIZE(sch5636_attr); i++) + device_remove_file(&pdev->dev, &sch5636_attr[i].dev_attr); + + for (i = 0; i < SCH5636_NO_TEMPS * 3; i++) + device_remove_file(&pdev->dev, + &sch5636_temp_attr[i].dev_attr); + + for (i = 0; i < SCH5636_NO_FANS * 3; i++) + device_remove_file(&pdev->dev, + &sch5636_fan_attr[i].dev_attr); + + return 0; +} + +static int sch5636_probe(struct platform_device *pdev) +{ + struct sch5636_data *data; + int i, err, val, revision[2]; + char id[4]; + + data = devm_kzalloc(&pdev->dev, sizeof(struct sch5636_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start; + mutex_init(&data->update_lock); + platform_set_drvdata(pdev, data); + + for (i = 0; i < 3; i++) { + val = sch56xx_read_virtual_reg(data->addr, + SCH5636_REG_FUJITSU_ID + i); + if (val < 0) { + pr_err("Could not read Fujitsu id byte at %#x\n", + SCH5636_REG_FUJITSU_ID + i); + err = val; + goto error; + } + id[i] = val; + } + id[i] = '\0'; + + if (strcmp(id, "THS")) { + pr_err("Unknown Fujitsu id: %02x%02x%02x\n", + id[0], id[1], id[2]); + err = -ENODEV; + goto error; + } + + for (i = 0; i < 2; i++) { + val = sch56xx_read_virtual_reg(data->addr, + SCH5636_REG_FUJITSU_REV + i); + if (val < 0) { + err = val; + goto error; + } + revision[i] = val; + } + pr_info("Found %s chip at %#hx, revison: %d.%02d\n", DEVNAME, + data->addr, revision[0], revision[1]); + + /* Read all temp + fan ctrl registers to determine which are active */ + for (i = 0; i < SCH5636_NO_TEMPS; i++) { + val = sch56xx_read_virtual_reg(data->addr, + SCH5636_REG_TEMP_CTRL(i)); + if (unlikely(val < 0)) { + err = val; + goto error; + } + data->temp_ctrl[i] = val; + } + + for (i = 0; i < SCH5636_NO_FANS; i++) { + val = sch56xx_read_virtual_reg(data->addr, + SCH5636_REG_FAN_CTRL(i)); + if (unlikely(val < 0)) { + err = val; + goto error; + } + data->fan_ctrl[i] = val; + } + + for (i = 0; i < ARRAY_SIZE(sch5636_attr); i++) { + err = device_create_file(&pdev->dev, + &sch5636_attr[i].dev_attr); + if (err) + goto error; + } + + for (i = 0; i < (SCH5636_NO_TEMPS * 3); i++) { + if (data->temp_ctrl[i/3] & SCH5636_TEMP_DEACTIVATED) + continue; + + err = device_create_file(&pdev->dev, + &sch5636_temp_attr[i].dev_attr); + if (err) + goto error; + } + + for (i = 0; i < (SCH5636_NO_FANS * 3); i++) { + if (data->fan_ctrl[i/3] & SCH5636_FAN_DEACTIVATED) + continue; + + err = device_create_file(&pdev->dev, + &sch5636_fan_attr[i].dev_attr); + if (err) + goto error; + } + + data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + data->hwmon_dev = NULL; + goto error; + } + + /* Note failing to register the watchdog is not a fatal error */ + data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr, + (revision[0] << 8) | revision[1], + &data->update_lock, 0); + + return 0; + +error: + sch5636_remove(pdev); + return err; +} + +static struct platform_driver sch5636_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, + }, + .probe = sch5636_probe, + .remove = sch5636_remove, +}; + +module_platform_driver(sch5636_driver); + +MODULE_DESCRIPTION("SMSC SCH5636 Hardware Monitoring Driver"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c new file mode 100644 index 00000000000..73868198328 --- /dev/null +++ b/drivers/hwmon/sch56xx-common.c @@ -0,0 +1,619 @@ +/*************************************************************************** + * Copyright (C) 2010-2012 Hans de Goede <hdegoede@redhat.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. * + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/watchdog.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/kref.h> +#include <linux/slab.h> +#include "sch56xx-common.h" + +/* Insmod parameters */ +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +#define SIO_SCH56XX_LD_EM 0x0C /* Embedded uController Logical Dev */ +#define SIO_UNLOCK_KEY 0x55 /* Key to enable Super-I/O */ +#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ + +#define SIO_REG_LDSEL 0x07 /* Logical device select */ +#define SIO_REG_DEVID 0x20 /* Device ID */ +#define SIO_REG_ENABLE 0x30 /* Logical device enable */ +#define SIO_REG_ADDR 0x66 /* Logical device address (2 bytes) */ + +#define SIO_SCH5627_ID 0xC6 /* Chipset ID */ +#define SIO_SCH5636_ID 0xC7 /* Chipset ID */ + +#define REGION_LENGTH 10 + +#define SCH56XX_CMD_READ 0x02 +#define SCH56XX_CMD_WRITE 0x03 + +/* Watchdog registers */ +#define SCH56XX_REG_WDOG_PRESET 0x58B +#define SCH56XX_REG_WDOG_CONTROL 0x58C +#define SCH56XX_WDOG_TIME_BASE_SEC 0x01 +#define SCH56XX_REG_WDOG_OUTPUT_ENABLE 0x58E +#define SCH56XX_WDOG_OUTPUT_ENABLE 0x02 + +struct sch56xx_watchdog_data { + u16 addr; + struct mutex *io_lock; + struct kref kref; + struct watchdog_info wdinfo; + struct watchdog_device wddev; + u8 watchdog_preset; + u8 watchdog_control; + u8 watchdog_output_enable; +}; + +static struct platform_device *sch56xx_pdev; + +/* Super I/O functions */ +static inline int superio_inb(int base, int reg) +{ + outb(reg, base); + return inb(base + 1); +} + +static inline int superio_enter(int base) +{ + /* Don't step on other drivers' I/O space by accident */ + if (!request_muxed_region(base, 2, "sch56xx")) { + pr_err("I/O address 0x%04x already in use\n", base); + return -EBUSY; + } + + outb(SIO_UNLOCK_KEY, base); + + return 0; +} + +static inline void superio_select(int base, int ld) +{ + outb(SIO_REG_LDSEL, base); + outb(ld, base + 1); +} + +static inline void superio_exit(int base) +{ + outb(SIO_LOCK_KEY, base); + release_region(base, 2); +} + +static int sch56xx_send_cmd(u16 addr, u8 cmd, u16 reg, u8 v) +{ + u8 val; + int i; + /* + * According to SMSC for the commands we use the maximum time for + * the EM to respond is 15 ms, but testing shows in practice it + * responds within 15-32 reads, so we first busy poll, and if + * that fails sleep a bit and try again until we are way past + * the 15 ms maximum response time. + */ + const int max_busy_polls = 64; + const int max_lazy_polls = 32; + + /* (Optional) Write-Clear the EC to Host Mailbox Register */ + val = inb(addr + 1); + outb(val, addr + 1); + + /* Set Mailbox Address Pointer to first location in Region 1 */ + outb(0x00, addr + 2); + outb(0x80, addr + 3); + + /* Write Request Packet Header */ + outb(cmd, addr + 4); /* VREG Access Type read:0x02 write:0x03 */ + outb(0x01, addr + 5); /* # of Entries: 1 Byte (8-bit) */ + outb(0x04, addr + 2); /* Mailbox AP to first data entry loc. */ + + /* Write Value field */ + if (cmd == SCH56XX_CMD_WRITE) + outb(v, addr + 4); + + /* Write Address field */ + outb(reg & 0xff, addr + 6); + outb(reg >> 8, addr + 7); + + /* Execute the Random Access Command */ + outb(0x01, addr); /* Write 01h to the Host-to-EC register */ + + /* EM Interface Polling "Algorithm" */ + for (i = 0; i < max_busy_polls + max_lazy_polls; i++) { + if (i >= max_busy_polls) + msleep(1); + /* Read Interrupt source Register */ + val = inb(addr + 8); + /* Write Clear the interrupt source bits */ + if (val) + outb(val, addr + 8); + /* Command Completed ? */ + if (val & 0x01) + break; + } + if (i == max_busy_polls + max_lazy_polls) { + pr_err("Max retries exceeded reading virtual register 0x%04hx (%d)\n", + reg, 1); + return -EIO; + } + + /* + * According to SMSC we may need to retry this, but sofar I've always + * seen this succeed in 1 try. + */ + for (i = 0; i < max_busy_polls; i++) { + /* Read EC-to-Host Register */ + val = inb(addr + 1); + /* Command Completed ? */ + if (val == 0x01) + break; + + if (i == 0) + pr_warn("EC reports: 0x%02x reading virtual register 0x%04hx\n", + (unsigned int)val, reg); + } + if (i == max_busy_polls) { + pr_err("Max retries exceeded reading virtual register 0x%04hx (%d)\n", + reg, 2); + return -EIO; + } + + /* + * According to the SMSC app note we should now do: + * + * Set Mailbox Address Pointer to first location in Region 1 * + * outb(0x00, addr + 2); + * outb(0x80, addr + 3); + * + * But if we do that things don't work, so let's not. + */ + + /* Read Value field */ + if (cmd == SCH56XX_CMD_READ) + return inb(addr + 4); + + return 0; +} + +int sch56xx_read_virtual_reg(u16 addr, u16 reg) +{ + return sch56xx_send_cmd(addr, SCH56XX_CMD_READ, reg, 0); +} +EXPORT_SYMBOL(sch56xx_read_virtual_reg); + +int sch56xx_write_virtual_reg(u16 addr, u16 reg, u8 val) +{ + return sch56xx_send_cmd(addr, SCH56XX_CMD_WRITE, reg, val); +} +EXPORT_SYMBOL(sch56xx_write_virtual_reg); + +int sch56xx_read_virtual_reg16(u16 addr, u16 reg) +{ + int lsb, msb; + + /* Read LSB first, this will cause the matching MSB to be latched */ + lsb = sch56xx_read_virtual_reg(addr, reg); + if (lsb < 0) + return lsb; + + msb = sch56xx_read_virtual_reg(addr, reg + 1); + if (msb < 0) + return msb; + + return lsb | (msb << 8); +} +EXPORT_SYMBOL(sch56xx_read_virtual_reg16); + +int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, + int high_nibble) +{ + int msb, lsn; + + /* Read MSB first, this will cause the matching LSN to be latched */ + msb = sch56xx_read_virtual_reg(addr, msb_reg); + if (msb < 0) + return msb; + + lsn = sch56xx_read_virtual_reg(addr, lsn_reg); + if (lsn < 0) + return lsn; + + if (high_nibble) + return (msb << 4) | (lsn >> 4); + else + return (msb << 4) | (lsn & 0x0f); +} +EXPORT_SYMBOL(sch56xx_read_virtual_reg12); + +/* + * Watchdog routines + */ + +/* Release our data struct when we're unregistered *and* + all references to our watchdog device are released */ +static void watchdog_release_resources(struct kref *r) +{ + struct sch56xx_watchdog_data *data = + container_of(r, struct sch56xx_watchdog_data, kref); + kfree(data); +} + +static int watchdog_set_timeout(struct watchdog_device *wddev, + unsigned int timeout) +{ + struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); + unsigned int resolution; + u8 control; + int ret; + + /* 1 second or 60 second resolution? */ + if (timeout <= 255) + resolution = 1; + else + resolution = 60; + + if (timeout < resolution || timeout > (resolution * 255)) + return -EINVAL; + + if (resolution == 1) + control = data->watchdog_control | SCH56XX_WDOG_TIME_BASE_SEC; + else + control = data->watchdog_control & ~SCH56XX_WDOG_TIME_BASE_SEC; + + if (data->watchdog_control != control) { + mutex_lock(data->io_lock); + ret = sch56xx_write_virtual_reg(data->addr, + SCH56XX_REG_WDOG_CONTROL, + control); + mutex_unlock(data->io_lock); + if (ret) + return ret; + + data->watchdog_control = control; + } + + /* + * Remember new timeout value, but do not write as that (re)starts + * the watchdog countdown. + */ + data->watchdog_preset = DIV_ROUND_UP(timeout, resolution); + wddev->timeout = data->watchdog_preset * resolution; + + return 0; +} + +static int watchdog_start(struct watchdog_device *wddev) +{ + struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); + int ret; + u8 val; + + /* + * The sch56xx's watchdog cannot really be started / stopped + * it is always running, but we can avoid the timer expiring + * from causing a system reset by clearing the output enable bit. + * + * The sch56xx's watchdog will set the watchdog event bit, bit 0 + * of the second interrupt source register (at base-address + 9), + * when the timer expires. + * + * This will only cause a system reset if the 0-1 flank happens when + * output enable is true. Setting output enable after the flank will + * not cause a reset, nor will the timer expiring a second time. + * This means we must clear the watchdog event bit in case it is set. + * + * The timer may still be running (after a recent watchdog_stop) and + * mere milliseconds away from expiring, so the timer must be reset + * first! + */ + + mutex_lock(data->io_lock); + + /* 1. Reset the watchdog countdown counter */ + ret = sch56xx_write_virtual_reg(data->addr, SCH56XX_REG_WDOG_PRESET, + data->watchdog_preset); + if (ret) + goto leave; + + /* 2. Enable output */ + val = data->watchdog_output_enable | SCH56XX_WDOG_OUTPUT_ENABLE; + ret = sch56xx_write_virtual_reg(data->addr, + SCH56XX_REG_WDOG_OUTPUT_ENABLE, val); + if (ret) + goto leave; + + data->watchdog_output_enable = val; + + /* 3. Clear the watchdog event bit if set */ + val = inb(data->addr + 9); + if (val & 0x01) + outb(0x01, data->addr + 9); + +leave: + mutex_unlock(data->io_lock); + return ret; +} + +static int watchdog_trigger(struct watchdog_device *wddev) +{ + struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); + int ret; + + /* Reset the watchdog countdown counter */ + mutex_lock(data->io_lock); + ret = sch56xx_write_virtual_reg(data->addr, SCH56XX_REG_WDOG_PRESET, + data->watchdog_preset); + mutex_unlock(data->io_lock); + + return ret; +} + +static int watchdog_stop(struct watchdog_device *wddev) +{ + struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); + int ret = 0; + u8 val; + + val = data->watchdog_output_enable & ~SCH56XX_WDOG_OUTPUT_ENABLE; + mutex_lock(data->io_lock); + ret = sch56xx_write_virtual_reg(data->addr, + SCH56XX_REG_WDOG_OUTPUT_ENABLE, val); + mutex_unlock(data->io_lock); + if (ret) + return ret; + + data->watchdog_output_enable = val; + return 0; +} + +static void watchdog_ref(struct watchdog_device *wddev) +{ + struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); + + kref_get(&data->kref); +} + +static void watchdog_unref(struct watchdog_device *wddev) +{ + struct sch56xx_watchdog_data *data = watchdog_get_drvdata(wddev); + + kref_put(&data->kref, watchdog_release_resources); +} + +static const struct watchdog_ops watchdog_ops = { + .owner = THIS_MODULE, + .start = watchdog_start, + .stop = watchdog_stop, + .ping = watchdog_trigger, + .set_timeout = watchdog_set_timeout, + .ref = watchdog_ref, + .unref = watchdog_unref, +}; + +struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, + u16 addr, u32 revision, struct mutex *io_lock, int check_enabled) +{ + struct sch56xx_watchdog_data *data; + int err, control, output_enable; + + /* Cache the watchdog registers */ + mutex_lock(io_lock); + control = + sch56xx_read_virtual_reg(addr, SCH56XX_REG_WDOG_CONTROL); + output_enable = + sch56xx_read_virtual_reg(addr, SCH56XX_REG_WDOG_OUTPUT_ENABLE); + mutex_unlock(io_lock); + + if (control < 0) + return NULL; + if (output_enable < 0) + return NULL; + if (check_enabled && !(output_enable & SCH56XX_WDOG_OUTPUT_ENABLE)) { + pr_warn("Watchdog not enabled by BIOS, not registering\n"); + return NULL; + } + + data = kzalloc(sizeof(struct sch56xx_watchdog_data), GFP_KERNEL); + if (!data) + return NULL; + + data->addr = addr; + data->io_lock = io_lock; + kref_init(&data->kref); + + strlcpy(data->wdinfo.identity, "sch56xx watchdog", + sizeof(data->wdinfo.identity)); + data->wdinfo.firmware_version = revision; + data->wdinfo.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT; + if (!nowayout) + data->wdinfo.options |= WDIOF_MAGICCLOSE; + + data->wddev.info = &data->wdinfo; + data->wddev.ops = &watchdog_ops; + data->wddev.parent = parent; + data->wddev.timeout = 60; + data->wddev.min_timeout = 1; + data->wddev.max_timeout = 255 * 60; + if (nowayout) + set_bit(WDOG_NO_WAY_OUT, &data->wddev.status); + if (output_enable & SCH56XX_WDOG_OUTPUT_ENABLE) + set_bit(WDOG_ACTIVE, &data->wddev.status); + + /* Since the watchdog uses a downcounter there is no register to read + the BIOS set timeout from (if any was set at all) -> + Choose a preset which will give us a 1 minute timeout */ + if (control & SCH56XX_WDOG_TIME_BASE_SEC) + data->watchdog_preset = 60; /* seconds */ + else + data->watchdog_preset = 1; /* minute */ + + data->watchdog_control = control; + data->watchdog_output_enable = output_enable; + + watchdog_set_drvdata(&data->wddev, data); + err = watchdog_register_device(&data->wddev); + if (err) { + pr_err("Registering watchdog chardev: %d\n", err); + kfree(data); + return NULL; + } + + return data; +} +EXPORT_SYMBOL(sch56xx_watchdog_register); + +void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data) +{ + watchdog_unregister_device(&data->wddev); + kref_put(&data->kref, watchdog_release_resources); + /* Don't touch data after this it may have been free-ed! */ +} +EXPORT_SYMBOL(sch56xx_watchdog_unregister); + +/* + * platform dev find, add and remove functions + */ + +static int __init sch56xx_find(int sioaddr, const char **name) +{ + u8 devid; + unsigned short address; + int err; + + err = superio_enter(sioaddr); + if (err) + return err; + + devid = superio_inb(sioaddr, SIO_REG_DEVID); + switch (devid) { + case SIO_SCH5627_ID: + *name = "sch5627"; + break; + case SIO_SCH5636_ID: + *name = "sch5636"; + break; + default: + pr_debug("Unsupported device id: 0x%02x\n", + (unsigned int)devid); + err = -ENODEV; + goto exit; + } + + superio_select(sioaddr, SIO_SCH56XX_LD_EM); + + if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) { + pr_warn("Device not activated\n"); + err = -ENODEV; + goto exit; + } + + /* + * Warning the order of the low / high byte is the other way around + * as on most other superio devices!! + */ + address = superio_inb(sioaddr, SIO_REG_ADDR) | + superio_inb(sioaddr, SIO_REG_ADDR + 1) << 8; + if (address == 0) { + pr_warn("Base address not set\n"); + err = -ENODEV; + goto exit; + } + err = address; + +exit: + superio_exit(sioaddr); + return err; +} + +static int __init sch56xx_device_add(int address, const char *name) +{ + struct resource res = { + .start = address, + .end = address + REGION_LENGTH - 1, + .flags = IORESOURCE_IO, + }; + int err; + + sch56xx_pdev = platform_device_alloc(name, address); + if (!sch56xx_pdev) + return -ENOMEM; + + res.name = sch56xx_pdev->name; + err = acpi_check_resource_conflict(&res); + if (err) + goto exit_device_put; + + err = platform_device_add_resources(sch56xx_pdev, &res, 1); + if (err) { + pr_err("Device resource addition failed\n"); + goto exit_device_put; + } + + err = platform_device_add(sch56xx_pdev); + if (err) { + pr_err("Device addition failed\n"); + goto exit_device_put; + } + + return 0; + +exit_device_put: + platform_device_put(sch56xx_pdev); + + return err; +} + +static int __init sch56xx_init(void) +{ + int address; + const char *name = NULL; + + address = sch56xx_find(0x4e, &name); + if (address < 0) + address = sch56xx_find(0x2e, &name); + if (address < 0) + return address; + + return sch56xx_device_add(address, name); +} + +static void __exit sch56xx_exit(void) +{ + platform_device_unregister(sch56xx_pdev); +} + +MODULE_DESCRIPTION("SMSC SCH56xx Hardware Monitoring Common Code"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_LICENSE("GPL"); + +module_init(sch56xx_init); +module_exit(sch56xx_exit); diff --git a/drivers/hwmon/sch56xx-common.h b/drivers/hwmon/sch56xx-common.h new file mode 100644 index 00000000000..704ea2c6d28 --- /dev/null +++ b/drivers/hwmon/sch56xx-common.h @@ -0,0 +1,32 @@ +/*************************************************************************** + * Copyright (C) 2010-2012 Hans de Goede <hdegoede@redhat.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/mutex.h> + +struct sch56xx_watchdog_data; + +int sch56xx_read_virtual_reg(u16 addr, u16 reg); +int sch56xx_write_virtual_reg(u16 addr, u16 reg, u8 val); +int sch56xx_read_virtual_reg16(u16 addr, u16 reg); +int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, + int high_nibble); + +struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, + u16 addr, u32 revision, struct mutex *io_lock, int check_enabled); +void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data); diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index 864a371f6eb..97cd45a8432 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -1,6 +1,10 @@ /* * sht15.c - support for the SHT15 Temperature and Humidity Sensor * + * Portions Copyright (c) 2010-2012 Savoir-faire Linux Inc. + * Jerome Oufella <jerome.oufella@savoirfairelinux.com> + * Vivien Didelot <vivien.didelot@savoirfairelinux.com> + * * Copyright (c) 2009 Jonathan Cameron * * Copyright (c) 2007 Wouter Horre @@ -9,16 +13,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * Currently ignoring checksum on readings. - * Default resolution only (14bit temp, 12bit humidity) - * Ignoring battery status. - * Heater not enabled. - * Timings are all conservative. - * - * Data sheet available (1/2009) at - * http://www.sensirion.ch/en/pdf/product_information/Datasheet-humidity-sensor-SHT1x.pdf - * - * Regulator supply name = vcc + * For further information, see the Documentation/hwmon/sht15 file. */ #include <linux/interrupt.h> @@ -29,29 +24,47 @@ #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> #include <linux/mutex.h> +#include <linux/platform_data/sht15.h> #include <linux/platform_device.h> #include <linux/sched.h> #include <linux/delay.h> #include <linux/jiffies.h> #include <linux/err.h> -#include <linux/sht15.h> #include <linux/regulator/consumer.h> -#include <asm/atomic.h> +#include <linux/slab.h> +#include <linux/atomic.h> -#define SHT15_MEASURE_TEMP 3 -#define SHT15_MEASURE_RH 5 +/* Commands */ +#define SHT15_MEASURE_TEMP 0x03 +#define SHT15_MEASURE_RH 0x05 +#define SHT15_WRITE_STATUS 0x06 +#define SHT15_READ_STATUS 0x07 +#define SHT15_SOFT_RESET 0x1E -#define SHT15_READING_NOTHING 0 -#define SHT15_READING_TEMP 1 -#define SHT15_READING_HUMID 2 +/* Min timings */ +#define SHT15_TSCKL 100 /* (nsecs) clock low */ +#define SHT15_TSCKH 100 /* (nsecs) clock high */ +#define SHT15_TSU 150 /* (nsecs) data setup time */ +#define SHT15_TSRST 11 /* (msecs) soft reset time */ -/* Min timings in nsecs */ -#define SHT15_TSCKL 100 /* clock low */ -#define SHT15_TSCKH 100 /* clock high */ -#define SHT15_TSU 150 /* data setup time */ +/* Status Register Bits */ +#define SHT15_STATUS_LOW_RESOLUTION 0x01 +#define SHT15_STATUS_NO_OTP_RELOAD 0x02 +#define SHT15_STATUS_HEATER 0x04 +#define SHT15_STATUS_LOW_BATTERY 0x40 + +/* List of supported chips */ +enum sht15_chips { sht10, sht11, sht15, sht71, sht75 }; + +/* Actions the driver may be doing */ +enum sht15_state { + SHT15_READING_NOTHING, + SHT15_READING_TEMP, + SHT15_READING_HUMID +}; /** - * struct sht15_temppair - elements of voltage dependant temp calc + * struct sht15_temppair - elements of voltage dependent temp calc * @vdd: supply voltage in microvolts * @d1: see data sheet */ @@ -60,9 +73,7 @@ struct sht15_temppair { int d1; }; -/* Table 9 from data sheet - relates temperature calculation - * to supply voltage. - */ +/* Table 9 from datasheet - relates temperature calculation to supply voltage */ static const struct sht15_temppair temppoints[] = { { 2500000, -39400 }, { 3000000, -39600 }, @@ -71,29 +82,70 @@ static const struct sht15_temppair temppoints[] = { { 5000000, -40100 }, }; +/* Table from CRC datasheet, section 2.4 */ +static const u8 sht15_crc8_table[] = { + 0, 49, 98, 83, 196, 245, 166, 151, + 185, 136, 219, 234, 125, 76, 31, 46, + 67, 114, 33, 16, 135, 182, 229, 212, + 250, 203, 152, 169, 62, 15, 92, 109, + 134, 183, 228, 213, 66, 115, 32, 17, + 63, 14, 93, 108, 251, 202, 153, 168, + 197, 244, 167, 150, 1, 48, 99, 82, + 124, 77, 30, 47, 184, 137, 218, 235, + 61, 12, 95, 110, 249, 200, 155, 170, + 132, 181, 230, 215, 64, 113, 34, 19, + 126, 79, 28, 45, 186, 139, 216, 233, + 199, 246, 165, 148, 3, 50, 97, 80, + 187, 138, 217, 232, 127, 78, 29, 44, + 2, 51, 96, 81, 198, 247, 164, 149, + 248, 201, 154, 171, 60, 13, 94, 111, + 65, 112, 35, 18, 133, 180, 231, 214, + 122, 75, 24, 41, 190, 143, 220, 237, + 195, 242, 161, 144, 7, 54, 101, 84, + 57, 8, 91, 106, 253, 204, 159, 174, + 128, 177, 226, 211, 68, 117, 38, 23, + 252, 205, 158, 175, 56, 9, 90, 107, + 69, 116, 39, 22, 129, 176, 227, 210, + 191, 142, 221, 236, 123, 74, 25, 40, + 6, 55, 100, 85, 194, 243, 160, 145, + 71, 118, 37, 20, 131, 178, 225, 208, + 254, 207, 156, 173, 58, 11, 88, 105, + 4, 53, 102, 87, 192, 241, 162, 147, + 189, 140, 223, 238, 121, 72, 27, 42, + 193, 240, 163, 146, 5, 52, 103, 86, + 120, 73, 26, 43, 188, 141, 222, 239, + 130, 179, 224, 209, 70, 119, 36, 21, + 59, 10, 89, 104, 255, 206, 157, 172 +}; + /** * struct sht15_data - device instance specific data - * @pdata: platform data (gpio's etc) - * @read_work: bh of interrupt handler - * @wait_queue: wait queue for getting values from device - * @val_temp: last temperature value read from device - * @val_humid: last humidity value read from device - * @flag: status flag used to identify what the last request was - * @valid: are the current stored values valid (start condition) - * @last_updat: time of last update - * @read_lock: mutex to ensure only one read in progress - * at a time. - * @dev: associate device structure - * @hwmon_dev: device associated with hwmon subsystem - * @reg: associated regulator (if specified) - * @nb: notifier block to handle notifications of voltage changes - * @supply_uV: local copy of supply voltage used to allow - * use of regulator consumer if available - * @supply_uV_valid: indicates that an updated value has not yet - * been obtained from the regulator and so any calculations - * based upon it will be invalid. - * @update_supply_work: work struct that is used to update the supply_uV - * @interrupt_handled: flag used to indicate a hander has been scheduled + * @pdata: platform data (gpio's etc). + * @read_work: bh of interrupt handler. + * @wait_queue: wait queue for getting values from device. + * @val_temp: last temperature value read from device. + * @val_humid: last humidity value read from device. + * @val_status: last status register value read from device. + * @checksum_ok: last value read from the device passed CRC validation. + * @checksumming: flag used to enable the data validation with CRC. + * @state: state identifying the action the driver is doing. + * @measurements_valid: are the current stored measures valid (start condition). + * @status_valid: is the current stored status valid (start condition). + * @last_measurement: time of last measure. + * @last_status: time of last status reading. + * @read_lock: mutex to ensure only one read in progress at a time. + * @dev: associate device structure. + * @hwmon_dev: device associated with hwmon subsystem. + * @reg: associated regulator (if specified). + * @nb: notifier block to handle notifications of voltage + * changes. + * @supply_uv: local copy of supply voltage used to allow use of + * regulator consumer if available. + * @supply_uv_valid: indicates that an updated value has not yet been + * obtained from the regulator and so any calculations + * based upon it will be invalid. + * @update_supply_work: work struct that is used to update the supply_uv. + * @interrupt_handled: flag used to indicate a handler has been scheduled. */ struct sht15_data { struct sht15_platform_data *pdata; @@ -101,30 +153,72 @@ struct sht15_data { wait_queue_head_t wait_queue; uint16_t val_temp; uint16_t val_humid; - u8 flag; - u8 valid; - unsigned long last_updat; + u8 val_status; + bool checksum_ok; + bool checksumming; + enum sht15_state state; + bool measurements_valid; + bool status_valid; + unsigned long last_measurement; + unsigned long last_status; struct mutex read_lock; struct device *dev; struct device *hwmon_dev; struct regulator *reg; struct notifier_block nb; - int supply_uV; - int supply_uV_valid; + int supply_uv; + bool supply_uv_valid; struct work_struct update_supply_work; atomic_t interrupt_handled; }; /** + * sht15_reverse() - reverse a byte + * @byte: byte to reverse. + */ +static u8 sht15_reverse(u8 byte) +{ + u8 i, c; + + for (c = 0, i = 0; i < 8; i++) + c |= (!!(byte & (1 << i))) << (7 - i); + return c; +} + +/** + * sht15_crc8() - compute crc8 + * @data: sht15 specific data. + * @value: sht15 retrieved data. + * + * This implements section 2 of the CRC datasheet. + */ +static u8 sht15_crc8(struct sht15_data *data, + const u8 *value, + int len) +{ + u8 crc = sht15_reverse(data->val_status & 0x0F); + + while (len--) { + crc = sht15_crc8_table[*value ^ crc]; + value++; + } + + return crc; +} + +/** * sht15_connection_reset() - reset the comms interface * @data: sht15 specific data * * This implements section 3.4 of the data sheet */ -static void sht15_connection_reset(struct sht15_data *data) +static int sht15_connection_reset(struct sht15_data *data) { - int i; - gpio_direction_output(data->pdata->gpio_data, 1); + int i, err; + + err = gpio_direction_output(data->pdata->gpio_data, 1); + if (err) + return err; ndelay(SHT15_TSCKL); gpio_set_value(data->pdata->gpio_sck, 0); ndelay(SHT15_TSCKL); @@ -134,15 +228,16 @@ static void sht15_connection_reset(struct sht15_data *data) gpio_set_value(data->pdata->gpio_sck, 0); ndelay(SHT15_TSCKL); } + return 0; } + /** * sht15_send_bit() - send an individual bit to the device * @data: device state data * @val: value of bit to be sent - **/ + */ static inline void sht15_send_bit(struct sht15_data *data, int val) { - gpio_set_value(data->pdata->gpio_data, val); ndelay(SHT15_TSU); gpio_set_value(data->pdata->gpio_sck, 1); @@ -153,16 +248,20 @@ static inline void sht15_send_bit(struct sht15_data *data, int val) /** * sht15_transmission_start() - specific sequence for new transmission - * * @data: device state data + * * Timings for this are not documented on the data sheet, so very * conservative ones used in implementation. This implements * figure 12 on the data sheet. - **/ -static void sht15_transmission_start(struct sht15_data *data) + */ +static int sht15_transmission_start(struct sht15_data *data) { + int err; + /* ensure data is high and output */ - gpio_direction_output(data->pdata->gpio_data, 1); + err = gpio_direction_output(data->pdata->gpio_data, 1); + if (err) + return err; ndelay(SHT15_TSU); gpio_set_value(data->pdata->gpio_sck, 0); ndelay(SHT15_TSCKL); @@ -178,33 +277,43 @@ static void sht15_transmission_start(struct sht15_data *data) ndelay(SHT15_TSU); gpio_set_value(data->pdata->gpio_sck, 0); ndelay(SHT15_TSCKL); + return 0; } + /** * sht15_send_byte() - send a single byte to the device * @data: device state * @byte: value to be sent - **/ + */ static void sht15_send_byte(struct sht15_data *data, u8 byte) { int i; + for (i = 0; i < 8; i++) { sht15_send_bit(data, !!(byte & 0x80)); byte <<= 1; } } + /** * sht15_wait_for_response() - checks for ack from device * @data: device state - **/ + */ static int sht15_wait_for_response(struct sht15_data *data) { - gpio_direction_input(data->pdata->gpio_data); + int err; + + err = gpio_direction_input(data->pdata->gpio_data); + if (err) + return err; gpio_set_value(data->pdata->gpio_sck, 1); ndelay(SHT15_TSCKH); if (gpio_get_value(data->pdata->gpio_data)) { gpio_set_value(data->pdata->gpio_sck, 0); dev_err(data->dev, "Command not acknowledged\n"); - sht15_connection_reset(data); + err = sht15_connection_reset(data); + if (err) + return err; return -EIO; } gpio_set_value(data->pdata->gpio_sck, 0); @@ -219,78 +328,294 @@ static int sht15_wait_for_response(struct sht15_data *data) * * On entry, sck is output low, data is output pull high * and the interrupt disabled. - **/ + */ static int sht15_send_cmd(struct sht15_data *data, u8 cmd) { - int ret = 0; - sht15_transmission_start(data); + int err; + + err = sht15_transmission_start(data); + if (err) + return err; sht15_send_byte(data, cmd); - ret = sht15_wait_for_response(data); + return sht15_wait_for_response(data); +} + +/** + * sht15_soft_reset() - send a soft reset command + * @data: sht15 specific data. + * + * As described in section 3.2 of the datasheet. + */ +static int sht15_soft_reset(struct sht15_data *data) +{ + int ret; + + ret = sht15_send_cmd(data, SHT15_SOFT_RESET); + if (ret) + return ret; + msleep(SHT15_TSRST); + /* device resets default hardware status register value */ + data->val_status = 0; + return ret; } + /** - * sht15_update_single_val() - get a new value from device + * sht15_ack() - send a ack + * @data: sht15 specific data. + * + * Each byte of data is acknowledged by pulling the data line + * low for one clock pulse. + */ +static int sht15_ack(struct sht15_data *data) +{ + int err; + + err = gpio_direction_output(data->pdata->gpio_data, 0); + if (err) + return err; + ndelay(SHT15_TSU); + gpio_set_value(data->pdata->gpio_sck, 1); + ndelay(SHT15_TSU); + gpio_set_value(data->pdata->gpio_sck, 0); + ndelay(SHT15_TSU); + gpio_set_value(data->pdata->gpio_data, 1); + + return gpio_direction_input(data->pdata->gpio_data); +} + +/** + * sht15_end_transmission() - notify device of end of transmission + * @data: device state. + * + * This is basically a NAK (single clock pulse, data high). + */ +static int sht15_end_transmission(struct sht15_data *data) +{ + int err; + + err = gpio_direction_output(data->pdata->gpio_data, 1); + if (err) + return err; + ndelay(SHT15_TSU); + gpio_set_value(data->pdata->gpio_sck, 1); + ndelay(SHT15_TSCKH); + gpio_set_value(data->pdata->gpio_sck, 0); + ndelay(SHT15_TSCKL); + return 0; +} + +/** + * sht15_read_byte() - Read a byte back from the device + * @data: device state. + */ +static u8 sht15_read_byte(struct sht15_data *data) +{ + int i; + u8 byte = 0; + + for (i = 0; i < 8; ++i) { + byte <<= 1; + gpio_set_value(data->pdata->gpio_sck, 1); + ndelay(SHT15_TSCKH); + byte |= !!gpio_get_value(data->pdata->gpio_data); + gpio_set_value(data->pdata->gpio_sck, 0); + ndelay(SHT15_TSCKL); + } + return byte; +} + +/** + * sht15_send_status() - write the status register byte + * @data: sht15 specific data. + * @status: the byte to set the status register with. + * + * As described in figure 14 and table 5 of the datasheet. + */ +static int sht15_send_status(struct sht15_data *data, u8 status) +{ + int err; + + err = sht15_send_cmd(data, SHT15_WRITE_STATUS); + if (err) + return err; + err = gpio_direction_output(data->pdata->gpio_data, 1); + if (err) + return err; + ndelay(SHT15_TSU); + sht15_send_byte(data, status); + err = sht15_wait_for_response(data); + if (err) + return err; + + data->val_status = status; + return 0; +} + +/** + * sht15_update_status() - get updated status register from device if too old + * @data: device instance specific data. + * + * As described in figure 15 and table 5 of the datasheet. + */ +static int sht15_update_status(struct sht15_data *data) +{ + int ret = 0; + u8 status; + u8 previous_config; + u8 dev_checksum = 0; + u8 checksum_vals[2]; + int timeout = HZ; + + mutex_lock(&data->read_lock); + if (time_after(jiffies, data->last_status + timeout) + || !data->status_valid) { + ret = sht15_send_cmd(data, SHT15_READ_STATUS); + if (ret) + goto unlock; + status = sht15_read_byte(data); + + if (data->checksumming) { + sht15_ack(data); + dev_checksum = sht15_reverse(sht15_read_byte(data)); + checksum_vals[0] = SHT15_READ_STATUS; + checksum_vals[1] = status; + data->checksum_ok = (sht15_crc8(data, checksum_vals, 2) + == dev_checksum); + } + + ret = sht15_end_transmission(data); + if (ret) + goto unlock; + + /* + * Perform checksum validation on the received data. + * Specification mentions that in case a checksum verification + * fails, a soft reset command must be sent to the device. + */ + if (data->checksumming && !data->checksum_ok) { + previous_config = data->val_status & 0x07; + ret = sht15_soft_reset(data); + if (ret) + goto unlock; + if (previous_config) { + ret = sht15_send_status(data, previous_config); + if (ret) { + dev_err(data->dev, + "CRC validation failed, unable " + "to restore device settings\n"); + goto unlock; + } + } + ret = -EAGAIN; + goto unlock; + } + + data->val_status = status; + data->status_valid = true; + data->last_status = jiffies; + } + +unlock: + mutex_unlock(&data->read_lock); + return ret; +} + +/** + * sht15_measurement() - get a new value from device * @data: device instance specific data * @command: command sent to request value * @timeout_msecs: timeout after which comms are assumed * to have failed are reset. - **/ -static inline int sht15_update_single_val(struct sht15_data *data, - int command, - int timeout_msecs) + */ +static int sht15_measurement(struct sht15_data *data, + int command, + int timeout_msecs) { int ret; + u8 previous_config; + ret = sht15_send_cmd(data, command); if (ret) return ret; - gpio_direction_input(data->pdata->gpio_data); + ret = gpio_direction_input(data->pdata->gpio_data); + if (ret) + return ret; atomic_set(&data->interrupt_handled, 0); enable_irq(gpio_to_irq(data->pdata->gpio_data)); if (gpio_get_value(data->pdata->gpio_data) == 0) { disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data)); - /* Only relevant if the interrupt hasn't occured. */ + /* Only relevant if the interrupt hasn't occurred. */ if (!atomic_read(&data->interrupt_handled)) schedule_work(&data->read_work); } ret = wait_event_timeout(data->wait_queue, - (data->flag == SHT15_READING_NOTHING), + (data->state == SHT15_READING_NOTHING), msecs_to_jiffies(timeout_msecs)); - if (ret == 0) {/* timeout occurred */ + if (data->state != SHT15_READING_NOTHING) { /* I/O error occurred */ + data->state = SHT15_READING_NOTHING; + return -EIO; + } else if (ret == 0) { /* timeout occurred */ disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data)); - sht15_connection_reset(data); + ret = sht15_connection_reset(data); + if (ret) + return ret; return -ETIME; } + + /* + * Perform checksum validation on the received data. + * Specification mentions that in case a checksum verification fails, + * a soft reset command must be sent to the device. + */ + if (data->checksumming && !data->checksum_ok) { + previous_config = data->val_status & 0x07; + ret = sht15_soft_reset(data); + if (ret) + return ret; + if (previous_config) { + ret = sht15_send_status(data, previous_config); + if (ret) { + dev_err(data->dev, + "CRC validation failed, unable " + "to restore device settings\n"); + return ret; + } + } + return -EAGAIN; + } + return 0; } /** - * sht15_update_vals() - get updated readings from device if too old + * sht15_update_measurements() - get updated measures from device if too old * @data: device state - **/ -static int sht15_update_vals(struct sht15_data *data) + */ +static int sht15_update_measurements(struct sht15_data *data) { int ret = 0; int timeout = HZ; mutex_lock(&data->read_lock); - if (time_after(jiffies, data->last_updat + timeout) - || !data->valid) { - data->flag = SHT15_READING_HUMID; - ret = sht15_update_single_val(data, SHT15_MEASURE_RH, 160); + if (time_after(jiffies, data->last_measurement + timeout) + || !data->measurements_valid) { + data->state = SHT15_READING_HUMID; + ret = sht15_measurement(data, SHT15_MEASURE_RH, 160); if (ret) - goto error_ret; - data->flag = SHT15_READING_TEMP; - ret = sht15_update_single_val(data, SHT15_MEASURE_TEMP, 400); + goto unlock; + data->state = SHT15_READING_TEMP; + ret = sht15_measurement(data, SHT15_MEASURE_TEMP, 400); if (ret) - goto error_ret; - data->valid = 1; - data->last_updat = jiffies; + goto unlock; + data->measurements_valid = true; + data->last_measurement = jiffies; } -error_ret: - mutex_unlock(&data->read_lock); +unlock: + mutex_unlock(&data->read_lock); return ret; } @@ -299,23 +624,24 @@ error_ret: * @data: device state * * As per section 4.3 of the data sheet. - **/ + */ static inline int sht15_calc_temp(struct sht15_data *data) { - int d1 = 0; + int d1 = temppoints[0].d1; + int d2 = (data->val_status & SHT15_STATUS_LOW_RESOLUTION) ? 40 : 10; int i; - for (i = 1; i < ARRAY_SIZE(temppoints); i++) + for (i = ARRAY_SIZE(temppoints) - 1; i > 0; i--) /* Find pointer to interpolate */ - if (data->supply_uV > temppoints[i - 1].vdd) { - d1 = (data->supply_uV/1000 - temppoints[i - 1].vdd) + if (data->supply_uv > temppoints[i - 1].vdd) { + d1 = (data->supply_uv - temppoints[i - 1].vdd) * (temppoints[i].d1 - temppoints[i - 1].d1) / (temppoints[i].vdd - temppoints[i - 1].vdd) + temppoints[i - 1].d1; break; } - return data->val_temp*10 + d1; + return data->val_temp * d2 + d1; } /** @@ -324,23 +650,102 @@ static inline int sht15_calc_temp(struct sht15_data *data) * * This is the temperature compensated version as per section 4.2 of * the data sheet. - **/ + * + * The sensor is assumed to be V3, which is compatible with V4. + * Humidity conversion coefficients are shown in table 7 of the datasheet. + */ static inline int sht15_calc_humid(struct sht15_data *data) { - int RHlinear; /* milli percent */ + int rh_linear; /* milli percent */ int temp = sht15_calc_temp(data); - + int c2, c3; + int t2; const int c1 = -4; - const int c2 = 40500; /* x 10 ^ -6 */ - const int c3 = -2800; /* x10 ^ -9 */ - - RHlinear = c1*1000 - + c2 * data->val_humid/1000 - + (data->val_humid * data->val_humid * c3)/1000000; - return (temp - 25000) * (10000 + 80 * data->val_humid) - / 1000000 + RHlinear; + + if (data->val_status & SHT15_STATUS_LOW_RESOLUTION) { + c2 = 648000; /* x 10 ^ -6 */ + c3 = -7200; /* x 10 ^ -7 */ + t2 = 1280; + } else { + c2 = 40500; /* x 10 ^ -6 */ + c3 = -28; /* x 10 ^ -7 */ + t2 = 80; + } + + rh_linear = c1 * 1000 + + c2 * data->val_humid / 1000 + + (data->val_humid * data->val_humid * c3) / 10000; + return (temp - 25000) * (10000 + t2 * data->val_humid) + / 1000000 + rh_linear; +} + +/** + * sht15_show_status() - show status information in sysfs + * @dev: device. + * @attr: device attribute. + * @buf: sysfs buffer where information is written to. + * + * Will be called on read access to temp1_fault, humidity1_fault + * and heater_enable sysfs attributes. + * Returns number of bytes written into buffer, negative errno on error. + */ +static ssize_t sht15_show_status(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + struct sht15_data *data = dev_get_drvdata(dev); + u8 bit = to_sensor_dev_attr(attr)->index; + + ret = sht15_update_status(data); + + return ret ? ret : sprintf(buf, "%d\n", !!(data->val_status & bit)); +} + +/** + * sht15_store_heater() - change heater state via sysfs + * @dev: device. + * @attr: device attribute. + * @buf: sysfs buffer to read the new heater state from. + * @count: length of the data. + * + * Will be called on write access to heater_enable sysfs attribute. + * Returns number of bytes actually decoded, negative errno on error. + */ +static ssize_t sht15_store_heater(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct sht15_data *data = dev_get_drvdata(dev); + long value; + u8 status; + + if (kstrtol(buf, 10, &value)) + return -EINVAL; + + mutex_lock(&data->read_lock); + status = data->val_status & 0x07; + if (!!value) + status |= SHT15_STATUS_HEATER; + else + status &= ~SHT15_STATUS_HEATER; + + ret = sht15_send_status(data, status); + mutex_unlock(&data->read_lock); + + return ret ? ret : count; } +/** + * sht15_show_temp() - show temperature measurement value in sysfs + * @dev: device. + * @attr: device attribute. + * @buf: sysfs buffer where measurement values are written to. + * + * Will be called on read access to temp1_input sysfs attribute. + * Returns number of bytes written into buffer, negative errno on error. + */ static ssize_t sht15_show_temp(struct device *dev, struct device_attribute *attr, char *buf) @@ -349,12 +754,21 @@ static ssize_t sht15_show_temp(struct device *dev, struct sht15_data *data = dev_get_drvdata(dev); /* Technically no need to read humidity as well */ - ret = sht15_update_vals(data); + ret = sht15_update_measurements(data); return ret ? ret : sprintf(buf, "%d\n", sht15_calc_temp(data)); } +/** + * sht15_show_humidity() - show humidity measurement value in sysfs + * @dev: device. + * @attr: device attribute. + * @buf: sysfs buffer where measurement values are written to. + * + * Will be called on read access to humidity1_input sysfs attribute. + * Returns number of bytes written into buffer, negative errno on error. + */ static ssize_t sht15_show_humidity(struct device *dev, struct device_attribute *attr, char *buf) @@ -362,11 +776,11 @@ static ssize_t sht15_show_humidity(struct device *dev, int ret; struct sht15_data *data = dev_get_drvdata(dev); - ret = sht15_update_vals(data); + ret = sht15_update_measurements(data); return ret ? ret : sprintf(buf, "%d\n", sht15_calc_humid(data)); +} -}; static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) @@ -375,16 +789,23 @@ static ssize_t show_name(struct device *dev, return sprintf(buf, "%s\n", pdev->name); } -static SENSOR_DEVICE_ATTR(temp1_input, - S_IRUGO, sht15_show_temp, - NULL, 0); -static SENSOR_DEVICE_ATTR(humidity1_input, - S_IRUGO, sht15_show_humidity, - NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, + sht15_show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO, + sht15_show_humidity, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, sht15_show_status, NULL, + SHT15_STATUS_LOW_BATTERY); +static SENSOR_DEVICE_ATTR(humidity1_fault, S_IRUGO, sht15_show_status, NULL, + SHT15_STATUS_LOW_BATTERY); +static SENSOR_DEVICE_ATTR(heater_enable, S_IRUGO | S_IWUSR, sht15_show_status, + sht15_store_heater, SHT15_STATUS_HEATER); static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); static struct attribute *sht15_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_humidity1_input.dev_attr.attr, + &sensor_dev_attr_temp1_fault.dev_attr.attr, + &sensor_dev_attr_humidity1_fault.dev_attr.attr, + &sensor_dev_attr_heater_enable.dev_attr.attr, &dev_attr_name.attr, NULL, }; @@ -396,90 +817,79 @@ static const struct attribute_group sht15_attr_group = { static irqreturn_t sht15_interrupt_fired(int irq, void *d) { struct sht15_data *data = d; + /* First disable the interrupt */ disable_irq_nosync(irq); atomic_inc(&data->interrupt_handled); /* Then schedule a reading work struct */ - if (data->flag != SHT15_READING_NOTHING) + if (data->state != SHT15_READING_NOTHING) schedule_work(&data->read_work); return IRQ_HANDLED; } -/* Each byte of data is acknowledged by pulling the data line - * low for one clock pulse. - */ -static void sht15_ack(struct sht15_data *data) -{ - gpio_direction_output(data->pdata->gpio_data, 0); - ndelay(SHT15_TSU); - gpio_set_value(data->pdata->gpio_sck, 1); - ndelay(SHT15_TSU); - gpio_set_value(data->pdata->gpio_sck, 0); - ndelay(SHT15_TSU); - gpio_set_value(data->pdata->gpio_data, 1); - - gpio_direction_input(data->pdata->gpio_data); -} -/** - * sht15_end_transmission() - notify device of end of transmission - * @data: device state - * - * This is basically a NAK. (single clock pulse, data high) - **/ -static void sht15_end_transmission(struct sht15_data *data) -{ - gpio_direction_output(data->pdata->gpio_data, 1); - ndelay(SHT15_TSU); - gpio_set_value(data->pdata->gpio_sck, 1); - ndelay(SHT15_TSCKH); - gpio_set_value(data->pdata->gpio_sck, 0); - ndelay(SHT15_TSCKL); -} - static void sht15_bh_read_data(struct work_struct *work_s) { - int i; uint16_t val = 0; + u8 dev_checksum = 0; + u8 checksum_vals[3]; struct sht15_data *data = container_of(work_s, struct sht15_data, read_work); + /* Firstly, verify the line is low */ if (gpio_get_value(data->pdata->gpio_data)) { - /* If not, then start the interrupt again - care - here as could have gone low in meantime so verify - it hasn't! - */ + /* + * If not, then start the interrupt again - care here as could + * have gone low in meantime so verify it hasn't! + */ atomic_set(&data->interrupt_handled, 0); enable_irq(gpio_to_irq(data->pdata->gpio_data)); - /* If still not occured or another handler has been scheduled */ + /* If still not occurred or another handler was scheduled */ if (gpio_get_value(data->pdata->gpio_data) || atomic_read(&data->interrupt_handled)) return; } + /* Read the data back from the device */ - for (i = 0; i < 16; ++i) { - val <<= 1; - gpio_set_value(data->pdata->gpio_sck, 1); - ndelay(SHT15_TSCKH); - val |= !!gpio_get_value(data->pdata->gpio_data); - gpio_set_value(data->pdata->gpio_sck, 0); - ndelay(SHT15_TSCKL); - if (i == 7) - sht15_ack(data); + val = sht15_read_byte(data); + val <<= 8; + if (sht15_ack(data)) + goto wakeup; + val |= sht15_read_byte(data); + + if (data->checksumming) { + /* + * Ask the device for a checksum and read it back. + * Note: the device sends the checksum byte reversed. + */ + if (sht15_ack(data)) + goto wakeup; + dev_checksum = sht15_reverse(sht15_read_byte(data)); + checksum_vals[0] = (data->state == SHT15_READING_TEMP) ? + SHT15_MEASURE_TEMP : SHT15_MEASURE_RH; + checksum_vals[1] = (u8) (val >> 8); + checksum_vals[2] = (u8) val; + data->checksum_ok + = (sht15_crc8(data, checksum_vals, 3) == dev_checksum); } + /* Tell the device we are done */ - sht15_end_transmission(data); + if (sht15_end_transmission(data)) + goto wakeup; - switch (data->flag) { + switch (data->state) { case SHT15_READING_TEMP: data->val_temp = val; break; case SHT15_READING_HUMID: data->val_humid = val; break; + default: + break; } - data->flag = SHT15_READING_NOTHING; + data->state = SHT15_READING_NOTHING; +wakeup: wake_up(&data->wait_queue); } @@ -488,7 +898,7 @@ static void sht15_update_voltage(struct work_struct *work_s) struct sht15_data *data = container_of(work_s, struct sht15_data, update_supply_work); - data->supply_uV = regulator_get_voltage(data->reg); + data->supply_uv = regulator_get_voltage(data->reg); } /** @@ -499,30 +909,29 @@ static void sht15_update_voltage(struct work_struct *work_s) * * Note that as the notification code holds the regulator lock, we have * to schedule an update of the supply voltage rather than getting it directly. - **/ + */ static int sht15_invalidate_voltage(struct notifier_block *nb, - unsigned long event, - void *ignored) + unsigned long event, + void *ignored) { struct sht15_data *data = container_of(nb, struct sht15_data, nb); if (event == REGULATOR_EVENT_VOLTAGE_CHANGE) - data->supply_uV_valid = false; + data->supply_uv_valid = false; schedule_work(&data->update_supply_work); return NOTIFY_OK; } -static int __devinit sht15_probe(struct platform_device *pdev) +static int sht15_probe(struct platform_device *pdev) { - int ret = 0; - struct sht15_data *data = kzalloc(sizeof(*data), GFP_KERNEL); + int ret; + struct sht15_data *data; + u8 status = 0; - if (!data) { - ret = -ENOMEM; - dev_err(&pdev->dev, "kzalloc failed"); - goto error_ret; - } + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; INIT_WORK(&data->read_work, sht15_bh_read_data); INIT_WORK(&data->update_supply_work, sht15_update_voltage); @@ -531,170 +940,160 @@ static int __devinit sht15_probe(struct platform_device *pdev) data->dev = &pdev->dev; init_waitqueue_head(&data->wait_queue); - if (pdev->dev.platform_data == NULL) { - dev_err(&pdev->dev, "no platform data supplied"); - goto err_free_data; + if (dev_get_platdata(&pdev->dev) == NULL) { + dev_err(&pdev->dev, "no platform data supplied\n"); + return -EINVAL; } - data->pdata = pdev->dev.platform_data; - data->supply_uV = data->pdata->supply_mv*1000; + data->pdata = dev_get_platdata(&pdev->dev); + data->supply_uv = data->pdata->supply_mv * 1000; + if (data->pdata->checksum) + data->checksumming = true; + if (data->pdata->no_otp_reload) + status |= SHT15_STATUS_NO_OTP_RELOAD; + if (data->pdata->low_resolution) + status |= SHT15_STATUS_LOW_RESOLUTION; -/* If a regulator is available, query what the supply voltage actually is!*/ - data->reg = regulator_get(data->dev, "vcc"); + /* + * If a regulator is available, + * query what the supply voltage actually is! + */ + data->reg = devm_regulator_get_optional(data->dev, "vcc"); if (!IS_ERR(data->reg)) { - data->supply_uV = regulator_get_voltage(data->reg); - regulator_enable(data->reg); - /* setup a notifier block to update this if another device - * causes the voltage to change */ + int voltage; + + voltage = regulator_get_voltage(data->reg); + if (voltage) + data->supply_uv = voltage; + + ret = regulator_enable(data->reg); + if (ret != 0) { + dev_err(&pdev->dev, + "failed to enable regulator: %d\n", ret); + return ret; + } + + /* + * Setup a notifier block to update this if another device + * causes the voltage to change + */ data->nb.notifier_call = &sht15_invalidate_voltage; ret = regulator_register_notifier(data->reg, &data->nb); + if (ret) { + dev_err(&pdev->dev, + "regulator notifier request failed\n"); + regulator_disable(data->reg); + return ret; + } } -/* Try requesting the GPIOs */ - ret = gpio_request(data->pdata->gpio_sck, "SHT15 sck"); + + /* Try requesting the GPIOs */ + ret = devm_gpio_request_one(&pdev->dev, data->pdata->gpio_sck, + GPIOF_OUT_INIT_LOW, "SHT15 sck"); if (ret) { - dev_err(&pdev->dev, "gpio request failed"); - goto err_free_data; + dev_err(&pdev->dev, "clock line GPIO request failed\n"); + goto err_release_reg; } - gpio_direction_output(data->pdata->gpio_sck, 0); - ret = gpio_request(data->pdata->gpio_data, "SHT15 data"); + + ret = devm_gpio_request(&pdev->dev, data->pdata->gpio_data, + "SHT15 data"); if (ret) { - dev_err(&pdev->dev, "gpio request failed"); - goto err_release_gpio_sck; + dev_err(&pdev->dev, "data line GPIO request failed\n"); + goto err_release_reg; } - ret = sysfs_create_group(&pdev->dev.kobj, &sht15_attr_group); + + ret = devm_request_irq(&pdev->dev, gpio_to_irq(data->pdata->gpio_data), + sht15_interrupt_fired, + IRQF_TRIGGER_FALLING, + "sht15 data", + data); if (ret) { - dev_err(&pdev->dev, "sysfs create failed"); - goto err_release_gpio_data; + dev_err(&pdev->dev, "failed to get irq for data line\n"); + goto err_release_reg; } + disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data)); + ret = sht15_connection_reset(data); + if (ret) + goto err_release_reg; + ret = sht15_soft_reset(data); + if (ret) + goto err_release_reg; - ret = request_irq(gpio_to_irq(data->pdata->gpio_data), - sht15_interrupt_fired, - IRQF_TRIGGER_FALLING, - "sht15 data", - data); + /* write status with platform data options */ + if (status) { + ret = sht15_send_status(data, status); + if (ret) + goto err_release_reg; + } + + ret = sysfs_create_group(&pdev->dev.kobj, &sht15_attr_group); if (ret) { - dev_err(&pdev->dev, "failed to get irq for data line"); - goto err_release_gpio_data; + dev_err(&pdev->dev, "sysfs create failed\n"); + goto err_release_reg; } - disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data)); - sht15_connection_reset(data); - sht15_send_cmd(data, 0x1E); data->hwmon_dev = hwmon_device_register(data->dev); if (IS_ERR(data->hwmon_dev)) { ret = PTR_ERR(data->hwmon_dev); - goto err_release_irq; + goto err_release_sysfs_group; } - return 0; -err_release_irq: - free_irq(gpio_to_irq(data->pdata->gpio_data), data); -err_release_gpio_data: - gpio_free(data->pdata->gpio_data); -err_release_gpio_sck: - gpio_free(data->pdata->gpio_sck); -err_free_data: - kfree(data); -error_ret: + return 0; +err_release_sysfs_group: + sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group); +err_release_reg: + if (!IS_ERR(data->reg)) { + regulator_unregister_notifier(data->reg, &data->nb); + regulator_disable(data->reg); + } return ret; } -static int __devexit sht15_remove(struct platform_device *pdev) +static int sht15_remove(struct platform_device *pdev) { struct sht15_data *data = platform_get_drvdata(pdev); - /* Make sure any reads from the device are done and - * prevent new ones beginnning */ + /* + * Make sure any reads from the device are done and + * prevent new ones beginning + */ mutex_lock(&data->read_lock); + if (sht15_soft_reset(data)) { + mutex_unlock(&data->read_lock); + return -EFAULT; + } hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group); if (!IS_ERR(data->reg)) { regulator_unregister_notifier(data->reg, &data->nb); regulator_disable(data->reg); - regulator_put(data->reg); } - free_irq(gpio_to_irq(data->pdata->gpio_data), data); - gpio_free(data->pdata->gpio_data); - gpio_free(data->pdata->gpio_sck); mutex_unlock(&data->read_lock); - kfree(data); + return 0; } +static struct platform_device_id sht15_device_ids[] = { + { "sht10", sht10 }, + { "sht11", sht11 }, + { "sht15", sht15 }, + { "sht71", sht71 }, + { "sht75", sht75 }, + { } +}; +MODULE_DEVICE_TABLE(platform, sht15_device_ids); -/* - * sht_drivers simultaneously refers to __devinit and __devexit function - * which causes spurious section mismatch warning. So use __refdata to - * get rid from this. - */ -static struct platform_driver __refdata sht_drivers[] = { - { - .driver = { - .name = "sht10", - .owner = THIS_MODULE, - }, - .probe = sht15_probe, - .remove = __devexit_p(sht15_remove), - }, { - .driver = { - .name = "sht11", - .owner = THIS_MODULE, - }, - .probe = sht15_probe, - .remove = __devexit_p(sht15_remove), - }, { - .driver = { - .name = "sht15", - .owner = THIS_MODULE, - }, - .probe = sht15_probe, - .remove = __devexit_p(sht15_remove), - }, { - .driver = { - .name = "sht71", - .owner = THIS_MODULE, - }, - .probe = sht15_probe, - .remove = __devexit_p(sht15_remove), - }, { - .driver = { - .name = "sht75", - .owner = THIS_MODULE, - }, - .probe = sht15_probe, - .remove = __devexit_p(sht15_remove), +static struct platform_driver sht15_driver = { + .driver = { + .name = "sht15", + .owner = THIS_MODULE, }, + .probe = sht15_probe, + .remove = sht15_remove, + .id_table = sht15_device_ids, }; - - -static int __init sht15_init(void) -{ - int ret; - int i; - - for (i = 0; i < ARRAY_SIZE(sht_drivers); i++) { - ret = platform_driver_register(&sht_drivers[i]); - if (ret) - goto error_unreg; - } - - return 0; - -error_unreg: - while (--i >= 0) - platform_driver_unregister(&sht_drivers[i]); - - return ret; -} -module_init(sht15_init); - -static void __exit sht15_exit(void) -{ - int i; - for (i = ARRAY_SIZE(sht_drivers) - 1; i >= 0; i--) - platform_driver_unregister(&sht_drivers[i]); -} -module_exit(sht15_exit); +module_platform_driver(sht15_driver); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Sensirion SHT15 temperature and humidity sensor driver"); diff --git a/drivers/hwmon/sht21.c b/drivers/hwmon/sht21.c new file mode 100644 index 00000000000..2e9f9570b6f --- /dev/null +++ b/drivers/hwmon/sht21.c @@ -0,0 +1,264 @@ +/* Sensirion SHT21 humidity and temperature sensor driver + * + * Copyright (C) 2010 Urs Fleisch <urs.fleisch@sensirion.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., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA + * + * Data sheet available (5/2010) at + * http://www.sensirion.com/en/pdf/product_information/Datasheet-humidity-sensor-SHT21.pdf + */ + +#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/device.h> +#include <linux/jiffies.h> + +/* I2C command bytes */ +#define SHT21_TRIG_T_MEASUREMENT_HM 0xe3 +#define SHT21_TRIG_RH_MEASUREMENT_HM 0xe5 + +/** + * struct sht21 - SHT21 device specific data + * @hwmon_dev: device registered with hwmon + * @lock: mutex to protect measurement values + * @valid: only 0 before first measurement is taken + * @last_update: time of last update (jiffies) + * @temperature: cached temperature measurement value + * @humidity: cached humidity measurement value + */ +struct sht21 { + struct device *hwmon_dev; + struct mutex lock; + char valid; + unsigned long last_update; + int temperature; + int humidity; +}; + +/** + * sht21_temp_ticks_to_millicelsius() - convert raw temperature ticks to + * milli celsius + * @ticks: temperature ticks value received from sensor + */ +static inline int sht21_temp_ticks_to_millicelsius(int ticks) +{ + ticks &= ~0x0003; /* clear status bits */ + /* + * Formula T = -46.85 + 175.72 * ST / 2^16 from data sheet 6.2, + * optimized for integer fixed point (3 digits) arithmetic + */ + return ((21965 * ticks) >> 13) - 46850; +} + +/** + * sht21_rh_ticks_to_per_cent_mille() - convert raw humidity ticks to + * one-thousandths of a percent relative humidity + * @ticks: humidity ticks value received from sensor + */ +static inline int sht21_rh_ticks_to_per_cent_mille(int ticks) +{ + ticks &= ~0x0003; /* clear status bits */ + /* + * Formula RH = -6 + 125 * SRH / 2^16 from data sheet 6.1, + * optimized for integer fixed point (3 digits) arithmetic + */ + return ((15625 * ticks) >> 13) - 6000; +} + +/** + * sht21_update_measurements() - get updated measurements from device + * @client: I2C client device + * + * Returns 0 on success, else negative errno. + */ +static int sht21_update_measurements(struct i2c_client *client) +{ + int ret = 0; + struct sht21 *sht21 = i2c_get_clientdata(client); + + mutex_lock(&sht21->lock); + /* + * Data sheet 2.4: + * SHT2x should not be active for more than 10% of the time - e.g. + * maximum two measurements per second at 12bit accuracy shall be made. + */ + if (time_after(jiffies, sht21->last_update + HZ / 2) || !sht21->valid) { + ret = i2c_smbus_read_word_swapped(client, + SHT21_TRIG_T_MEASUREMENT_HM); + if (ret < 0) + goto out; + sht21->temperature = sht21_temp_ticks_to_millicelsius(ret); + ret = i2c_smbus_read_word_swapped(client, + SHT21_TRIG_RH_MEASUREMENT_HM); + if (ret < 0) + goto out; + sht21->humidity = sht21_rh_ticks_to_per_cent_mille(ret); + sht21->last_update = jiffies; + sht21->valid = 1; + } +out: + mutex_unlock(&sht21->lock); + + return ret >= 0 ? 0 : ret; +} + +/** + * sht21_show_temperature() - show temperature measurement value in sysfs + * @dev: device + * @attr: device attribute + * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to + * + * Will be called on read access to temp1_input sysfs attribute. + * Returns number of bytes written into buffer, negative errno on error. + */ +static ssize_t sht21_show_temperature(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sht21 *sht21 = i2c_get_clientdata(client); + int ret = sht21_update_measurements(client); + if (ret < 0) + return ret; + return sprintf(buf, "%d\n", sht21->temperature); +} + +/** + * sht21_show_humidity() - show humidity measurement value in sysfs + * @dev: device + * @attr: device attribute + * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to + * + * Will be called on read access to humidity1_input sysfs attribute. + * Returns number of bytes written into buffer, negative errno on error. + */ +static ssize_t sht21_show_humidity(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sht21 *sht21 = i2c_get_clientdata(client); + int ret = sht21_update_measurements(client); + if (ret < 0) + return ret; + return sprintf(buf, "%d\n", sht21->humidity); +} + +/* sysfs attributes */ +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, sht21_show_temperature, + NULL, 0); +static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO, sht21_show_humidity, + NULL, 0); + +static struct attribute *sht21_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_humidity1_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group sht21_attr_group = { + .attrs = sht21_attributes, +}; + +/** + * sht21_probe() - probe device + * @client: I2C client device + * @id: device ID + * + * Called by the I2C core when an entry in the ID table matches a + * device's name. + * Returns 0 on success. + */ +static int sht21_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct sht21 *sht21; + int err; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WORD_DATA)) { + dev_err(&client->dev, + "adapter does not support SMBus word transactions\n"); + return -ENODEV; + } + + sht21 = devm_kzalloc(&client->dev, sizeof(*sht21), GFP_KERNEL); + if (!sht21) + return -ENOMEM; + + i2c_set_clientdata(client, sht21); + + mutex_init(&sht21->lock); + + err = sysfs_create_group(&client->dev.kobj, &sht21_attr_group); + if (err) { + dev_dbg(&client->dev, "could not create sysfs files\n"); + return err; + } + sht21->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(sht21->hwmon_dev)) { + dev_dbg(&client->dev, "unable to register hwmon device\n"); + err = PTR_ERR(sht21->hwmon_dev); + goto fail_remove_sysfs; + } + + dev_info(&client->dev, "initialized\n"); + + return 0; + +fail_remove_sysfs: + sysfs_remove_group(&client->dev.kobj, &sht21_attr_group); + return err; +} + +/** + * sht21_remove() - remove device + * @client: I2C client device + */ +static int sht21_remove(struct i2c_client *client) +{ + struct sht21 *sht21 = i2c_get_clientdata(client); + + hwmon_device_unregister(sht21->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &sht21_attr_group); + + return 0; +} + +/* Device ID table */ +static const struct i2c_device_id sht21_id[] = { + { "sht21", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sht21_id); + +static struct i2c_driver sht21_driver = { + .driver.name = "sht21", + .probe = sht21_probe, + .remove = sht21_remove, + .id_table = sht21_id, +}; + +module_i2c_driver(sht21_driver); + +MODULE_AUTHOR("Urs Fleisch <urs.fleisch@sensirion.com>"); +MODULE_DESCRIPTION("Sensirion SHT21 humidity and temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/shtc1.c b/drivers/hwmon/shtc1.c new file mode 100644 index 00000000000..decd7df995a --- /dev/null +++ b/drivers/hwmon/shtc1.c @@ -0,0 +1,251 @@ +/* Sensirion SHTC1 humidity and temperature sensor driver + * + * Copyright (C) 2014 Sensirion AG, Switzerland + * Author: Johannes Winkelmann <johannes.winkelmann@sensirion.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/platform_data/shtc1.h> + +/* commands (high precision mode) */ +static const unsigned char shtc1_cmd_measure_blocking_hpm[] = { 0x7C, 0xA2 }; +static const unsigned char shtc1_cmd_measure_nonblocking_hpm[] = { 0x78, 0x66 }; + +/* commands (low precision mode) */ +static const unsigned char shtc1_cmd_measure_blocking_lpm[] = { 0x64, 0x58 }; +static const unsigned char shtc1_cmd_measure_nonblocking_lpm[] = { 0x60, 0x9c }; + +/* command for reading the ID register */ +static const unsigned char shtc1_cmd_read_id_reg[] = { 0xef, 0xc8 }; + +/* constants for reading the ID register */ +#define SHTC1_ID 0x07 +#define SHTC1_ID_REG_MASK 0x1f + +/* delays for non-blocking i2c commands, both in us */ +#define SHTC1_NONBLOCKING_WAIT_TIME_HPM 14400 +#define SHTC1_NONBLOCKING_WAIT_TIME_LPM 1000 + +#define SHTC1_CMD_LENGTH 2 +#define SHTC1_RESPONSE_LENGTH 6 + +struct shtc1_data { + struct i2c_client *client; + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + + const unsigned char *command; + unsigned int nonblocking_wait_time; /* in us */ + + struct shtc1_platform_data setup; + + int temperature; /* 1000 * temperature in dgr C */ + int humidity; /* 1000 * relative humidity in %RH */ +}; + +static int shtc1_update_values(struct i2c_client *client, + struct shtc1_data *data, + char *buf, int bufsize) +{ + int ret = i2c_master_send(client, data->command, SHTC1_CMD_LENGTH); + if (ret != SHTC1_CMD_LENGTH) { + dev_err(&client->dev, "failed to send command: %d\n", ret); + return ret < 0 ? ret : -EIO; + } + + /* + * In blocking mode (clock stretching mode) the I2C bus + * is blocked for other traffic, thus the call to i2c_master_recv() + * will wait until the data is ready. For non blocking mode, we + * have to wait ourselves. + */ + if (!data->setup.blocking_io) + usleep_range(data->nonblocking_wait_time, + data->nonblocking_wait_time + 1000); + + ret = i2c_master_recv(client, buf, bufsize); + if (ret != bufsize) { + dev_err(&client->dev, "failed to read values: %d\n", ret); + return ret < 0 ? ret : -EIO; + } + + return 0; +} + +/* sysfs attributes */ +static struct shtc1_data *shtc1_update_client(struct device *dev) +{ + struct shtc1_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned char buf[SHTC1_RESPONSE_LENGTH]; + int val; + int ret = 0; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ / 10) || !data->valid) { + ret = shtc1_update_values(client, data, buf, sizeof(buf)); + if (ret) + goto out; + + /* + * From datasheet: + * T = -45 + 175 * ST / 2^16 + * RH = 100 * SRH / 2^16 + * + * Adapted for integer fixed point (3 digit) arithmetic. + */ + val = be16_to_cpup((__be16 *)buf); + data->temperature = ((21875 * val) >> 13) - 45000; + val = be16_to_cpup((__be16 *)(buf + 3)); + data->humidity = ((12500 * val) >> 13); + + data->last_updated = jiffies; + data->valid = true; + } + +out: + mutex_unlock(&data->update_lock); + + return ret == 0 ? data : ERR_PTR(ret); +} + +static ssize_t temp1_input_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct shtc1_data *data = shtc1_update_client(dev); + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", data->temperature); +} + +static ssize_t humidity1_input_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct shtc1_data *data = shtc1_update_client(dev); + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", data->humidity); +} + +static DEVICE_ATTR_RO(temp1_input); +static DEVICE_ATTR_RO(humidity1_input); + +static struct attribute *shtc1_attrs[] = { + &dev_attr_temp1_input.attr, + &dev_attr_humidity1_input.attr, + NULL +}; + +ATTRIBUTE_GROUPS(shtc1); + +static void shtc1_select_command(struct shtc1_data *data) +{ + if (data->setup.high_precision) { + data->command = data->setup.blocking_io ? + shtc1_cmd_measure_blocking_hpm : + shtc1_cmd_measure_nonblocking_hpm; + data->nonblocking_wait_time = SHTC1_NONBLOCKING_WAIT_TIME_HPM; + + } else { + data->command = data->setup.blocking_io ? + shtc1_cmd_measure_blocking_lpm : + shtc1_cmd_measure_nonblocking_lpm; + data->nonblocking_wait_time = SHTC1_NONBLOCKING_WAIT_TIME_LPM; + } +} + +static int shtc1_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + char id_reg[2]; + struct shtc1_data *data; + struct device *hwmon_dev; + struct i2c_adapter *adap = client->adapter; + struct device *dev = &client->dev; + + if (!i2c_check_functionality(adap, I2C_FUNC_I2C)) { + dev_err(dev, "plain i2c transactions not supported\n"); + return -ENODEV; + } + + ret = i2c_master_send(client, shtc1_cmd_read_id_reg, SHTC1_CMD_LENGTH); + if (ret != SHTC1_CMD_LENGTH) { + dev_err(dev, "could not send read_id_reg command: %d\n", ret); + return ret < 0 ? ret : -ENODEV; + } + ret = i2c_master_recv(client, id_reg, sizeof(id_reg)); + if (ret != sizeof(id_reg)) { + dev_err(dev, "could not read ID register: %d\n", ret); + return -ENODEV; + } + if ((id_reg[1] & SHTC1_ID_REG_MASK) != SHTC1_ID) { + dev_err(dev, "ID register doesn't match\n"); + return -ENODEV; + } + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->setup.blocking_io = false; + data->setup.high_precision = true; + data->client = client; + + if (client->dev.platform_data) + data->setup = *(struct shtc1_platform_data *)dev->platform_data; + shtc1_select_command(data); + mutex_init(&data->update_lock); + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, + client->name, + data, + shtc1_groups); + if (IS_ERR(hwmon_dev)) + dev_dbg(dev, "unable to register hwmon device\n"); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +/* device ID table */ +static const struct i2c_device_id shtc1_id[] = { + { "shtc1", 0 }, + { "shtw1", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, shtc1_id); + +static struct i2c_driver shtc1_i2c_driver = { + .driver.name = "shtc1", + .probe = shtc1_probe, + .id_table = shtc1_id, +}; + +module_i2c_driver(shtc1_i2c_driver); + +MODULE_AUTHOR("Johannes Winkelmann <johannes.winkelmann@sensirion.com>"); +MODULE_DESCRIPTION("Sensirion SHTC1 humidity and temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c index 79c2931e300..3532026e25d 100644 --- a/drivers/hwmon/sis5595.c +++ b/drivers/hwmon/sis5595.c @@ -1,54 +1,56 @@ /* - sis5595.c - Part of lm_sensors, Linux kernel modules - for hardware monitoring - - Copyright (C) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>, - Kyösti Mälkki <kmalkki@cc.hut.fi>, and - Mark D. Studebaker <mdsxyz123@yahoo.com> - Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with - the help of 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; 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. -*/ + * sis5595.c - Part of lm_sensors, Linux kernel modules + * for hardware monitoring + * + * Copyright (C) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>, + * Kyösti Mälkki <kmalkki@cc.hut.fi>, and + * Mark D. Studebaker <mdsxyz123@yahoo.com> + * Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with + * the help of Jean Delvare <jdelvare@suse.de> + * + * 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. + */ /* - SiS southbridge has a LM78-like chip integrated on the same IC. - This driver is a customized copy of lm78.c - - Supports following revisions: - Version PCI ID PCI Revision - 1 1039/0008 AF or less - 2 1039/0008 B0 or greater - - Note: these chips contain a 0008 device which is incompatible with the - 5595. We recognize these by the presence of the listed - "blacklist" PCI ID and refuse to load. - - NOT SUPPORTED PCI ID BLACKLIST PCI ID - 540 0008 0540 - 550 0008 0550 - 5513 0008 5511 - 5581 0008 5597 - 5582 0008 5597 - 5597 0008 5597 - 5598 0008 5597/5598 - 630 0008 0630 - 645 0008 0645 - 730 0008 0730 - 735 0008 0735 -*/ + * SiS southbridge has a LM78-like chip integrated on the same IC. + * This driver is a customized copy of lm78.c + * + * Supports following revisions: + * Version PCI ID PCI Revision + * 1 1039/0008 AF or less + * 2 1039/0008 B0 or greater + * + * Note: these chips contain a 0008 device which is incompatible with the + * 5595. We recognize these by the presence of the listed + * "blacklist" PCI ID and refuse to load. + * + * NOT SUPPORTED PCI ID BLACKLIST PCI ID + * 540 0008 0540 + * 550 0008 0550 + * 5513 0008 5511 + * 5581 0008 5597 + * 5582 0008 5597 + * 5597 0008 5597 + * 5598 0008 5597/5598 + * 630 0008 0630 + * 645 0008 0645 + * 730 0008 0730 + * 735 0008 0735 + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/slab.h> @@ -66,8 +68,10 @@ #include <linux/io.h> -/* If force_addr is set to anything different from 0, we forcibly enable - the device at the given address. */ +/* + * If force_addr is set to anything different from 0, we forcibly enable + * the device at the given address. + */ static u16 force_addr; module_param(force_addr, ushort, 0); MODULE_PARM_DESC(force_addr, @@ -96,33 +100,39 @@ static struct platform_device *pdev; #define SIS5595_REG_FAN_MIN(nr) (0x3b + (nr)) #define SIS5595_REG_FAN(nr) (0x28 + (nr)) -/* On the first version of the chip, the temp registers are separate. - On the second version, - TEMP pin is shared with IN4, configured in PCI register 0x7A. - The registers are the same as well. - OVER and HYST are really MAX and MIN. */ +/* + * On the first version of the chip, the temp registers are separate. + * On the second version, + * TEMP pin is shared with IN4, configured in PCI register 0x7A. + * The registers are the same as well. + * OVER and HYST are really MAX and MIN. + */ #define REV2MIN 0xb0 -#define SIS5595_REG_TEMP (( data->revision) >= REV2MIN) ? \ - SIS5595_REG_IN(4) : 0x27 -#define SIS5595_REG_TEMP_OVER (( data->revision) >= REV2MIN) ? \ - SIS5595_REG_IN_MAX(4) : 0x39 -#define SIS5595_REG_TEMP_HYST (( data->revision) >= REV2MIN) ? \ - SIS5595_REG_IN_MIN(4) : 0x3a +#define SIS5595_REG_TEMP (((data->revision) >= REV2MIN) ? \ + SIS5595_REG_IN(4) : 0x27) +#define SIS5595_REG_TEMP_OVER (((data->revision) >= REV2MIN) ? \ + SIS5595_REG_IN_MAX(4) : 0x39) +#define SIS5595_REG_TEMP_HYST (((data->revision) >= REV2MIN) ? \ + SIS5595_REG_IN_MIN(4) : 0x3a) #define SIS5595_REG_CONFIG 0x40 #define SIS5595_REG_ALARM1 0x41 #define SIS5595_REG_ALARM2 0x42 #define SIS5595_REG_FANDIV 0x47 -/* Conversions. Limit checking is only done on the TO_REG - variants. */ +/* + * Conversions. Limit checking is only done on the TO_REG + * variants. + */ -/* IN: mV, (0V to 4.08V) - REG: 16mV/bit */ +/* + * IN: mV, (0V to 4.08V) + * REG: 16mV/bit + */ static inline u8 IN_TO_REG(unsigned long val) { - unsigned long nval = SENSORS_LIMIT(val, 0, 4080); + unsigned long nval = clamp_val(val, 0, 4080); return (nval + 8) / 16; } #define IN_FROM_REG(val) ((val) * 16) @@ -131,36 +141,44 @@ static inline u8 FAN_TO_REG(long rpm, int div) { if (rpm <= 0) return 255; - return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254); + if (rpm > 1350000) + return 1; + return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254); } static inline int FAN_FROM_REG(u8 val, int div) { - return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div); + return val == 0 ? -1 : val == 255 ? 0 : 1350000 / (val * div); } -/* TEMP: mC (-54.12C to +157.53C) - REG: 0.83C/bit + 52.12, two's complement */ +/* + * TEMP: mC (-54.12C to +157.53C) + * REG: 0.83C/bit + 52.12, two's complement + */ static inline int TEMP_FROM_REG(s8 val) { return val * 830 + 52120; } static inline s8 TEMP_TO_REG(int val) { - int nval = SENSORS_LIMIT(val, -54120, 157530) ; - return nval<0 ? (nval-5212-415)/830 : (nval-5212+415)/830; + int nval = clamp_val(val, -54120, 157530) ; + return nval < 0 ? (nval - 5212 - 415) / 830 : (nval - 5212 + 415) / 830; } -/* FAN DIV: 1, 2, 4, or 8 (defaults to 2) - REG: 0, 1, 2, or 3 (respectively) (defaults to 1) */ +/* + * FAN DIV: 1, 2, 4, or 8 (defaults to 2) + * REG: 0, 1, 2, or 3 (respectively) (defaults to 1) + */ static inline u8 DIV_TO_REG(int val) { - return val==8 ? 3 : val==4 ? 2 : val==1 ? 0 : 1; + return val == 8 ? 3 : val == 4 ? 2 : val == 1 ? 0 : 1; } #define DIV_FROM_REG(val) (1 << (val)) -/* For each registered chip, we need to keep some data in memory. - The structure is dynamically allocated. */ +/* + * For each registered chip, we need to keep some data in memory. + * The structure is dynamically allocated. + */ struct sis5595_data { unsigned short addr; const char *name; @@ -188,7 +206,7 @@ struct sis5595_data { static struct pci_dev *s_bridge; /* pointer to the (only) sis5595 */ static int sis5595_probe(struct platform_device *pdev); -static int __devexit sis5595_remove(struct platform_device *pdev); +static int sis5595_remove(struct platform_device *pdev); static int sis5595_read_value(struct sis5595_data *data, u8 reg); static void sis5595_write_value(struct sis5595_data *data, u8 reg, u8 value); @@ -201,7 +219,7 @@ static struct platform_driver sis5595_driver = { .name = "sis5595", }, .probe = sis5595_probe, - .remove = __devexit_p(sis5595_remove), + .remove = sis5595_remove, }; /* 4 Voltages */ @@ -238,7 +256,12 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *da, struct sis5595_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int nr = attr->index; - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_min[nr] = IN_TO_REG(val); @@ -253,7 +276,12 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *da, struct sis5595_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int nr = attr->index; - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_max[nr] = IN_TO_REG(val); @@ -277,22 +305,30 @@ show_in_offset(3); show_in_offset(4); /* Temperature */ -static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, + char *buf) { struct sis5595_data *data = sis5595_update_device(dev); return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp)); } -static ssize_t show_temp_over(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_temp_over(struct device *dev, struct device_attribute *attr, + char *buf) { struct sis5595_data *data = sis5595_update_device(dev); return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over)); } -static ssize_t set_temp_over(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t set_temp_over(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct sis5595_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_over = TEMP_TO_REG(val); @@ -301,16 +337,23 @@ static ssize_t set_temp_over(struct device *dev, struct device_attribute *attr, return count; } -static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *attr, + char *buf) { struct sis5595_data *data = sis5595_update_device(dev); return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst)); } -static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct sis5595_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_hyst = TEMP_TO_REG(val); @@ -333,7 +376,7 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *da, struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int nr = attr->index; return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], - DIV_FROM_REG(data->fan_div[nr])) ); + DIV_FROM_REG(data->fan_div[nr]))); } static ssize_t show_fan_min(struct device *dev, struct device_attribute *da, @@ -342,8 +385,8 @@ static ssize_t show_fan_min(struct device *dev, struct device_attribute *da, struct sis5595_data *data = sis5595_update_device(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int nr = attr->index; - return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr], - DIV_FROM_REG(data->fan_div[nr])) ); + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr]))); } static ssize_t set_fan_min(struct device *dev, struct device_attribute *da, @@ -352,7 +395,12 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *da, struct sis5595_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int nr = attr->index; - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); @@ -367,13 +415,15 @@ static ssize_t show_fan_div(struct device *dev, struct device_attribute *da, struct sis5595_data *data = sis5595_update_device(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int nr = attr->index; - return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) ); + return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr])); } -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan divisor. This follows the principle of - least surprise; the user doesn't expect the fan minimum to change just - because the divisor changed. */ +/* + * Note: we save and restore the fan minimum here, because its value is + * determined in part by the fan divisor. This follows the principle of + * least surprise; the user doesn't expect the fan minimum to change just + * because the divisor changed. + */ static ssize_t set_fan_div(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { @@ -381,8 +431,13 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *da, struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int nr = attr->index; unsigned long min; - unsigned long val = simple_strtoul(buf, NULL, 10); int reg; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); min = FAN_FROM_REG(data->fan_min[nr], @@ -390,17 +445,26 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *da, reg = sis5595_read_value(data, SIS5595_REG_FANDIV); switch (val) { - case 1: data->fan_div[nr] = 0; break; - case 2: data->fan_div[nr] = 1; break; - case 4: data->fan_div[nr] = 2; break; - case 8: data->fan_div[nr] = 3; break; + case 1: + data->fan_div[nr] = 0; + break; + case 2: + data->fan_div[nr] = 1; + break; + case 4: + data->fan_div[nr] = 2; + break; + case 8: + data->fan_div[nr] = 3; + break; default: - dev_err(dev, "fan_div value %ld not " - "supported. Choose one of 1, 2, 4 or 8!\n", val); + dev_err(dev, + "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n", + val); mutex_unlock(&data->update_lock); return -EINVAL; } - + switch (nr) { case 0: reg = (reg & 0xcf) | (data->fan_div[nr] << 4); @@ -429,7 +493,8 @@ show_fan_offset(1); show_fan_offset(2); /* Alarms */ -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, + char *buf) { struct sis5595_data *data = sis5595_update_device(dev); return sprintf(buf, "%d\n", data->alarms); @@ -519,9 +584,9 @@ static struct attribute *sis5595_attributes_temp1[] = { static const struct attribute_group sis5595_group_temp1 = { .attrs = sis5595_attributes_temp1, }; - + /* This is called when the module is loaded */ -static int __devinit sis5595_probe(struct platform_device *pdev) +static int sis5595_probe(struct platform_device *pdev) { int err = 0; int i; @@ -531,16 +596,14 @@ static int __devinit sis5595_probe(struct platform_device *pdev) /* Reserve the ISA region */ res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start, SIS5595_EXTENT, - sis5595_driver.driver.name)) { - err = -EBUSY; - goto exit; - } + if (!devm_request_region(&pdev->dev, res->start, SIS5595_EXTENT, + sis5595_driver.driver.name)) + return -EBUSY; - if (!(data = kzalloc(sizeof(struct sis5595_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit_release; - } + data = devm_kzalloc(&pdev->dev, sizeof(struct sis5595_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; mutex_init(&data->lock); mutex_init(&data->update_lock); @@ -548,7 +611,9 @@ static int __devinit sis5595_probe(struct platform_device *pdev) data->name = "sis5595"; platform_set_drvdata(pdev, data); - /* Check revision and pin registers to determine whether 4 or 5 voltages */ + /* + * Check revision and pin registers to determine whether 4 or 5 voltages + */ data->revision = s_bridge->revision; /* 4 voltages, 1 temp */ data->maxins = 3; @@ -558,7 +623,7 @@ static int __devinit sis5595_probe(struct platform_device *pdev) /* 5 voltages, no temps */ data->maxins = 4; } - + /* Initialize the SIS5595 chip */ sis5595_init_device(data); @@ -569,15 +634,16 @@ static int __devinit sis5595_probe(struct platform_device *pdev) } /* Register sysfs hooks */ - if ((err = sysfs_create_group(&pdev->dev.kobj, &sis5595_group))) - goto exit_free; + err = sysfs_create_group(&pdev->dev.kobj, &sis5595_group); + if (err) + return err; if (data->maxins == 4) { - if ((err = sysfs_create_group(&pdev->dev.kobj, - &sis5595_group_in4))) + err = sysfs_create_group(&pdev->dev.kobj, &sis5595_group_in4); + if (err) goto exit_remove_files; } else { - if ((err = sysfs_create_group(&pdev->dev.kobj, - &sis5595_group_temp1))) + err = sysfs_create_group(&pdev->dev.kobj, &sis5595_group_temp1); + if (err) goto exit_remove_files; } @@ -593,15 +659,10 @@ exit_remove_files: sysfs_remove_group(&pdev->dev.kobj, &sis5595_group); sysfs_remove_group(&pdev->dev.kobj, &sis5595_group_in4); sysfs_remove_group(&pdev->dev.kobj, &sis5595_group_temp1); -exit_free: - kfree(data); -exit_release: - release_region(res->start, SIS5595_EXTENT); -exit: return err; } -static int __devexit sis5595_remove(struct platform_device *pdev) +static int sis5595_remove(struct platform_device *pdev) { struct sis5595_data *data = platform_get_drvdata(pdev); @@ -610,10 +671,6 @@ static int __devexit sis5595_remove(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &sis5595_group_in4); sysfs_remove_group(&pdev->dev.kobj, &sis5595_group_temp1); - release_region(data->addr, SIS5595_EXTENT); - platform_set_drvdata(pdev, NULL); - kfree(data); - return 0; } @@ -639,7 +696,7 @@ static void sis5595_write_value(struct sis5595_data *data, u8 reg, u8 value) } /* Called when we have found a new SIS5595. */ -static void __devinit sis5595_init_device(struct sis5595_data *data) +static void sis5595_init_device(struct sis5595_data *data) { u8 config = sis5595_read_value(data, SIS5595_REG_CONFIG); if (!(config & 0x01)) @@ -704,21 +761,23 @@ static const struct pci_device_id sis5595_pci_ids[] = { MODULE_DEVICE_TABLE(pci, sis5595_pci_ids); -static int blacklist[] __devinitdata = { +static int blacklist[] = { PCI_DEVICE_ID_SI_540, PCI_DEVICE_ID_SI_550, PCI_DEVICE_ID_SI_630, PCI_DEVICE_ID_SI_645, PCI_DEVICE_ID_SI_730, PCI_DEVICE_ID_SI_735, - PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but - that ID shows up in other chips so we - use the 5511 ID for recognition */ + PCI_DEVICE_ID_SI_5511, /* + * 5513 chip has the 0008 device but + * that ID shows up in other chips so we + * use the 5511 ID for recognition + */ PCI_DEVICE_ID_SI_5597, PCI_DEVICE_ID_SI_5598, 0 }; -static int __devinit sis5595_device_add(unsigned short address) +static int sis5595_device_add(unsigned short address) { struct resource res = { .start = address, @@ -735,21 +794,19 @@ static int __devinit sis5595_device_add(unsigned short address) pdev = platform_device_alloc("sis5595", address); if (!pdev) { err = -ENOMEM; - printk(KERN_ERR "sis5595: Device allocation failed\n"); + pr_err("Device allocation failed\n"); goto exit; } err = platform_device_add_resources(pdev, &res, 1); if (err) { - printk(KERN_ERR "sis5595: Device resource addition failed " - "(%d)\n", err); + pr_err("Device resource addition failed (%d)\n", err); goto exit_device_put; } err = platform_device_add(pdev); if (err) { - printk(KERN_ERR "sis5595: Device addition failed (%d)\n", - err); + pr_err("Device addition failed (%d)\n", err); goto exit_device_put; } @@ -761,7 +818,7 @@ exit: return err; } -static int __devinit sis5595_pci_probe(struct pci_dev *dev, +static int sis5595_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { u16 address; @@ -770,13 +827,16 @@ static int __devinit sis5595_pci_probe(struct pci_dev *dev, for (i = blacklist; *i != 0; i++) { struct pci_dev *d; - if ((d = pci_get_device(PCI_VENDOR_ID_SI, *i, NULL))) { - dev_err(&d->dev, "Looked for SIS5595 but found unsupported device %.4x\n", *i); + d = pci_get_device(PCI_VENDOR_ID_SI, *i, NULL); + if (d) { + dev_err(&d->dev, + "Looked for SIS5595 but found unsupported device %.4x\n", + *i); pci_dev_put(d); return -ENODEV; } } - + force_addr &= ~(SIS5595_EXTENT - 1); if (force_addr) { dev_warn(&dev->dev, "Forcing ISA address 0x%x\n", force_addr); @@ -788,10 +848,11 @@ static int __devinit sis5595_pci_probe(struct pci_dev *dev, dev_err(&dev->dev, "Failed to read ISA address\n"); return -ENODEV; } - + address &= ~(SIS5595_EXTENT - 1); if (!address) { - dev_err(&dev->dev, "Base address not set - upgrade BIOS or use force_addr=0xaddr\n"); + dev_err(&dev->dev, + "Base address not set - upgrade BIOS or use force_addr=0xaddr\n"); return -ENODEV; } if (force_addr && address != force_addr) { @@ -828,7 +889,8 @@ static int __devinit sis5595_pci_probe(struct pci_dev *dev, if (sis5595_device_add(address)) goto exit_unregister; - /* Always return failure here. This is to allow other drivers to bind + /* + * Always return failure here. This is to allow other drivers to bind * to this pci device. We don't really want to have control over the * pci device, we only wanted to read as few register values from it. */ diff --git a/drivers/hwmon/smm665.c b/drivers/hwmon/smm665.c new file mode 100644 index 00000000000..4ef5802df6d --- /dev/null +++ b/drivers/hwmon/smm665.c @@ -0,0 +1,721 @@ +/* + * Driver for SMM665 Power Controller / Monitor + * + * Copyright (C) 2010 Ericsson AB. + * + * 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 of the License. + * + * This driver should also work for SMM465, SMM764, and SMM766, but is untested + * for those chips. Only monitoring functionality is implemented. + * + * Datasheets: + * http://www.summitmicro.com/prod_select/summary/SMM665/SMM665B_2089_20.pdf + * http://www.summitmicro.com/prod_select/summary/SMM766B/SMM766B_2122.pdf + */ + +#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> +#include <linux/delay.h> +#include <linux/jiffies.h> + +/* Internal reference voltage (VREF, x 1000 */ +#define SMM665_VREF_ADC_X1000 1250 + +/* module parameters */ +static int vref = SMM665_VREF_ADC_X1000; +module_param(vref, int, 0); +MODULE_PARM_DESC(vref, "Reference voltage in mV"); + +enum chips { smm465, smm665, smm665c, smm764, smm766 }; + +/* + * ADC channel addresses + */ +#define SMM665_MISC16_ADC_DATA_A 0x00 +#define SMM665_MISC16_ADC_DATA_B 0x01 +#define SMM665_MISC16_ADC_DATA_C 0x02 +#define SMM665_MISC16_ADC_DATA_D 0x03 +#define SMM665_MISC16_ADC_DATA_E 0x04 +#define SMM665_MISC16_ADC_DATA_F 0x05 +#define SMM665_MISC16_ADC_DATA_VDD 0x06 +#define SMM665_MISC16_ADC_DATA_12V 0x07 +#define SMM665_MISC16_ADC_DATA_INT_TEMP 0x08 +#define SMM665_MISC16_ADC_DATA_AIN1 0x09 +#define SMM665_MISC16_ADC_DATA_AIN2 0x0a + +/* + * Command registers + */ +#define SMM665_MISC8_CMD_STS 0x80 +#define SMM665_MISC8_STATUS1 0x81 +#define SMM665_MISC8_STATUSS2 0x82 +#define SMM665_MISC8_IO_POLARITY 0x83 +#define SMM665_MISC8_PUP_POLARITY 0x84 +#define SMM665_MISC8_ADOC_STATUS1 0x85 +#define SMM665_MISC8_ADOC_STATUS2 0x86 +#define SMM665_MISC8_WRITE_PROT 0x87 +#define SMM665_MISC8_STS_TRACK 0x88 + +/* + * Configuration registers and register groups + */ +#define SMM665_ADOC_ENABLE 0x0d +#define SMM665_LIMIT_BASE 0x80 /* First limit register */ + +/* + * Limit register bit masks + */ +#define SMM665_TRIGGER_RST 0x8000 +#define SMM665_TRIGGER_HEALTHY 0x4000 +#define SMM665_TRIGGER_POWEROFF 0x2000 +#define SMM665_TRIGGER_SHUTDOWN 0x1000 +#define SMM665_ADC_MASK 0x03ff + +#define smm665_is_critical(lim) ((lim) & (SMM665_TRIGGER_RST \ + | SMM665_TRIGGER_POWEROFF \ + | SMM665_TRIGGER_SHUTDOWN)) +/* + * Fault register bit definitions + * Values are merged from status registers 1/2, + * with status register 1 providing the upper 8 bits. + */ +#define SMM665_FAULT_A 0x0001 +#define SMM665_FAULT_B 0x0002 +#define SMM665_FAULT_C 0x0004 +#define SMM665_FAULT_D 0x0008 +#define SMM665_FAULT_E 0x0010 +#define SMM665_FAULT_F 0x0020 +#define SMM665_FAULT_VDD 0x0040 +#define SMM665_FAULT_12V 0x0080 +#define SMM665_FAULT_TEMP 0x0100 +#define SMM665_FAULT_AIN1 0x0200 +#define SMM665_FAULT_AIN2 0x0400 + +/* + * I2C Register addresses + * + * The configuration register needs to be the configured base register. + * The command/status register address is derived from it. + */ +#define SMM665_REGMASK 0x78 +#define SMM665_CMDREG_BASE 0x48 +#define SMM665_CONFREG_BASE 0x50 + +/* + * Equations given by chip manufacturer to calculate voltage/temperature values + * vref = Reference voltage on VREF_ADC pin (module parameter) + * adc = 10bit ADC value read back from registers + */ + +/* Voltage A-F and VDD */ +#define SMM665_VMON_ADC_TO_VOLTS(adc) ((adc) * vref / 256) + +/* Voltage 12VIN */ +#define SMM665_12VIN_ADC_TO_VOLTS(adc) ((adc) * vref * 3 / 256) + +/* Voltage AIN1, AIN2 */ +#define SMM665_AIN_ADC_TO_VOLTS(adc) ((adc) * vref / 512) + +/* Temp Sensor */ +#define SMM665_TEMP_ADC_TO_CELSIUS(adc) (((adc) <= 511) ? \ + ((int)(adc) * 1000 / 4) : \ + (((int)(adc) - 0x400) * 1000 / 4)) + +#define SMM665_NUM_ADC 11 + +/* + * Chip dependent ADC conversion time, in uS + */ +#define SMM665_ADC_WAIT_SMM665 70 +#define SMM665_ADC_WAIT_SMM766 185 + +struct smm665_data { + enum chips type; + int conversion_time; /* ADC conversion time */ + struct device *hwmon_dev; + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + u16 adc[SMM665_NUM_ADC]; /* adc values (raw) */ + u16 faults; /* fault status */ + /* The following values are in mV */ + int critical_min_limit[SMM665_NUM_ADC]; + int alarm_min_limit[SMM665_NUM_ADC]; + int critical_max_limit[SMM665_NUM_ADC]; + int alarm_max_limit[SMM665_NUM_ADC]; + struct i2c_client *cmdreg; +}; + +/* + * smm665_read16() + * + * Read 16 bit value from <reg>, <reg+1>. Upper 8 bits are in <reg>. + */ +static int smm665_read16(struct i2c_client *client, int reg) +{ + int rv, val; + + rv = i2c_smbus_read_byte_data(client, reg); + if (rv < 0) + return rv; + val = rv << 8; + rv = i2c_smbus_read_byte_data(client, reg + 1); + if (rv < 0) + return rv; + val |= rv; + return val; +} + +/* + * Read adc value. + */ +static int smm665_read_adc(struct smm665_data *data, int adc) +{ + struct i2c_client *client = data->cmdreg; + int rv; + int radc; + + /* + * Algorithm for reading ADC, per SMM665 datasheet + * + * {[S][addr][W][Ack]} {[offset][Ack]} {[S][addr][R][Nack]} + * [wait conversion time] + * {[S][addr][R][Ack]} {[datahi][Ack]} {[datalo][Ack][P]} + * + * To implement the first part of this exchange, + * do a full read transaction and expect a failure/Nack. + * This sets up the address pointer on the SMM665 + * and starts the ADC conversion. + * Then do a two-byte read transaction. + */ + rv = i2c_smbus_read_byte_data(client, adc << 3); + if (rv != -ENXIO) { + /* + * We expect ENXIO to reflect NACK + * (per Documentation/i2c/fault-codes). + * Everything else is an error. + */ + dev_dbg(&client->dev, + "Unexpected return code %d when setting ADC index", rv); + return (rv < 0) ? rv : -EIO; + } + + udelay(data->conversion_time); + + /* + * Now read two bytes. + * + * Neither i2c_smbus_read_byte() nor + * i2c_smbus_read_block_data() worked here, + * so use i2c_smbus_read_word_swapped() instead. + * We could also try to use i2c_master_recv(), + * but that is not always supported. + */ + rv = i2c_smbus_read_word_swapped(client, 0); + if (rv < 0) { + dev_dbg(&client->dev, "Failed to read ADC value: error %d", rv); + return rv; + } + /* + * Validate/verify readback adc channel (in bit 11..14). + */ + radc = (rv >> 11) & 0x0f; + if (radc != adc) { + dev_dbg(&client->dev, "Unexpected RADC: Expected %d got %d", + adc, radc); + return -EIO; + } + + return rv & SMM665_ADC_MASK; +} + +static struct smm665_data *smm665_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct smm665_data *data = i2c_get_clientdata(client); + struct smm665_data *ret = data; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + int i, val; + + /* + * read status registers + */ + val = smm665_read16(client, SMM665_MISC8_STATUS1); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->faults = val; + + /* Read adc registers */ + for (i = 0; i < SMM665_NUM_ADC; i++) { + val = smm665_read_adc(data, i); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->adc[i] = val; + } + data->last_updated = jiffies; + data->valid = 1; + } +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +/* Return converted value from given adc */ +static int smm665_convert(u16 adcval, int index) +{ + int val = 0; + + switch (index) { + case SMM665_MISC16_ADC_DATA_12V: + val = SMM665_12VIN_ADC_TO_VOLTS(adcval & SMM665_ADC_MASK); + break; + + case SMM665_MISC16_ADC_DATA_VDD: + case SMM665_MISC16_ADC_DATA_A: + case SMM665_MISC16_ADC_DATA_B: + case SMM665_MISC16_ADC_DATA_C: + case SMM665_MISC16_ADC_DATA_D: + case SMM665_MISC16_ADC_DATA_E: + case SMM665_MISC16_ADC_DATA_F: + val = SMM665_VMON_ADC_TO_VOLTS(adcval & SMM665_ADC_MASK); + break; + + case SMM665_MISC16_ADC_DATA_AIN1: + case SMM665_MISC16_ADC_DATA_AIN2: + val = SMM665_AIN_ADC_TO_VOLTS(adcval & SMM665_ADC_MASK); + break; + + case SMM665_MISC16_ADC_DATA_INT_TEMP: + val = SMM665_TEMP_ADC_TO_CELSIUS(adcval & SMM665_ADC_MASK); + break; + + default: + /* If we get here, the developer messed up */ + WARN_ON_ONCE(1); + break; + } + + return val; +} + +static int smm665_get_min(struct device *dev, int index) +{ + struct i2c_client *client = to_i2c_client(dev); + struct smm665_data *data = i2c_get_clientdata(client); + + return data->alarm_min_limit[index]; +} + +static int smm665_get_max(struct device *dev, int index) +{ + struct i2c_client *client = to_i2c_client(dev); + struct smm665_data *data = i2c_get_clientdata(client); + + return data->alarm_max_limit[index]; +} + +static int smm665_get_lcrit(struct device *dev, int index) +{ + struct i2c_client *client = to_i2c_client(dev); + struct smm665_data *data = i2c_get_clientdata(client); + + return data->critical_min_limit[index]; +} + +static int smm665_get_crit(struct device *dev, int index) +{ + struct i2c_client *client = to_i2c_client(dev); + struct smm665_data *data = i2c_get_clientdata(client); + + return data->critical_max_limit[index]; +} + +static ssize_t smm665_show_crit_alarm(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct smm665_data *data = smm665_update_device(dev); + int val = 0; + + if (IS_ERR(data)) + return PTR_ERR(data); + + if (data->faults & (1 << attr->index)) + val = 1; + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t smm665_show_input(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct smm665_data *data = smm665_update_device(dev); + int adc = attr->index; + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = smm665_convert(data->adc[adc], adc); + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +#define SMM665_SHOW(what) \ +static ssize_t smm665_show_##what(struct device *dev, \ + struct device_attribute *da, char *buf) \ +{ \ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \ + const int val = smm665_get_##what(dev, attr->index); \ + return snprintf(buf, PAGE_SIZE, "%d\n", val); \ +} + +SMM665_SHOW(min); +SMM665_SHOW(max); +SMM665_SHOW(lcrit); +SMM665_SHOW(crit); + +/* + * 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 SMM665_ATTR(name, type, cmd_idx) \ + static SENSOR_DEVICE_ATTR(name##_##type, S_IRUGO, \ + smm665_show_##type, NULL, cmd_idx) + +/* Construct a sensor_device_attribute structure for each register */ + +/* Input voltages */ +SMM665_ATTR(in1, input, SMM665_MISC16_ADC_DATA_12V); +SMM665_ATTR(in2, input, SMM665_MISC16_ADC_DATA_VDD); +SMM665_ATTR(in3, input, SMM665_MISC16_ADC_DATA_A); +SMM665_ATTR(in4, input, SMM665_MISC16_ADC_DATA_B); +SMM665_ATTR(in5, input, SMM665_MISC16_ADC_DATA_C); +SMM665_ATTR(in6, input, SMM665_MISC16_ADC_DATA_D); +SMM665_ATTR(in7, input, SMM665_MISC16_ADC_DATA_E); +SMM665_ATTR(in8, input, SMM665_MISC16_ADC_DATA_F); +SMM665_ATTR(in9, input, SMM665_MISC16_ADC_DATA_AIN1); +SMM665_ATTR(in10, input, SMM665_MISC16_ADC_DATA_AIN2); + +/* Input voltages min */ +SMM665_ATTR(in1, min, SMM665_MISC16_ADC_DATA_12V); +SMM665_ATTR(in2, min, SMM665_MISC16_ADC_DATA_VDD); +SMM665_ATTR(in3, min, SMM665_MISC16_ADC_DATA_A); +SMM665_ATTR(in4, min, SMM665_MISC16_ADC_DATA_B); +SMM665_ATTR(in5, min, SMM665_MISC16_ADC_DATA_C); +SMM665_ATTR(in6, min, SMM665_MISC16_ADC_DATA_D); +SMM665_ATTR(in7, min, SMM665_MISC16_ADC_DATA_E); +SMM665_ATTR(in8, min, SMM665_MISC16_ADC_DATA_F); +SMM665_ATTR(in9, min, SMM665_MISC16_ADC_DATA_AIN1); +SMM665_ATTR(in10, min, SMM665_MISC16_ADC_DATA_AIN2); + +/* Input voltages max */ +SMM665_ATTR(in1, max, SMM665_MISC16_ADC_DATA_12V); +SMM665_ATTR(in2, max, SMM665_MISC16_ADC_DATA_VDD); +SMM665_ATTR(in3, max, SMM665_MISC16_ADC_DATA_A); +SMM665_ATTR(in4, max, SMM665_MISC16_ADC_DATA_B); +SMM665_ATTR(in5, max, SMM665_MISC16_ADC_DATA_C); +SMM665_ATTR(in6, max, SMM665_MISC16_ADC_DATA_D); +SMM665_ATTR(in7, max, SMM665_MISC16_ADC_DATA_E); +SMM665_ATTR(in8, max, SMM665_MISC16_ADC_DATA_F); +SMM665_ATTR(in9, max, SMM665_MISC16_ADC_DATA_AIN1); +SMM665_ATTR(in10, max, SMM665_MISC16_ADC_DATA_AIN2); + +/* Input voltages lcrit */ +SMM665_ATTR(in1, lcrit, SMM665_MISC16_ADC_DATA_12V); +SMM665_ATTR(in2, lcrit, SMM665_MISC16_ADC_DATA_VDD); +SMM665_ATTR(in3, lcrit, SMM665_MISC16_ADC_DATA_A); +SMM665_ATTR(in4, lcrit, SMM665_MISC16_ADC_DATA_B); +SMM665_ATTR(in5, lcrit, SMM665_MISC16_ADC_DATA_C); +SMM665_ATTR(in6, lcrit, SMM665_MISC16_ADC_DATA_D); +SMM665_ATTR(in7, lcrit, SMM665_MISC16_ADC_DATA_E); +SMM665_ATTR(in8, lcrit, SMM665_MISC16_ADC_DATA_F); +SMM665_ATTR(in9, lcrit, SMM665_MISC16_ADC_DATA_AIN1); +SMM665_ATTR(in10, lcrit, SMM665_MISC16_ADC_DATA_AIN2); + +/* Input voltages crit */ +SMM665_ATTR(in1, crit, SMM665_MISC16_ADC_DATA_12V); +SMM665_ATTR(in2, crit, SMM665_MISC16_ADC_DATA_VDD); +SMM665_ATTR(in3, crit, SMM665_MISC16_ADC_DATA_A); +SMM665_ATTR(in4, crit, SMM665_MISC16_ADC_DATA_B); +SMM665_ATTR(in5, crit, SMM665_MISC16_ADC_DATA_C); +SMM665_ATTR(in6, crit, SMM665_MISC16_ADC_DATA_D); +SMM665_ATTR(in7, crit, SMM665_MISC16_ADC_DATA_E); +SMM665_ATTR(in8, crit, SMM665_MISC16_ADC_DATA_F); +SMM665_ATTR(in9, crit, SMM665_MISC16_ADC_DATA_AIN1); +SMM665_ATTR(in10, crit, SMM665_MISC16_ADC_DATA_AIN2); + +/* critical alarms */ +SMM665_ATTR(in1, crit_alarm, SMM665_FAULT_12V); +SMM665_ATTR(in2, crit_alarm, SMM665_FAULT_VDD); +SMM665_ATTR(in3, crit_alarm, SMM665_FAULT_A); +SMM665_ATTR(in4, crit_alarm, SMM665_FAULT_B); +SMM665_ATTR(in5, crit_alarm, SMM665_FAULT_C); +SMM665_ATTR(in6, crit_alarm, SMM665_FAULT_D); +SMM665_ATTR(in7, crit_alarm, SMM665_FAULT_E); +SMM665_ATTR(in8, crit_alarm, SMM665_FAULT_F); +SMM665_ATTR(in9, crit_alarm, SMM665_FAULT_AIN1); +SMM665_ATTR(in10, crit_alarm, SMM665_FAULT_AIN2); + +/* Temperature */ +SMM665_ATTR(temp1, input, SMM665_MISC16_ADC_DATA_INT_TEMP); +SMM665_ATTR(temp1, min, SMM665_MISC16_ADC_DATA_INT_TEMP); +SMM665_ATTR(temp1, max, SMM665_MISC16_ADC_DATA_INT_TEMP); +SMM665_ATTR(temp1, lcrit, SMM665_MISC16_ADC_DATA_INT_TEMP); +SMM665_ATTR(temp1, crit, SMM665_MISC16_ADC_DATA_INT_TEMP); +SMM665_ATTR(temp1, crit_alarm, SMM665_FAULT_TEMP); + +/* + * Finally, construct an array of pointers to members of the above objects, + * as required for sysfs_create_group() + */ +static struct attribute *smm665_attributes[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in1_lcrit.dev_attr.attr, + &sensor_dev_attr_in1_crit.dev_attr.attr, + &sensor_dev_attr_in1_crit_alarm.dev_attr.attr, + + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in2_lcrit.dev_attr.attr, + &sensor_dev_attr_in2_crit.dev_attr.attr, + &sensor_dev_attr_in2_crit_alarm.dev_attr.attr, + + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in3_lcrit.dev_attr.attr, + &sensor_dev_attr_in3_crit.dev_attr.attr, + &sensor_dev_attr_in3_crit_alarm.dev_attr.attr, + + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in4_lcrit.dev_attr.attr, + &sensor_dev_attr_in4_crit.dev_attr.attr, + &sensor_dev_attr_in4_crit_alarm.dev_attr.attr, + + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, + &sensor_dev_attr_in5_lcrit.dev_attr.attr, + &sensor_dev_attr_in5_crit.dev_attr.attr, + &sensor_dev_attr_in5_crit_alarm.dev_attr.attr, + + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in6_min.dev_attr.attr, + &sensor_dev_attr_in6_max.dev_attr.attr, + &sensor_dev_attr_in6_lcrit.dev_attr.attr, + &sensor_dev_attr_in6_crit.dev_attr.attr, + &sensor_dev_attr_in6_crit_alarm.dev_attr.attr, + + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in7_min.dev_attr.attr, + &sensor_dev_attr_in7_max.dev_attr.attr, + &sensor_dev_attr_in7_lcrit.dev_attr.attr, + &sensor_dev_attr_in7_crit.dev_attr.attr, + &sensor_dev_attr_in7_crit_alarm.dev_attr.attr, + + &sensor_dev_attr_in8_input.dev_attr.attr, + &sensor_dev_attr_in8_min.dev_attr.attr, + &sensor_dev_attr_in8_max.dev_attr.attr, + &sensor_dev_attr_in8_lcrit.dev_attr.attr, + &sensor_dev_attr_in8_crit.dev_attr.attr, + &sensor_dev_attr_in8_crit_alarm.dev_attr.attr, + + &sensor_dev_attr_in9_input.dev_attr.attr, + &sensor_dev_attr_in9_min.dev_attr.attr, + &sensor_dev_attr_in9_max.dev_attr.attr, + &sensor_dev_attr_in9_lcrit.dev_attr.attr, + &sensor_dev_attr_in9_crit.dev_attr.attr, + &sensor_dev_attr_in9_crit_alarm.dev_attr.attr, + + &sensor_dev_attr_in10_input.dev_attr.attr, + &sensor_dev_attr_in10_min.dev_attr.attr, + &sensor_dev_attr_in10_max.dev_attr.attr, + &sensor_dev_attr_in10_lcrit.dev_attr.attr, + &sensor_dev_attr_in10_crit.dev_attr.attr, + &sensor_dev_attr_in10_crit_alarm.dev_attr.attr, + + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_lcrit.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + + NULL, +}; + +static const struct attribute_group smm665_group = { + .attrs = smm665_attributes, +}; + +static int smm665_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct smm665_data *data; + int i, ret; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA + | I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + if (i2c_smbus_read_byte_data(client, SMM665_ADOC_ENABLE) < 0) + return -ENODEV; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + data->type = id->driver_data; + data->cmdreg = i2c_new_dummy(adapter, (client->addr & ~SMM665_REGMASK) + | SMM665_CMDREG_BASE); + if (!data->cmdreg) + return -ENOMEM; + + switch (data->type) { + case smm465: + case smm665: + data->conversion_time = SMM665_ADC_WAIT_SMM665; + break; + case smm665c: + case smm764: + case smm766: + data->conversion_time = SMM665_ADC_WAIT_SMM766; + break; + } + + ret = -ENODEV; + if (i2c_smbus_read_byte_data(data->cmdreg, SMM665_MISC8_CMD_STS) < 0) + goto out_unregister; + + /* + * Read limits. + * + * Limit registers start with register SMM665_LIMIT_BASE. + * Each channel uses 8 registers, providing four limit values + * per channel. Each limit value requires two registers, with the + * high byte in the first register and the low byte in the second + * register. The first two limits are under limit values, followed + * by two over limit values. + * + * Limit register order matches the ADC register order, so we use + * ADC register defines throughout the code to index limit registers. + * + * We save the first retrieved value both as "critical" and "alarm" + * value. The second value overwrites either the critical or the + * alarm value, depending on its configuration. This ensures that both + * critical and alarm values are initialized, even if both registers are + * configured as critical or non-critical. + */ + for (i = 0; i < SMM665_NUM_ADC; i++) { + int val; + + val = smm665_read16(client, SMM665_LIMIT_BASE + i * 8); + if (unlikely(val < 0)) + goto out_unregister; + data->critical_min_limit[i] = data->alarm_min_limit[i] + = smm665_convert(val, i); + val = smm665_read16(client, SMM665_LIMIT_BASE + i * 8 + 2); + if (unlikely(val < 0)) + goto out_unregister; + if (smm665_is_critical(val)) + data->critical_min_limit[i] = smm665_convert(val, i); + else + data->alarm_min_limit[i] = smm665_convert(val, i); + val = smm665_read16(client, SMM665_LIMIT_BASE + i * 8 + 4); + if (unlikely(val < 0)) + goto out_unregister; + data->critical_max_limit[i] = data->alarm_max_limit[i] + = smm665_convert(val, i); + val = smm665_read16(client, SMM665_LIMIT_BASE + i * 8 + 6); + if (unlikely(val < 0)) + goto out_unregister; + if (smm665_is_critical(val)) + data->critical_max_limit[i] = smm665_convert(val, i); + else + data->alarm_max_limit[i] = smm665_convert(val, i); + } + + /* Register sysfs hooks */ + ret = sysfs_create_group(&client->dev.kobj, &smm665_group); + if (ret) + goto out_unregister; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + goto out_remove_group; + } + + return 0; + +out_remove_group: + sysfs_remove_group(&client->dev.kobj, &smm665_group); +out_unregister: + i2c_unregister_device(data->cmdreg); + return ret; +} + +static int smm665_remove(struct i2c_client *client) +{ + struct smm665_data *data = i2c_get_clientdata(client); + + i2c_unregister_device(data->cmdreg); + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &smm665_group); + + return 0; +} + +static const struct i2c_device_id smm665_id[] = { + {"smm465", smm465}, + {"smm665", smm665}, + {"smm665c", smm665c}, + {"smm764", smm764}, + {"smm766", smm766}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, smm665_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver smm665_driver = { + .driver = { + .name = "smm665", + }, + .probe = smm665_probe, + .remove = smm665_remove, + .id_table = smm665_id, +}; + +module_i2c_driver(smm665_driver); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("SMM665 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/smsc47b397.c b/drivers/hwmon/smsc47b397.c index f46d936c12d..bd89e87bd6a 100644 --- a/drivers/hwmon/smsc47b397.c +++ b/drivers/hwmon/smsc47b397.c @@ -1,30 +1,32 @@ /* - smsc47b397.c - Part of lm_sensors, Linux kernel modules - for hardware monitoring - - Supports the SMSC LPC47B397-NC Super-I/O chip. - - Author/Maintainer: Mark M. Hoffman <mhoffman@lightlink.com> - Copyright (C) 2004 Utilitek Systems, Inc. - - derived in part from smsc47m1.c: - Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com> - Copyright (C) 2004 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; 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. -*/ + * smsc47b397.c - Part of lm_sensors, Linux kernel modules + * for hardware monitoring + * + * Supports the SMSC LPC47B397-NC Super-I/O chip. + * + * Author/Maintainer: Mark M. Hoffman <mhoffman@lightlink.com> + * Copyright (C) 2004 Utilitek Systems, Inc. + * + * derived in part from smsc47m1.c: + * Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com> + * Copyright (C) 2004 Jean Delvare <jdelvare@suse.de> + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/slab.h> @@ -111,7 +113,7 @@ struct smsc47b397_data { u8 temp[4]; }; -static int smsc47b397_read_value(struct smsc47b397_data* data, u8 reg) +static int smsc47b397_read_value(struct smsc47b397_data *data, u8 reg) { int res; @@ -155,8 +157,10 @@ static struct smsc47b397_data *smsc47b397_update_device(struct device *dev) return data; } -/* TEMP: 0.001C/bit (-128C to +127C) - REG: 1C/bit, two's complement */ +/* + * TEMP: 0.001C/bit (-128C to +127C) + * REG: 1C/bit, two's complement + */ static int temp_from_reg(u8 reg) { return (s8)reg * 1000; @@ -175,8 +179,10 @@ static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3); -/* FAN: 1 RPM/bit - REG: count of 90kHz pulses / revolution */ +/* + * FAN: 1 RPM/bit + * REG: count of 90kHz pulses / revolution + */ static int fan_from_reg(u16 reg) { if (reg == 0 || reg == 0xffff) @@ -222,16 +228,12 @@ static const struct attribute_group smsc47b397_group = { .attrs = smsc47b397_attributes, }; -static int __devexit smsc47b397_remove(struct platform_device *pdev) +static int smsc47b397_remove(struct platform_device *pdev) { struct smsc47b397_data *data = platform_get_drvdata(pdev); - struct resource *res; hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&pdev->dev.kobj, &smsc47b397_group); - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - release_region(res->start, SMSC_EXTENT); - kfree(data); return 0; } @@ -244,10 +246,10 @@ static struct platform_driver smsc47b397_driver = { .name = DRVNAME, }, .probe = smsc47b397_probe, - .remove = __devexit_p(smsc47b397_remove), + .remove = smsc47b397_remove, }; -static int __devinit smsc47b397_probe(struct platform_device *pdev) +static int smsc47b397_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct smsc47b397_data *data; @@ -255,18 +257,17 @@ static int __devinit smsc47b397_probe(struct platform_device *pdev) int err = 0; res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start, SMSC_EXTENT, - smsc47b397_driver.driver.name)) { + if (!devm_request_region(dev, res->start, SMSC_EXTENT, + smsc47b397_driver.driver.name)) { dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", (unsigned long)res->start, (unsigned long)res->start + SMSC_EXTENT - 1); return -EBUSY; } - if (!(data = kzalloc(sizeof(struct smsc47b397_data), GFP_KERNEL))) { - err = -ENOMEM; - goto error_release; - } + data = devm_kzalloc(dev, sizeof(struct smsc47b397_data), GFP_KERNEL); + if (!data) + return -ENOMEM; data->addr = res->start; data->name = "smsc47b397"; @@ -274,8 +275,9 @@ static int __devinit smsc47b397_probe(struct platform_device *pdev) mutex_init(&data->update_lock); platform_set_drvdata(pdev, data); - if ((err = sysfs_create_group(&dev->kobj, &smsc47b397_group))) - goto error_free; + err = sysfs_create_group(&dev->kobj, &smsc47b397_group); + if (err) + return err; data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { @@ -287,10 +289,6 @@ static int __devinit smsc47b397_probe(struct platform_device *pdev) error_remove: sysfs_remove_group(&dev->kobj, &smsc47b397_group); -error_free: - kfree(data); -error_release: - release_region(res->start, SMSC_EXTENT); return err; } @@ -311,21 +309,19 @@ static int __init smsc47b397_device_add(unsigned short address) pdev = platform_device_alloc(DRVNAME, address); if (!pdev) { err = -ENOMEM; - printk(KERN_ERR DRVNAME ": Device allocation failed\n"); + pr_err("Device allocation failed\n"); goto exit; } err = platform_device_add_resources(pdev, &res, 1); if (err) { - printk(KERN_ERR DRVNAME ": Device resource addition failed " - "(%d)\n", err); + pr_err("Device resource addition failed (%d)\n", err); goto exit_device_put; } err = platform_device_add(pdev); if (err) { - printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", - err); + pr_err("Device addition failed (%d)\n", err); goto exit_device_put; } @@ -337,15 +333,16 @@ exit: return err; } -static int __init smsc47b397_find(unsigned short *addr) +static int __init smsc47b397_find(void) { u8 id, rev; char *name; + unsigned short addr; superio_enter(); id = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID); - switch(id) { + switch (id) { case 0x81: name = "SCH5307-NS"; break; @@ -364,15 +361,14 @@ static int __init smsc47b397_find(unsigned short *addr) rev = superio_inb(SUPERIO_REG_DEVREV); superio_select(SUPERIO_REG_LD8); - *addr = (superio_inb(SUPERIO_REG_BASE_MSB) << 8) + addr = (superio_inb(SUPERIO_REG_BASE_MSB) << 8) | superio_inb(SUPERIO_REG_BASE_LSB); - printk(KERN_INFO DRVNAME ": found SMSC %s " - "(base address 0x%04x, revision %u)\n", - name, *addr, rev); + pr_info("found SMSC %s (base address 0x%04x, revision %u)\n", + name, addr, rev); superio_exit(); - return 0; + return addr; } static int __init smsc47b397_init(void) @@ -380,8 +376,10 @@ static int __init smsc47b397_init(void) unsigned short address; int ret; - if ((ret = smsc47b397_find(&address))) + ret = smsc47b397_find(); + if (ret < 0) return ret; + address = ret; ret = platform_driver_register(&smsc47b397_driver); if (ret) diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c index 8fa462f2b57..23a22c4eee5 100644 --- a/drivers/hwmon/smsc47m1.c +++ b/drivers/hwmon/smsc47m1.c @@ -1,30 +1,32 @@ /* - smsc47m1.c - Part of lm_sensors, Linux kernel modules - for hardware monitoring - - Supports the SMSC LPC47B27x, LPC47M10x, LPC47M112, LPC47M13x, - LPC47M14x, LPC47M15x, LPC47M192, LPC47M292 and LPC47M997 - Super-I/O chips. - - Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com> - Copyright (C) 2004-2007 Jean Delvare <khali@linux-fr.org> - Ported to Linux 2.6 by Gabriele Gorla <gorlik@yahoo.com> - and Jean Delvare - - 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. + * smsc47m1.c - Part of lm_sensors, Linux kernel modules + * for hardware monitoring + * + * Supports the SMSC LPC47B27x, LPC47M10x, LPC47M112, LPC47M13x, + * LPC47M14x, LPC47M15x, LPC47M192, LPC47M292 and LPC47M997 + * Super-I/O chips. + * + * Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com> + * Copyright (C) 2004-2007 Jean Delvare <jdelvare@suse.de> + * Ported to Linux 2.6 by Gabriele Gorla <gorlik@yahoo.com> + * and Jean Delvare + * + * 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. + */ - 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. -*/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/slab.h> @@ -51,8 +53,8 @@ enum chips { smsc47m1, smsc47m2 }; /* Super-I/0 registers and commands */ -#define REG 0x2e /* The register to read/write */ -#define VAL 0x2f /* The value to read/write */ +#define REG 0x2e /* The register to read/write */ +#define VAL 0x2f /* The value to read/write */ static inline void superio_outb(int reg, int val) @@ -109,10 +111,11 @@ static const u8 SMSC47M1_REG_PWM[3] = { 0x56, 0x57, 0x69 }; #define SMSC47M2_REG_PPIN3 0x2c #define SMSC47M2_REG_FANDIV3 0x6a -#define MIN_FROM_REG(reg,div) ((reg)>=192 ? 0 : \ - 983040/((192-(reg))*(div))) -#define FAN_FROM_REG(reg,div,preload) ((reg)<=(preload) || (reg)==255 ? 0 : \ - 983040/(((reg)-(preload))*(div))) +#define MIN_FROM_REG(reg, div) ((reg) >= 192 ? 0 : \ + 983040 / ((192 - (reg)) * (div))) +#define FAN_FROM_REG(reg, div, preload) ((reg) <= (preload) || (reg) == 255 ? \ + 0 : \ + 983040 / (((reg) - (preload)) * (div))) #define DIV_FROM_REG(reg) (1 << (reg)) #define PWM_FROM_REG(reg) (((reg) & 0x7E) << 1) #define PWM_EN_FROM_REG(reg) ((~(reg)) & 0x01) @@ -169,10 +172,12 @@ static ssize_t get_fan(struct device *dev, struct device_attribute struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct smsc47m1_data *data = smsc47m1_update_device(dev, 0); int nr = attr->index; - /* This chip (stupidly) stops monitoring fan speed if PWM is - enabled and duty cycle is 0%. This is fine if the monitoring - and control concern the same fan, but troublesome if they are - not (which could as well happen). */ + /* + * This chip (stupidly) stops monitoring fan speed if PWM is + * enabled and duty cycle is 0%. This is fine if the monitoring + * and control concern the same fan, but troublesome if they are + * not (which could as well happen). + */ int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 : FAN_FROM_REG(data->fan[nr], DIV_FROM_REG(data->fan_div[nr]), @@ -236,7 +241,13 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct smsc47m1_data *data = dev_get_drvdata(dev); int nr = attr->index; - long rpmdiv, val = simple_strtol(buf, NULL, 10); + long rpmdiv; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]); @@ -254,28 +265,44 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute return count; } -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan clock divider. This follows the principle - of least surprise; the user doesn't expect the fan minimum to change just - because the divider changed. */ +/* + * Note: we save and restore the fan minimum here, because its value is + * determined in part by the fan clock divider. This follows the principle + * of least surprise; the user doesn't expect the fan minimum to change just + * because the divider changed. + */ static ssize_t set_fan_div(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct smsc47m1_data *data = dev_get_drvdata(dev); int nr = attr->index; - long new_div = simple_strtol(buf, NULL, 10), tmp; + long new_div; + int err; + long tmp; u8 old_div = DIV_FROM_REG(data->fan_div[nr]); + err = kstrtol(buf, 10, &new_div); + if (err) + return err; + if (new_div == old_div) /* No change */ return count; mutex_lock(&data->update_lock); switch (new_div) { - case 1: data->fan_div[nr] = 0; break; - case 2: data->fan_div[nr] = 1; break; - case 4: data->fan_div[nr] = 2; break; - case 8: data->fan_div[nr] = 3; break; + case 1: + data->fan_div[nr] = 0; + break; + case 2: + data->fan_div[nr] = 1; + break; + case 4: + data->fan_div[nr] = 2; + break; + case 8: + data->fan_div[nr] = 3; + break; default: mutex_unlock(&data->update_lock); return -EINVAL; @@ -299,7 +326,7 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute /* Preserve fan min */ tmp = 192 - (old_div * (192 - data->fan_preload[nr]) + new_div / 2) / new_div; - data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191); + data->fan_preload[nr] = clamp_val(tmp, 0, 191); smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr], data->fan_preload[nr]); mutex_unlock(&data->update_lock); @@ -313,7 +340,12 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct smsc47m1_data *data = dev_get_drvdata(dev); int nr = attr->index; - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; if (val < 0 || val > 255) return -EINVAL; @@ -334,9 +366,14 @@ static ssize_t set_pwm_en(struct device *dev, struct device_attribute struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct smsc47m1_data *data = dev_get_drvdata(dev); int nr = attr->index; - long val = simple_strtol(buf, NULL, 10); - - if (val != 0 && val != 1) + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + if (val > 1) return -EINVAL; mutex_lock(&data->update_lock); @@ -378,30 +415,73 @@ static ssize_t show_name(struct device *dev, struct device_attribute } static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); -/* Almost all sysfs files may or may not be created depending on the chip - setup so we create them individually. It is still convenient to define a - group to remove them all at once. */ -static struct attribute *smsc47m1_attributes[] = { +static struct attribute *smsc47m1_attributes_fan1[] = { &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan1_min.dev_attr.attr, &sensor_dev_attr_fan1_div.dev_attr.attr, &sensor_dev_attr_fan1_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group smsc47m1_group_fan1 = { + .attrs = smsc47m1_attributes_fan1, +}; + +static struct attribute *smsc47m1_attributes_fan2[] = { &sensor_dev_attr_fan2_input.dev_attr.attr, &sensor_dev_attr_fan2_min.dev_attr.attr, &sensor_dev_attr_fan2_div.dev_attr.attr, &sensor_dev_attr_fan2_alarm.dev_attr.attr, + NULL +}; + +static const struct attribute_group smsc47m1_group_fan2 = { + .attrs = smsc47m1_attributes_fan2, +}; + +static struct attribute *smsc47m1_attributes_fan3[] = { &sensor_dev_attr_fan3_input.dev_attr.attr, &sensor_dev_attr_fan3_min.dev_attr.attr, &sensor_dev_attr_fan3_div.dev_attr.attr, &sensor_dev_attr_fan3_alarm.dev_attr.attr, + NULL +}; +static const struct attribute_group smsc47m1_group_fan3 = { + .attrs = smsc47m1_attributes_fan3, +}; + +static struct attribute *smsc47m1_attributes_pwm1[] = { &sensor_dev_attr_pwm1.dev_attr.attr, &sensor_dev_attr_pwm1_enable.dev_attr.attr, + NULL +}; + +static const struct attribute_group smsc47m1_group_pwm1 = { + .attrs = smsc47m1_attributes_pwm1, +}; + +static struct attribute *smsc47m1_attributes_pwm2[] = { &sensor_dev_attr_pwm2.dev_attr.attr, &sensor_dev_attr_pwm2_enable.dev_attr.attr, + NULL +}; + +static const struct attribute_group smsc47m1_group_pwm2 = { + .attrs = smsc47m1_attributes_pwm2, +}; + +static struct attribute *smsc47m1_attributes_pwm3[] = { &sensor_dev_attr_pwm3.dev_attr.attr, &sensor_dev_attr_pwm3_enable.dev_attr.attr, + NULL +}; + +static const struct attribute_group smsc47m1_group_pwm3 = { + .attrs = smsc47m1_attributes_pwm3, +}; +static struct attribute *smsc47m1_attributes[] = { &dev_attr_alarms.attr, &dev_attr_name.attr, NULL @@ -411,10 +491,10 @@ static const struct attribute_group smsc47m1_group = { .attrs = smsc47m1_attributes, }; -static int __init smsc47m1_find(unsigned short *addr, - struct smsc47m1_sio_data *sio_data) +static int __init smsc47m1_find(struct smsc47m1_sio_data *sio_data) { u8 val; + unsigned short addr; superio_enter(); val = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID); @@ -435,30 +515,29 @@ static int __init smsc47m1_find(unsigned short *addr, */ switch (val) { case 0x51: - pr_info(DRVNAME ": Found SMSC LPC47B27x\n"); + pr_info("Found SMSC LPC47B27x\n"); sio_data->type = smsc47m1; break; case 0x59: - pr_info(DRVNAME ": Found SMSC LPC47M10x/LPC47M112/LPC47M13x\n"); + pr_info("Found SMSC LPC47M10x/LPC47M112/LPC47M13x\n"); sio_data->type = smsc47m1; break; case 0x5F: - pr_info(DRVNAME ": Found SMSC LPC47M14x\n"); + pr_info("Found SMSC LPC47M14x\n"); sio_data->type = smsc47m1; break; case 0x60: - pr_info(DRVNAME ": Found SMSC LPC47M15x/LPC47M192/LPC47M997\n"); + pr_info("Found SMSC LPC47M15x/LPC47M192/LPC47M997\n"); sio_data->type = smsc47m1; break; case 0x6B: if (superio_inb(SUPERIO_REG_DEVREV) & 0x80) { - pr_debug(DRVNAME ": " - "Found SMSC LPC47M233, unsupported\n"); + pr_debug("Found SMSC LPC47M233, unsupported\n"); superio_exit(); return -ENODEV; } - pr_info(DRVNAME ": Found SMSC LPC47M292\n"); + pr_info("Found SMSC LPC47M292\n"); sio_data->type = smsc47m2; break; default: @@ -467,24 +546,26 @@ static int __init smsc47m1_find(unsigned short *addr, } superio_select(); - *addr = (superio_inb(SUPERIO_REG_BASE) << 8) + addr = (superio_inb(SUPERIO_REG_BASE) << 8) | superio_inb(SUPERIO_REG_BASE + 1); - if (*addr == 0) { - pr_info(DRVNAME ": Device address not set, will not use\n"); + if (addr == 0) { + pr_info("Device address not set, will not use\n"); superio_exit(); return -ENODEV; } - /* Enable only if address is set (needed at least on the - * Compaq Presario S4000NX) */ + /* + * Enable only if address is set (needed at least on the + * Compaq Presario S4000NX) + */ sio_data->activate = superio_inb(SUPERIO_REG_ACT); if ((sio_data->activate & 0x01) == 0) { - pr_info(DRVNAME ": Enabling device\n"); + pr_info("Enabling device\n"); superio_outb(SUPERIO_REG_ACT, sio_data->activate | 0x01); } superio_exit(); - return 0; + return addr; } /* Restore device to its initial state */ @@ -494,7 +575,7 @@ static void smsc47m1_restore(const struct smsc47m1_sio_data *sio_data) superio_enter(); superio_select(); - pr_info(DRVNAME ": Disabling device\n"); + pr_info("Disabling device\n"); superio_outb(SUPERIO_REG_ACT, sio_data->activate); superio_exit(); @@ -503,18 +584,17 @@ static void smsc47m1_restore(const struct smsc47m1_sio_data *sio_data) #define CHECK 1 #define REQUEST 2 -#define RELEASE 3 /* * This function can be used to: * - test for resource conflicts with ACPI * - request the resources - * - release the resources * We only allocate the I/O ports we really need, to minimize the risk of * conflicts with ACPI or with other drivers. */ -static int smsc47m1_handle_resources(unsigned short address, enum chips type, - int action, struct device *dev) +static int __init smsc47m1_handle_resources(unsigned short address, + enum chips type, int action, + struct device *dev) { static const u8 ports_m1[] = { /* register, region length */ @@ -561,37 +641,40 @@ static int smsc47m1_handle_resources(unsigned short address, enum chips type, break; case REQUEST: /* Request the resources */ - if (!request_region(start, len, DRVNAME)) { - dev_err(dev, "Region 0x%hx-0x%hx already in " - "use!\n", start, start + len); - - /* Undo all requests */ - for (i -= 2; i >= 0; i -= 2) - release_region(address + ports[i], - ports[i + 1]); + if (!devm_request_region(dev, start, len, DRVNAME)) { + dev_err(dev, + "Region 0x%hx-0x%hx already in use!\n", + start, start + len); return -EBUSY; } break; - case RELEASE: - /* Release the resources */ - release_region(start, len); - break; } } return 0; } +static void smsc47m1_remove_files(struct device *dev) +{ + sysfs_remove_group(&dev->kobj, &smsc47m1_group); + sysfs_remove_group(&dev->kobj, &smsc47m1_group_fan1); + sysfs_remove_group(&dev->kobj, &smsc47m1_group_fan2); + sysfs_remove_group(&dev->kobj, &smsc47m1_group_fan3); + sysfs_remove_group(&dev->kobj, &smsc47m1_group_pwm1); + sysfs_remove_group(&dev->kobj, &smsc47m1_group_pwm2); + sysfs_remove_group(&dev->kobj, &smsc47m1_group_pwm3); +} + static int __init smsc47m1_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct smsc47m1_sio_data *sio_data = dev->platform_data; + struct smsc47m1_sio_data *sio_data = dev_get_platdata(dev); struct smsc47m1_data *data; struct resource *res; int err; int fan1, fan2, fan3, pwm1, pwm2, pwm3; - static const char *names[] = { + static const char * const names[] = { "smsc47m1", "smsc47m2", }; @@ -602,10 +685,9 @@ static int __init smsc47m1_probe(struct platform_device *pdev) if (err < 0) return err; - if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) { - err = -ENOMEM; - goto error_release; - } + data = devm_kzalloc(dev, sizeof(struct smsc47m1_data), GFP_KERNEL); + if (!data) + return -ENOMEM; data->addr = res->start; data->type = sio_data->type; @@ -613,8 +695,10 @@ static int __init smsc47m1_probe(struct platform_device *pdev) mutex_init(&data->update_lock); platform_set_drvdata(pdev, data); - /* If no function is properly configured, there's no point in - actually registering the chip. */ + /* + * If no function is properly configured, there's no point in + * actually registering the chip. + */ pwm1 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(0)) & 0x05) == 0x04; pwm2 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(1)) & 0x05) @@ -638,88 +722,70 @@ static int __init smsc47m1_probe(struct platform_device *pdev) } if (!(fan1 || fan2 || fan3 || pwm1 || pwm2 || pwm3)) { dev_warn(dev, "Device not configured, will not use\n"); - err = -ENODEV; - goto error_free; + return -ENODEV; } - /* Some values (fan min, clock dividers, pwm registers) may be - needed before any update is triggered, so we better read them - at least once here. We don't usually do it that way, but in - this particular case, manually reading 5 registers out of 8 - doesn't make much sense and we're better using the existing - function. */ + /* + * Some values (fan min, clock dividers, pwm registers) may be + * needed before any update is triggered, so we better read them + * at least once here. We don't usually do it that way, but in + * this particular case, manually reading 5 registers out of 8 + * doesn't make much sense and we're better using the existing + * function. + */ smsc47m1_update_device(dev, 1); /* Register sysfs hooks */ if (fan1) { - if ((err = device_create_file(dev, - &sensor_dev_attr_fan1_input.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan1_min.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan1_div.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan1_alarm.dev_attr))) + err = sysfs_create_group(&dev->kobj, + &smsc47m1_group_fan1); + if (err) goto error_remove_files; } else dev_dbg(dev, "Fan 1 not enabled by hardware, skipping\n"); if (fan2) { - if ((err = device_create_file(dev, - &sensor_dev_attr_fan2_input.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan2_min.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan2_div.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan2_alarm.dev_attr))) + err = sysfs_create_group(&dev->kobj, + &smsc47m1_group_fan2); + if (err) goto error_remove_files; } else dev_dbg(dev, "Fan 2 not enabled by hardware, skipping\n"); if (fan3) { - if ((err = device_create_file(dev, - &sensor_dev_attr_fan3_input.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan3_min.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan3_div.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_fan3_alarm.dev_attr))) + err = sysfs_create_group(&dev->kobj, + &smsc47m1_group_fan3); + if (err) goto error_remove_files; } else if (data->type == smsc47m2) dev_dbg(dev, "Fan 3 not enabled by hardware, skipping\n"); if (pwm1) { - if ((err = device_create_file(dev, - &sensor_dev_attr_pwm1.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_pwm1_enable.dev_attr))) + err = sysfs_create_group(&dev->kobj, + &smsc47m1_group_pwm1); + if (err) goto error_remove_files; } else dev_dbg(dev, "PWM 1 not enabled by hardware, skipping\n"); if (pwm2) { - if ((err = device_create_file(dev, - &sensor_dev_attr_pwm2.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_pwm2_enable.dev_attr))) + err = sysfs_create_group(&dev->kobj, + &smsc47m1_group_pwm2); + if (err) goto error_remove_files; } else dev_dbg(dev, "PWM 2 not enabled by hardware, skipping\n"); if (pwm3) { - if ((err = device_create_file(dev, - &sensor_dev_attr_pwm3.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_pwm3_enable.dev_attr))) + err = sysfs_create_group(&dev->kobj, + &smsc47m1_group_pwm3); + if (err) goto error_remove_files; } else if (data->type == smsc47m2) dev_dbg(dev, "PWM 3 not enabled by hardware, skipping\n"); - if ((err = device_create_file(dev, &dev_attr_alarms))) - goto error_remove_files; - if ((err = device_create_file(dev, &dev_attr_name))) + err = sysfs_create_group(&dev->kobj, &smsc47m1_group); + if (err) goto error_remove_files; data->hwmon_dev = hwmon_device_register(dev); @@ -731,27 +797,16 @@ static int __init smsc47m1_probe(struct platform_device *pdev) return 0; error_remove_files: - sysfs_remove_group(&dev->kobj, &smsc47m1_group); -error_free: - platform_set_drvdata(pdev, NULL); - kfree(data); -error_release: - smsc47m1_handle_resources(res->start, sio_data->type, RELEASE, dev); + smsc47m1_remove_files(dev); return err; } static int __exit smsc47m1_remove(struct platform_device *pdev) { struct smsc47m1_data *data = platform_get_drvdata(pdev); - struct resource *res; hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&pdev->dev.kobj, &smsc47m1_group); - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - smsc47m1_handle_resources(res->start, data->type, RELEASE, &pdev->dev); - platform_set_drvdata(pdev, NULL); - kfree(data); + smsc47m1_remove_files(&pdev->dev); return 0; } @@ -823,28 +878,26 @@ static int __init smsc47m1_device_add(unsigned short address, pdev = platform_device_alloc(DRVNAME, address); if (!pdev) { err = -ENOMEM; - printk(KERN_ERR DRVNAME ": Device allocation failed\n"); + pr_err("Device allocation failed\n"); goto exit; } err = platform_device_add_resources(pdev, &res, 1); if (err) { - printk(KERN_ERR DRVNAME ": Device resource addition failed " - "(%d)\n", err); + pr_err("Device resource addition failed (%d)\n", err); goto exit_device_put; } err = platform_device_add_data(pdev, sio_data, sizeof(struct smsc47m1_sio_data)); if (err) { - printk(KERN_ERR DRVNAME ": Platform data allocation failed\n"); + pr_err("Platform data allocation failed\n"); goto exit_device_put; } err = platform_device_add(pdev); if (err) { - printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", - err); + pr_err("Device addition failed (%d)\n", err); goto exit_device_put; } @@ -862,13 +915,15 @@ static int __init sm_smsc47m1_init(void) unsigned short address; struct smsc47m1_sio_data sio_data; - if (smsc47m1_find(&address, &sio_data)) - return -ENODEV; + err = smsc47m1_find(&sio_data); + if (err < 0) + return err; + address = err; /* Sets global pdev as a side effect */ err = smsc47m1_device_add(address, &sio_data); if (err) - goto exit; + return err; err = platform_driver_probe(&smsc47m1_driver, smsc47m1_probe); if (err) @@ -879,14 +934,13 @@ static int __init sm_smsc47m1_init(void) exit_device: platform_device_unregister(pdev); smsc47m1_restore(&sio_data); -exit: return err; } static void __exit sm_smsc47m1_exit(void) { platform_driver_unregister(&smsc47m1_driver); - smsc47m1_restore(pdev->dev.platform_data); + smsc47m1_restore(dev_get_platdata(&pdev->dev)); platform_device_unregister(pdev); } diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c index 40b26673d87..34b9a601ad0 100644 --- a/drivers/hwmon/smsc47m192.c +++ b/drivers/hwmon/smsc47m192.c @@ -1,25 +1,25 @@ /* - smsc47m192.c - Support for hardware monitoring block of - SMSC LPC47M192 and compatible Super I/O chips - - Copyright (C) 2006 Hartmut Rick <linux@rick.claranet.de> - - Derived from lm78.c and other chip drivers. - - 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. -*/ + * smsc47m192.c - Support for hardware monitoring block of + * SMSC LPC47M192 and compatible Super I/O chips + * + * Copyright (C) 2006 Hartmut Rick <linux@rick.claranet.de> + * + * Derived from lm78.c and other chip drivers. + * + * 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> @@ -37,16 +37,16 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END }; /* SMSC47M192 registers */ -#define SMSC47M192_REG_IN(nr) ((nr)<6 ? (0x20 + (nr)) : \ +#define SMSC47M192_REG_IN(nr) ((nr) < 6 ? (0x20 + (nr)) : \ (0x50 + (nr) - 6)) -#define SMSC47M192_REG_IN_MAX(nr) ((nr)<6 ? (0x2b + (nr) * 2) : \ +#define SMSC47M192_REG_IN_MAX(nr) ((nr) < 6 ? (0x2b + (nr) * 2) : \ (0x54 + (((nr) - 6) * 2))) -#define SMSC47M192_REG_IN_MIN(nr) ((nr)<6 ? (0x2c + (nr) * 2) : \ +#define SMSC47M192_REG_IN_MIN(nr) ((nr) < 6 ? (0x2c + (nr) * 2) : \ (0x55 + (((nr) - 6) * 2))) static u8 SMSC47M192_REG_TEMP[3] = { 0x27, 0x26, 0x52 }; static u8 SMSC47M192_REG_TEMP_MAX[3] = { 0x39, 0x37, 0x58 }; static u8 SMSC47M192_REG_TEMP_MIN[3] = { 0x3A, 0x38, 0x59 }; -#define SMSC47M192_REG_TEMP_OFFSET(nr) ((nr)==2 ? 0x1e : 0x1f) +#define SMSC47M192_REG_TEMP_OFFSET(nr) ((nr) == 2 ? 0x1e : 0x1f) #define SMSC47M192_REG_ALARM1 0x41 #define SMSC47M192_REG_ALARM2 0x42 #define SMSC47M192_REG_VID 0x47 @@ -77,14 +77,16 @@ static inline unsigned int IN_FROM_REG(u8 reg, int n) static inline u8 IN_TO_REG(unsigned long val, int n) { - return SENSORS_LIMIT(SCALE(val, 192, nom_mv[n]), 0, 255); + return clamp_val(SCALE(val, 192, nom_mv[n]), 0, 255); } -/* TEMP: 0.001 degC units (-128C to +127C) - REG: 1C/bit, two's complement */ +/* + * TEMP: 0.001 degC units (-128C to +127C) + * REG: 1C/bit, two's complement + */ static inline s8 TEMP_TO_REG(int val) { - return SENSORS_LIMIT(SCALE(val, 1, 1000), -128000, 127000); + return SCALE(clamp_val(val, -128000, 127000), 1, 1000); } static inline int TEMP_FROM_REG(s8 val) @@ -170,7 +172,12 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct smsc47m192_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_min[nr] = IN_TO_REG(val, nr); @@ -187,7 +194,12 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct smsc47m192_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_max[nr] = IN_TO_REG(val, nr); @@ -249,7 +261,12 @@ static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct smsc47m192_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_min[nr] = TEMP_TO_REG(val); @@ -266,7 +283,12 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct smsc47m192_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_max[nr] = TEMP_TO_REG(val); @@ -293,22 +315,29 @@ static ssize_t set_temp_offset(struct device *dev, struct device_attribute struct i2c_client *client = to_i2c_client(dev); struct smsc47m192_data *data = i2c_get_clientdata(client); u8 sfr = i2c_smbus_read_byte_data(client, SMSC47M192_REG_SFR); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_offset[nr] = TEMP_TO_REG(val); - if (nr>1) + if (nr > 1) i2c_smbus_write_byte_data(client, SMSC47M192_REG_TEMP_OFFSET(nr), data->temp_offset[nr]); else if (data->temp_offset[nr] != 0) { - /* offset[0] and offset[1] share the same register, - SFR bit 4 activates offset[0] */ + /* + * offset[0] and offset[1] share the same register, + * SFR bit 4 activates offset[0] + */ i2c_smbus_write_byte_data(client, SMSC47M192_REG_SFR, - (sfr & 0xef) | (nr==0 ? 0x10 : 0)); + (sfr & 0xef) | (nr == 0 ? 0x10 : 0)); data->temp_offset[1-nr] = 0; i2c_smbus_write_byte_data(client, SMSC47M192_REG_TEMP_OFFSET(nr), data->temp_offset[nr]); - } else if ((sfr & 0x10) == (nr==0 ? 0x10 : 0)) + } else if ((sfr & 0x10) == (nr == 0 ? 0x10 : 0)) i2c_smbus_write_byte_data(client, SMSC47M192_REG_TEMP_OFFSET(nr), 0); mutex_unlock(&data->update_lock); @@ -349,7 +378,16 @@ static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct smsc47m192_data *data = dev_get_drvdata(dev); - data->vrm = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + if (val > 255) + return -EINVAL; + + data->vrm = val; return count; } static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); @@ -458,13 +496,13 @@ static void smsc47m192_init_client(struct i2c_client *client) (sfr & 0xfd) | 0x02); if (!(config & 0x01)) { /* initialize alarm limits */ - for (i=0; i<8; i++) { + for (i = 0; i < 8; i++) { i2c_smbus_write_byte_data(client, SMSC47M192_REG_IN_MIN(i), 0); i2c_smbus_write_byte_data(client, SMSC47M192_REG_IN_MAX(i), 0xff); } - for (i=0; i<3; i++) { + for (i = 0; i < 3; i++) { i2c_smbus_write_byte_data(client, SMSC47M192_REG_TEMP_MIN[i], 0x80); i2c_smbus_write_byte_data(client, @@ -518,11 +556,10 @@ static int smsc47m192_probe(struct i2c_client *client, int config; int err; - data = kzalloc(sizeof(struct smsc47m192_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&client->dev, sizeof(struct smsc47m192_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); data->vrm = vid_which_vrm(); @@ -532,14 +569,16 @@ static int smsc47m192_probe(struct i2c_client *client, smsc47m192_init_client(client); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&client->dev.kobj, &smsc47m192_group))) - goto exit_free; + err = sysfs_create_group(&client->dev.kobj, &smsc47m192_group); + if (err) + return err; /* Pin 110 is either in4 (+12V) or VID4 */ config = i2c_smbus_read_byte_data(client, SMSC47M192_REG_CONFIG); if (!(config & 0x20)) { - if ((err = sysfs_create_group(&client->dev.kobj, - &smsc47m192_group_in4))) + err = sysfs_create_group(&client->dev.kobj, + &smsc47m192_group_in4); + if (err) goto exit_remove_files; } @@ -554,9 +593,6 @@ static int smsc47m192_probe(struct i2c_client *client, exit_remove_files: sysfs_remove_group(&client->dev.kobj, &smsc47m192_group); sysfs_remove_group(&client->dev.kobj, &smsc47m192_group_in4); -exit_free: - kfree(data); -exit: return err; } @@ -568,8 +604,6 @@ static int smsc47m192_remove(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &smsc47m192_group); sysfs_remove_group(&client->dev.kobj, &smsc47m192_group_in4); - kfree(data); - return 0; } @@ -606,8 +640,10 @@ static struct smsc47m192_data *smsc47m192_update_device(struct device *dev) for (i = 1; i < 3; i++) data->temp_offset[i] = i2c_smbus_read_byte_data(client, SMSC47M192_REG_TEMP_OFFSET(i)); - /* first offset is temp_offset[0] if SFR bit 4 is set, - temp_offset[1] otherwise */ + /* + * first offset is temp_offset[0] if SFR bit 4 is set, + * temp_offset[1] otherwise + */ if (sfr & 0x10) { data->temp_offset[0] = data->temp_offset[1]; data->temp_offset[1] = 0; @@ -624,7 +660,7 @@ static struct smsc47m192_data *smsc47m192_update_device(struct device *dev) data->alarms = i2c_smbus_read_byte_data(client, SMSC47M192_REG_ALARM1) | (i2c_smbus_read_byte_data(client, - SMSC47M192_REG_ALARM2) << 8); + SMSC47M192_REG_ALARM2) << 8); data->last_updated = jiffies; data->valid = 1; @@ -635,19 +671,8 @@ static struct smsc47m192_data *smsc47m192_update_device(struct device *dev) return data; } -static int __init smsc47m192_init(void) -{ - return i2c_add_driver(&smsc47m192_driver); -} - -static void __exit smsc47m192_exit(void) -{ - i2c_del_driver(&smsc47m192_driver); -} +module_i2c_driver(smsc47m192_driver); MODULE_AUTHOR("Hartmut Rick <linux@rick.claranet.de>"); MODULE_DESCRIPTION("SMSC47M192 driver"); MODULE_LICENSE("GPL"); - -module_init(smsc47m192_init); -module_exit(smsc47m192_exit); diff --git a/drivers/hwmon/thmc50.c b/drivers/hwmon/thmc50.c index 7dfb4dec4c5..db288db7d3e 100644 --- a/drivers/hwmon/thmc50.c +++ b/drivers/hwmon/thmc50.c @@ -1,24 +1,24 @@ /* - thmc50.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (C) 2007 Krzysztof Helt <krzysztof.h1@wp.pl> - Based on 2.4 driver by Frodo Looijaard <frodol@dds.nl> and - Philip Edelbrock <phil@netroedge.com> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ + * thmc50.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (C) 2007 Krzysztof Helt <krzysztof.h1@wp.pl> + * Based on 2.4 driver by Frodo Looijaard <frodol@dds.nl> and + * Philip Edelbrock <phil@netroedge.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ #include <linux/module.h> #include <linux/init.h> @@ -28,6 +28,7 @@ #include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/jiffies.h> MODULE_LICENSE("GPL"); @@ -40,8 +41,8 @@ enum chips { thmc50, adm1022 }; static unsigned short adm1022_temp3[16]; static unsigned int adm1022_temp3_num; module_param_array(adm1022_temp3, ushort, &adm1022_temp3_num, 0); -MODULE_PARM_DESC(adm1022_temp3, "List of adapter,address pairs " - "to enable 3rd temperature (ADM1022 only)"); +MODULE_PARM_DESC(adm1022_temp3, + "List of adapter,address pairs to enable 3rd temperature (ADM1022 only)"); /* Many THMC50 constants specified below */ @@ -124,11 +125,16 @@ static ssize_t set_analog_out(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct thmc50_data *data = i2c_get_clientdata(client); - int tmp = simple_strtoul(buf, NULL, 10); int config; + unsigned long tmp; + int err; + + err = kstrtoul(buf, 10, &tmp); + if (err) + return err; mutex_lock(&data->update_lock); - data->analog_out = SENSORS_LIMIT(tmp, 0, 255); + data->analog_out = clamp_val(tmp, 0, 255); i2c_smbus_write_byte_data(client, THMC50_REG_ANALOG_OUT, data->analog_out); @@ -173,10 +179,15 @@ static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct thmc50_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - data->temp_min[nr] = SENSORS_LIMIT(val / 1000, -128, 127); + data->temp_min[nr] = clamp_val(val / 1000, -128, 127); i2c_smbus_write_byte_data(client, THMC50_REG_TEMP_MIN[nr], data->temp_min[nr]); mutex_unlock(&data->update_lock); @@ -197,10 +208,15 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct thmc50_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - data->temp_max[nr] = SENSORS_LIMIT(val / 1000, -128, 127); + data->temp_max[nr] = clamp_val(val / 1000, -128, 127); i2c_smbus_write_byte_data(client, THMC50_REG_TEMP_MAX[nr], data->temp_max[nr]); mutex_unlock(&data->update_lock); @@ -296,8 +312,7 @@ static int thmc50_detect(struct i2c_client *client, const char *type_name; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - pr_debug("thmc50: detect failed, " - "smbus byte data not supported!\n"); + pr_debug("thmc50: detect failed, smbus byte data not supported!\n"); return -ENODEV; } @@ -346,12 +361,10 @@ static int thmc50_probe(struct i2c_client *client, struct thmc50_data *data; int err; - data = kzalloc(sizeof(struct thmc50_data), GFP_KERNEL); - if (!data) { - pr_debug("thmc50: detect failed, kzalloc failed!\n"); - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&client->dev, sizeof(struct thmc50_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); data->type = id->driver_data; @@ -360,14 +373,16 @@ static int thmc50_probe(struct i2c_client *client, thmc50_init_client(client); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&client->dev.kobj, &thmc50_group))) - goto exit_free; + err = sysfs_create_group(&client->dev.kobj, &thmc50_group); + if (err) + return err; /* Register ADM1022 sysfs hooks */ - if (data->has_temp3) - if ((err = sysfs_create_group(&client->dev.kobj, - &temp3_group))) + if (data->has_temp3) { + err = sysfs_create_group(&client->dev.kobj, &temp3_group); + if (err) goto exit_remove_sysfs_thmc50; + } /* Register a new directory entry with module sensors */ data->hwmon_dev = hwmon_device_register(&client->dev); @@ -383,9 +398,6 @@ exit_remove_sysfs: sysfs_remove_group(&client->dev.kobj, &temp3_group); exit_remove_sysfs_thmc50: sysfs_remove_group(&client->dev.kobj, &thmc50_group); -exit_free: - kfree(data); -exit: return err; } @@ -398,8 +410,6 @@ static int thmc50_remove(struct i2c_client *client) if (data->has_temp3) sysfs_remove_group(&client->dev.kobj, &temp3_group); - kfree(data); - return 0; } @@ -465,18 +475,7 @@ static struct thmc50_data *thmc50_update_device(struct device *dev) return data; } -static int __init sm_thmc50_init(void) -{ - return i2c_add_driver(&thmc50_driver); -} - -static void __exit sm_thmc50_exit(void) -{ - i2c_del_driver(&thmc50_driver); -} +module_i2c_driver(thmc50_driver); MODULE_AUTHOR("Krzysztof Helt <krzysztof.h1@wp.pl>"); MODULE_DESCRIPTION("THMC50 driver"); - -module_init(sm_thmc50_init); -module_exit(sm_thmc50_exit); diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c new file mode 100644 index 00000000000..51719956cc0 --- /dev/null +++ b/drivers/hwmon/tmp102.c @@ -0,0 +1,307 @@ +/* Texas Instruments TMP102 SMBus temperature sensor driver + * + * Copyright (C) 2010 Steven King <sfking@fdwdc.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., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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/device.h> +#include <linux/jiffies.h> +#include <linux/thermal.h> +#include <linux/of.h> + +#define DRIVER_NAME "tmp102" + +#define TMP102_TEMP_REG 0x00 +#define TMP102_CONF_REG 0x01 +/* note: these bit definitions are byte swapped */ +#define TMP102_CONF_SD 0x0100 +#define TMP102_CONF_TM 0x0200 +#define TMP102_CONF_POL 0x0400 +#define TMP102_CONF_F0 0x0800 +#define TMP102_CONF_F1 0x1000 +#define TMP102_CONF_R0 0x2000 +#define TMP102_CONF_R1 0x4000 +#define TMP102_CONF_OS 0x8000 +#define TMP102_CONF_EM 0x0010 +#define TMP102_CONF_AL 0x0020 +#define TMP102_CONF_CR0 0x0040 +#define TMP102_CONF_CR1 0x0080 +#define TMP102_TLOW_REG 0x02 +#define TMP102_THIGH_REG 0x03 + +struct tmp102 { + struct i2c_client *client; + struct device *hwmon_dev; + struct thermal_zone_device *tz; + struct mutex lock; + u16 config_orig; + unsigned long last_update; + int temp[3]; +}; + +/* convert left adjusted 13-bit TMP102 register value to milliCelsius */ +static inline int tmp102_reg_to_mC(s16 val) +{ + return ((val & ~0x01) * 1000) / 128; +} + +/* convert milliCelsius to left adjusted 13-bit TMP102 register value */ +static inline u16 tmp102_mC_to_reg(int val) +{ + return (val * 128) / 1000; +} + +static const u8 tmp102_reg[] = { + TMP102_TEMP_REG, + TMP102_TLOW_REG, + TMP102_THIGH_REG, +}; + +static struct tmp102 *tmp102_update_device(struct device *dev) +{ + struct tmp102 *tmp102 = dev_get_drvdata(dev); + struct i2c_client *client = tmp102->client; + + mutex_lock(&tmp102->lock); + if (time_after(jiffies, tmp102->last_update + HZ / 3)) { + int i; + for (i = 0; i < ARRAY_SIZE(tmp102->temp); ++i) { + int status = i2c_smbus_read_word_swapped(client, + tmp102_reg[i]); + if (status > -1) + tmp102->temp[i] = tmp102_reg_to_mC(status); + } + tmp102->last_update = jiffies; + } + mutex_unlock(&tmp102->lock); + return tmp102; +} + +static int tmp102_read_temp(void *dev, long *temp) +{ + struct tmp102 *tmp102 = tmp102_update_device(dev); + + *temp = tmp102->temp[0]; + + return 0; +} + +static ssize_t tmp102_show_temp(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sda = to_sensor_dev_attr(attr); + struct tmp102 *tmp102 = tmp102_update_device(dev); + + return sprintf(buf, "%d\n", tmp102->temp[sda->index]); +} + +static ssize_t tmp102_set_temp(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sda = to_sensor_dev_attr(attr); + struct tmp102 *tmp102 = dev_get_drvdata(dev); + struct i2c_client *client = tmp102->client; + long val; + int status; + + if (kstrtol(buf, 10, &val) < 0) + return -EINVAL; + val = clamp_val(val, -256000, 255000); + + mutex_lock(&tmp102->lock); + tmp102->temp[sda->index] = val; + status = i2c_smbus_write_word_swapped(client, tmp102_reg[sda->index], + tmp102_mC_to_reg(val)); + mutex_unlock(&tmp102->lock); + return status ? : count; +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, tmp102_show_temp, NULL , 0); + +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, tmp102_show_temp, + tmp102_set_temp, 1); + +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, tmp102_show_temp, + tmp102_set_temp, 2); + +static struct attribute *tmp102_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(tmp102); + +#define TMP102_CONFIG (TMP102_CONF_TM | TMP102_CONF_EM | TMP102_CONF_CR1) +#define TMP102_CONFIG_RD_ONLY (TMP102_CONF_R0 | TMP102_CONF_R1 | TMP102_CONF_AL) + +static int tmp102_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct tmp102 *tmp102; + int status; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WORD_DATA)) { + dev_err(dev, + "adapter doesn't support SMBus word transactions\n"); + return -ENODEV; + } + + tmp102 = devm_kzalloc(dev, sizeof(*tmp102), GFP_KERNEL); + if (!tmp102) + return -ENOMEM; + + i2c_set_clientdata(client, tmp102); + tmp102->client = client; + + status = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG); + if (status < 0) { + dev_err(dev, "error reading config register\n"); + return status; + } + tmp102->config_orig = status; + status = i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, + TMP102_CONFIG); + if (status < 0) { + dev_err(dev, "error writing config register\n"); + goto fail_restore_config; + } + status = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG); + if (status < 0) { + dev_err(dev, "error reading config register\n"); + goto fail_restore_config; + } + status &= ~TMP102_CONFIG_RD_ONLY; + if (status != TMP102_CONFIG) { + dev_err(dev, "config settings did not stick\n"); + status = -ENODEV; + goto fail_restore_config; + } + tmp102->last_update = jiffies - HZ; + mutex_init(&tmp102->lock); + + hwmon_dev = hwmon_device_register_with_groups(dev, client->name, + tmp102, tmp102_groups); + if (IS_ERR(hwmon_dev)) { + dev_dbg(dev, "unable to register hwmon device\n"); + status = PTR_ERR(hwmon_dev); + goto fail_restore_config; + } + tmp102->hwmon_dev = hwmon_dev; + tmp102->tz = thermal_zone_of_sensor_register(hwmon_dev, 0, hwmon_dev, + tmp102_read_temp, NULL); + if (IS_ERR(tmp102->tz)) + tmp102->tz = NULL; + + dev_info(dev, "initialized\n"); + + return 0; + +fail_restore_config: + i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, + tmp102->config_orig); + return status; +} + +static int tmp102_remove(struct i2c_client *client) +{ + struct tmp102 *tmp102 = i2c_get_clientdata(client); + + thermal_zone_of_sensor_unregister(tmp102->hwmon_dev, tmp102->tz); + hwmon_device_unregister(tmp102->hwmon_dev); + + /* Stop monitoring if device was stopped originally */ + if (tmp102->config_orig & TMP102_CONF_SD) { + int config; + + config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG); + if (config >= 0) + i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, + config | TMP102_CONF_SD); + } + + return 0; +} + +#ifdef CONFIG_PM +static int tmp102_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + int config; + + config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG); + if (config < 0) + return config; + + config |= TMP102_CONF_SD; + return i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, config); +} + +static int tmp102_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + int config; + + config = i2c_smbus_read_word_swapped(client, TMP102_CONF_REG); + if (config < 0) + return config; + + config &= ~TMP102_CONF_SD; + return i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, config); +} + +static const struct dev_pm_ops tmp102_dev_pm_ops = { + .suspend = tmp102_suspend, + .resume = tmp102_resume, +}; + +#define TMP102_DEV_PM_OPS (&tmp102_dev_pm_ops) +#else +#define TMP102_DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + +static const struct i2c_device_id tmp102_id[] = { + { "tmp102", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tmp102_id); + +static struct i2c_driver tmp102_driver = { + .driver.name = DRIVER_NAME, + .driver.pm = TMP102_DEV_PM_OPS, + .probe = tmp102_probe, + .remove = tmp102_remove, + .id_table = tmp102_id, +}; + +module_i2c_driver(tmp102_driver); + +MODULE_AUTHOR("Steven King <sfking@fdwdc.com>"); +MODULE_DESCRIPTION("Texas Instruments TMP102 temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index a13b30e8d8d..7fa6e7d0b9b 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -5,6 +5,9 @@ * Gabriel Konat, Sander Leget, Wouter Willems * Copyright (C) 2009 Andre Prendel <andre.prendel@gmx.de> * + * Cleanup and support for TMP431 and TMP432 by Guenter Roeck + * Copyright (c) 2013 Guenter Roeck <linux@roeck-us.net> + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -30,6 +33,7 @@ #include <linux/module.h> #include <linux/init.h> +#include <linux/bitops.h> #include <linux/slab.h> #include <linux/jiffies.h> #include <linux/i2c.h> @@ -40,9 +44,9 @@ #include <linux/sysfs.h> /* Addresses to scan */ -static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END }; +static const unsigned short normal_i2c[] = { 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; -enum chips { tmp401, tmp411 }; +enum chips { tmp401, tmp411, tmp431, tmp432 }; /* * The TMP401 registers, note some registers have different addresses for @@ -54,53 +58,84 @@ enum chips { tmp401, tmp411 }; #define TMP401_CONVERSION_RATE_READ 0x04 #define TMP401_CONVERSION_RATE_WRITE 0x0A #define TMP401_TEMP_CRIT_HYST 0x21 -#define TMP401_CONSECUTIVE_ALERT 0x22 #define TMP401_MANUFACTURER_ID_REG 0xFE #define TMP401_DEVICE_ID_REG 0xFF -#define TMP411_N_FACTOR_REG 0x18 - -static const u8 TMP401_TEMP_MSB[2] = { 0x00, 0x01 }; -static const u8 TMP401_TEMP_LSB[2] = { 0x15, 0x10 }; -static const u8 TMP401_TEMP_LOW_LIMIT_MSB_READ[2] = { 0x06, 0x08 }; -static const u8 TMP401_TEMP_LOW_LIMIT_MSB_WRITE[2] = { 0x0C, 0x0E }; -static const u8 TMP401_TEMP_LOW_LIMIT_LSB[2] = { 0x17, 0x14 }; -static const u8 TMP401_TEMP_HIGH_LIMIT_MSB_READ[2] = { 0x05, 0x07 }; -static const u8 TMP401_TEMP_HIGH_LIMIT_MSB_WRITE[2] = { 0x0B, 0x0D }; -static const u8 TMP401_TEMP_HIGH_LIMIT_LSB[2] = { 0x16, 0x13 }; -/* These are called the THERM limit / hysteresis / mask in the datasheet */ -static const u8 TMP401_TEMP_CRIT_LIMIT[2] = { 0x20, 0x19 }; - -static const u8 TMP411_TEMP_LOWEST_MSB[2] = { 0x30, 0x34 }; -static const u8 TMP411_TEMP_LOWEST_LSB[2] = { 0x31, 0x35 }; -static const u8 TMP411_TEMP_HIGHEST_MSB[2] = { 0x32, 0x36 }; -static const u8 TMP411_TEMP_HIGHEST_LSB[2] = { 0x33, 0x37 }; + +static const u8 TMP401_TEMP_MSB_READ[6][2] = { + { 0x00, 0x01 }, /* temp */ + { 0x06, 0x08 }, /* low limit */ + { 0x05, 0x07 }, /* high limit */ + { 0x20, 0x19 }, /* therm (crit) limit */ + { 0x30, 0x34 }, /* lowest */ + { 0x32, 0x36 }, /* highest */ +}; + +static const u8 TMP401_TEMP_MSB_WRITE[6][2] = { + { 0, 0 }, /* temp (unused) */ + { 0x0C, 0x0E }, /* low limit */ + { 0x0B, 0x0D }, /* high limit */ + { 0x20, 0x19 }, /* therm (crit) limit */ + { 0x30, 0x34 }, /* lowest */ + { 0x32, 0x36 }, /* highest */ +}; + +static const u8 TMP401_TEMP_LSB[6][2] = { + { 0x15, 0x10 }, /* temp */ + { 0x17, 0x14 }, /* low limit */ + { 0x16, 0x13 }, /* high limit */ + { 0, 0 }, /* therm (crit) limit (unused) */ + { 0x31, 0x35 }, /* lowest */ + { 0x33, 0x37 }, /* highest */ +}; + +static const u8 TMP432_TEMP_MSB_READ[4][3] = { + { 0x00, 0x01, 0x23 }, /* temp */ + { 0x06, 0x08, 0x16 }, /* low limit */ + { 0x05, 0x07, 0x15 }, /* high limit */ + { 0x20, 0x19, 0x1A }, /* therm (crit) limit */ +}; + +static const u8 TMP432_TEMP_MSB_WRITE[4][3] = { + { 0, 0, 0 }, /* temp - unused */ + { 0x0C, 0x0E, 0x16 }, /* low limit */ + { 0x0B, 0x0D, 0x15 }, /* high limit */ + { 0x20, 0x19, 0x1A }, /* therm (crit) limit */ +}; + +static const u8 TMP432_TEMP_LSB[3][3] = { + { 0x29, 0x10, 0x24 }, /* temp */ + { 0x3E, 0x14, 0x18 }, /* low limit */ + { 0x3D, 0x13, 0x17 }, /* high limit */ +}; + +/* [0] = fault, [1] = low, [2] = high, [3] = therm/crit */ +static const u8 TMP432_STATUS_REG[] = { + 0x1b, 0x36, 0x35, 0x37 }; /* Flags */ -#define TMP401_CONFIG_RANGE 0x04 -#define TMP401_CONFIG_SHUTDOWN 0x40 -#define TMP401_STATUS_LOCAL_CRIT 0x01 -#define TMP401_STATUS_REMOTE_CRIT 0x02 -#define TMP401_STATUS_REMOTE_OPEN 0x04 -#define TMP401_STATUS_REMOTE_LOW 0x08 -#define TMP401_STATUS_REMOTE_HIGH 0x10 -#define TMP401_STATUS_LOCAL_LOW 0x20 -#define TMP401_STATUS_LOCAL_HIGH 0x40 +#define TMP401_CONFIG_RANGE BIT(2) +#define TMP401_CONFIG_SHUTDOWN BIT(6) +#define TMP401_STATUS_LOCAL_CRIT BIT(0) +#define TMP401_STATUS_REMOTE_CRIT BIT(1) +#define TMP401_STATUS_REMOTE_OPEN BIT(2) +#define TMP401_STATUS_REMOTE_LOW BIT(3) +#define TMP401_STATUS_REMOTE_HIGH BIT(4) +#define TMP401_STATUS_LOCAL_LOW BIT(5) +#define TMP401_STATUS_LOCAL_HIGH BIT(6) + +/* On TMP432, each status has its own register */ +#define TMP432_STATUS_LOCAL BIT(0) +#define TMP432_STATUS_REMOTE1 BIT(1) +#define TMP432_STATUS_REMOTE2 BIT(2) /* Manufacturer / Device ID's */ #define TMP401_MANUFACTURER_ID 0x55 #define TMP401_DEVICE_ID 0x11 -#define TMP411_DEVICE_ID 0x12 - -/* - * Functions declarations - */ - -static int tmp401_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int tmp401_detect(struct i2c_client *client, - struct i2c_board_info *info); -static int tmp401_remove(struct i2c_client *client); -static struct tmp401_data *tmp401_update_device(struct device *dev); +#define TMP411A_DEVICE_ID 0x12 +#define TMP411B_DEVICE_ID 0x13 +#define TMP411C_DEVICE_ID 0x10 +#define TMP431_DEVICE_ID 0x31 +#define TMP432_DEVICE_ID 0x32 /* * Driver data (common to all clients) @@ -109,43 +144,31 @@ static struct tmp401_data *tmp401_update_device(struct device *dev); static const struct i2c_device_id tmp401_id[] = { { "tmp401", tmp401 }, { "tmp411", tmp411 }, + { "tmp431", tmp431 }, + { "tmp432", tmp432 }, { } }; MODULE_DEVICE_TABLE(i2c, tmp401_id); -static struct i2c_driver tmp401_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "tmp401", - }, - .probe = tmp401_probe, - .remove = tmp401_remove, - .id_table = tmp401_id, - .detect = tmp401_detect, - .address_list = normal_i2c, -}; - /* * Client data (each client gets its own) */ struct tmp401_data { - struct device *hwmon_dev; + struct i2c_client *client; + const struct attribute_group *groups[3]; struct mutex update_lock; char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ - int kind; + enum chips kind; + + unsigned int update_interval; /* in milliseconds */ /* register values */ - u8 status; + u8 status[4]; u8 config; - u16 temp[2]; - u16 temp_low[2]; - u16 temp_high[2]; - u8 temp_crit[2]; + u16 temp[6][3]; u8 temp_crit_hyst; - u16 temp_lowest[2]; - u16 temp_highest[2]; }; /* @@ -159,80 +182,138 @@ static int tmp401_register_to_temp(u16 reg, u8 config) if (config & TMP401_CONFIG_RANGE) temp -= 64 * 256; - return (temp * 625 + 80) / 160; + return DIV_ROUND_CLOSEST(temp * 125, 32); } -static u16 tmp401_temp_to_register(long temp, u8 config) +static u16 tmp401_temp_to_register(long temp, u8 config, int zbits) { if (config & TMP401_CONFIG_RANGE) { - temp = SENSORS_LIMIT(temp, -64000, 191000); + temp = clamp_val(temp, -64000, 191000); temp += 64000; } else - temp = SENSORS_LIMIT(temp, 0, 127000); + temp = clamp_val(temp, 0, 127000); - return (temp * 160 + 312) / 625; + return DIV_ROUND_CLOSEST(temp * (1 << (8 - zbits)), 1000) << zbits; } -static int tmp401_crit_register_to_temp(u8 reg, u8 config) +static int tmp401_update_device_reg16(struct i2c_client *client, + struct tmp401_data *data) { - int temp = reg; - - if (config & TMP401_CONFIG_RANGE) - temp -= 64; - - return temp * 1000; + int i, j, val; + int num_regs = data->kind == tmp411 ? 6 : 4; + int num_sensors = data->kind == tmp432 ? 3 : 2; + + for (i = 0; i < num_sensors; i++) { /* local / r1 / r2 */ + for (j = 0; j < num_regs; j++) { /* temp / low / ... */ + u8 regaddr; + /* + * High byte must be read first immediately followed + * by the low byte + */ + regaddr = data->kind == tmp432 ? + TMP432_TEMP_MSB_READ[j][i] : + TMP401_TEMP_MSB_READ[j][i]; + val = i2c_smbus_read_byte_data(client, regaddr); + if (val < 0) + return val; + data->temp[j][i] = val << 8; + if (j == 3) /* crit is msb only */ + continue; + regaddr = data->kind == tmp432 ? TMP432_TEMP_LSB[j][i] + : TMP401_TEMP_LSB[j][i]; + val = i2c_smbus_read_byte_data(client, regaddr); + if (val < 0) + return val; + data->temp[j][i] |= val; + } + } + return 0; } -static u8 tmp401_crit_temp_to_register(long temp, u8 config) +static struct tmp401_data *tmp401_update_device(struct device *dev) { - if (config & TMP401_CONFIG_RANGE) { - temp = SENSORS_LIMIT(temp, -64000, 191000); - temp += 64000; - } else - temp = SENSORS_LIMIT(temp, 0, 127000); + struct tmp401_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + struct tmp401_data *ret = data; + int i, val; + unsigned long next_update; - return (temp + 500) / 1000; -} + mutex_lock(&data->update_lock); -static ssize_t show_temp_value(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct tmp401_data *data = tmp401_update_device(dev); + next_update = data->last_updated + + msecs_to_jiffies(data->update_interval); + if (time_after(jiffies, next_update) || !data->valid) { + if (data->kind != tmp432) { + /* + * The driver uses the TMP432 status format internally. + * Convert status to TMP432 format for other chips. + */ + val = i2c_smbus_read_byte_data(client, TMP401_STATUS); + if (val < 0) { + ret = ERR_PTR(val); + goto abort; + } + data->status[0] = + (val & TMP401_STATUS_REMOTE_OPEN) >> 1; + data->status[1] = + ((val & TMP401_STATUS_REMOTE_LOW) >> 2) | + ((val & TMP401_STATUS_LOCAL_LOW) >> 5); + data->status[2] = + ((val & TMP401_STATUS_REMOTE_HIGH) >> 3) | + ((val & TMP401_STATUS_LOCAL_HIGH) >> 6); + data->status[3] = val & (TMP401_STATUS_LOCAL_CRIT + | TMP401_STATUS_REMOTE_CRIT); + } else { + for (i = 0; i < ARRAY_SIZE(data->status); i++) { + val = i2c_smbus_read_byte_data(client, + TMP432_STATUS_REG[i]); + if (val < 0) { + ret = ERR_PTR(val); + goto abort; + } + data->status[i] = val; + } + } - return sprintf(buf, "%d\n", - tmp401_register_to_temp(data->temp[index], data->config)); -} + val = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ); + if (val < 0) { + ret = ERR_PTR(val); + goto abort; + } + data->config = val; + val = tmp401_update_device_reg16(client, data); + if (val < 0) { + ret = ERR_PTR(val); + goto abort; + } + val = i2c_smbus_read_byte_data(client, TMP401_TEMP_CRIT_HYST); + if (val < 0) { + ret = ERR_PTR(val); + goto abort; + } + data->temp_crit_hyst = val; -static ssize_t show_temp_min(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct tmp401_data *data = tmp401_update_device(dev); + data->last_updated = jiffies; + data->valid = 1; + } - return sprintf(buf, "%d\n", - tmp401_register_to_temp(data->temp_low[index], data->config)); +abort: + mutex_unlock(&data->update_lock); + return ret; } -static ssize_t show_temp_max(struct device *dev, - struct device_attribute *devattr, char *buf) +static ssize_t show_temp(struct device *dev, + struct device_attribute *devattr, char *buf) { - int index = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->nr; + int index = to_sensor_dev_attr_2(devattr)->index; struct tmp401_data *data = tmp401_update_device(dev); - return sprintf(buf, "%d\n", - tmp401_register_to_temp(data->temp_high[index], data->config)); -} - -static ssize_t show_temp_crit(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct tmp401_data *data = tmp401_update_device(dev); + if (IS_ERR(data)) + return PTR_ERR(data); return sprintf(buf, "%d\n", - tmp401_crit_register_to_temp(data->temp_crit[index], - data->config)); + tmp401_register_to_temp(data->temp[nr][index], data->config)); } static ssize_t show_temp_crit_hyst(struct device *dev, @@ -241,122 +322,57 @@ static ssize_t show_temp_crit_hyst(struct device *dev, int temp, index = to_sensor_dev_attr(devattr)->index; struct tmp401_data *data = tmp401_update_device(dev); + if (IS_ERR(data)) + return PTR_ERR(data); + mutex_lock(&data->update_lock); - temp = tmp401_crit_register_to_temp(data->temp_crit[index], - data->config); + temp = tmp401_register_to_temp(data->temp[3][index], data->config); temp -= data->temp_crit_hyst * 1000; mutex_unlock(&data->update_lock); return sprintf(buf, "%d\n", temp); } -static ssize_t show_temp_lowest(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct tmp401_data *data = tmp401_update_device(dev); - - return sprintf(buf, "%d\n", - tmp401_register_to_temp(data->temp_lowest[index], - data->config)); -} - -static ssize_t show_temp_highest(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct tmp401_data *data = tmp401_update_device(dev); - - return sprintf(buf, "%d\n", - tmp401_register_to_temp(data->temp_highest[index], - data->config)); -} - static ssize_t show_status(struct device *dev, struct device_attribute *devattr, char *buf) { - int mask = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->nr; + int mask = to_sensor_dev_attr_2(devattr)->index; struct tmp401_data *data = tmp401_update_device(dev); - if (data->status & mask) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} + if (IS_ERR(data)) + return PTR_ERR(data); -static ssize_t store_temp_min(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct tmp401_data *data = tmp401_update_device(dev); - long val; - u16 reg; - - if (strict_strtol(buf, 10, &val)) - return -EINVAL; - - reg = tmp401_temp_to_register(val, data->config); - - mutex_lock(&data->update_lock); - - i2c_smbus_write_byte_data(to_i2c_client(dev), - TMP401_TEMP_LOW_LIMIT_MSB_WRITE[index], reg >> 8); - i2c_smbus_write_byte_data(to_i2c_client(dev), - TMP401_TEMP_LOW_LIMIT_LSB[index], reg & 0xFF); - - data->temp_low[index] = reg; - - mutex_unlock(&data->update_lock); - - return count; + return sprintf(buf, "%d\n", !!(data->status[nr] & mask)); } -static ssize_t store_temp_max(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count) +static ssize_t store_temp(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) { - int index = to_sensor_dev_attr(devattr)->index; - struct tmp401_data *data = tmp401_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->nr; + int index = to_sensor_dev_attr_2(devattr)->index; + struct tmp401_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; long val; u16 reg; + u8 regaddr; - if (strict_strtol(buf, 10, &val)) - return -EINVAL; - - reg = tmp401_temp_to_register(val, data->config); - - mutex_lock(&data->update_lock); - - i2c_smbus_write_byte_data(to_i2c_client(dev), - TMP401_TEMP_HIGH_LIMIT_MSB_WRITE[index], reg >> 8); - i2c_smbus_write_byte_data(to_i2c_client(dev), - TMP401_TEMP_HIGH_LIMIT_LSB[index], reg & 0xFF); - - data->temp_high[index] = reg; - - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t store_temp_crit(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct tmp401_data *data = tmp401_update_device(dev); - long val; - u8 reg; - - if (strict_strtol(buf, 10, &val)) + if (kstrtol(buf, 10, &val)) return -EINVAL; - reg = tmp401_crit_temp_to_register(val, data->config); + reg = tmp401_temp_to_register(val, data->config, nr == 3 ? 8 : 4); mutex_lock(&data->update_lock); - i2c_smbus_write_byte_data(to_i2c_client(dev), - TMP401_TEMP_CRIT_LIMIT[index], reg); - - data->temp_crit[index] = reg; + regaddr = data->kind == tmp432 ? TMP432_TEMP_MSB_WRITE[nr][index] + : TMP401_TEMP_MSB_WRITE[nr][index]; + i2c_smbus_write_byte_data(client, regaddr, reg >> 8); + if (nr != 3) { + regaddr = data->kind == tmp432 ? TMP432_TEMP_LSB[nr][index] + : TMP401_TEMP_LSB[nr][index]; + i2c_smbus_write_byte_data(client, regaddr, reg & 0xFF); + } + data->temp[nr][index] = reg; mutex_unlock(&data->update_lock); @@ -371,22 +387,24 @@ static ssize_t store_temp_crit_hyst(struct device *dev, struct device_attribute long val; u8 reg; - if (strict_strtol(buf, 10, &val)) + if (IS_ERR(data)) + return PTR_ERR(data); + + if (kstrtol(buf, 10, &val)) return -EINVAL; if (data->config & TMP401_CONFIG_RANGE) - val = SENSORS_LIMIT(val, -64000, 191000); + val = clamp_val(val, -64000, 191000); else - val = SENSORS_LIMIT(val, 0, 127000); + val = clamp_val(val, 0, 127000); mutex_lock(&data->update_lock); - temp = tmp401_crit_register_to_temp(data->temp_crit[index], - data->config); - val = SENSORS_LIMIT(val, temp - 255000, temp); + temp = tmp401_register_to_temp(data->temp[3][index], data->config); + val = clamp_val(val, temp - 255000, temp); reg = ((temp - val) + 500) / 1000; - i2c_smbus_write_byte_data(to_i2c_client(dev), - TMP401_TEMP_CRIT_HYST, reg); + i2c_smbus_write_byte_data(data->client, TMP401_TEMP_CRIT_HYST, + reg); data->temp_crit_hyst = reg; @@ -403,48 +421,129 @@ static ssize_t store_temp_crit_hyst(struct device *dev, struct device_attribute static ssize_t reset_temp_history(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { + struct tmp401_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; long val; - if (strict_strtol(buf, 10, &val)) + if (kstrtol(buf, 10, &val)) return -EINVAL; if (val != 1) { - dev_err(dev, "temp_reset_history value %ld not" - " supported. Use 1 to reset the history!\n", val); + dev_err(dev, + "temp_reset_history value %ld not supported. Use 1 to reset the history!\n", + val); return -EINVAL; } - i2c_smbus_write_byte_data(to_i2c_client(dev), - TMP411_TEMP_LOWEST_MSB[0], val); + mutex_lock(&data->update_lock); + i2c_smbus_write_byte_data(client, TMP401_TEMP_MSB_WRITE[5][0], val); + data->valid = 0; + mutex_unlock(&data->update_lock); return count; } -static struct sensor_device_attribute tmp401_attr[] = { - SENSOR_ATTR(temp1_input, 0444, show_temp_value, NULL, 0), - SENSOR_ATTR(temp1_min, 0644, show_temp_min, store_temp_min, 0), - SENSOR_ATTR(temp1_max, 0644, show_temp_max, store_temp_max, 0), - SENSOR_ATTR(temp1_crit, 0644, show_temp_crit, store_temp_crit, 0), - SENSOR_ATTR(temp1_crit_hyst, 0644, show_temp_crit_hyst, - store_temp_crit_hyst, 0), - SENSOR_ATTR(temp1_min_alarm, 0444, show_status, NULL, - TMP401_STATUS_LOCAL_LOW), - SENSOR_ATTR(temp1_max_alarm, 0444, show_status, NULL, - TMP401_STATUS_LOCAL_HIGH), - SENSOR_ATTR(temp1_crit_alarm, 0444, show_status, NULL, - TMP401_STATUS_LOCAL_CRIT), - SENSOR_ATTR(temp2_input, 0444, show_temp_value, NULL, 1), - SENSOR_ATTR(temp2_min, 0644, show_temp_min, store_temp_min, 1), - SENSOR_ATTR(temp2_max, 0644, show_temp_max, store_temp_max, 1), - SENSOR_ATTR(temp2_crit, 0644, show_temp_crit, store_temp_crit, 1), - SENSOR_ATTR(temp2_crit_hyst, 0444, show_temp_crit_hyst, NULL, 1), - SENSOR_ATTR(temp2_fault, 0444, show_status, NULL, - TMP401_STATUS_REMOTE_OPEN), - SENSOR_ATTR(temp2_min_alarm, 0444, show_status, NULL, - TMP401_STATUS_REMOTE_LOW), - SENSOR_ATTR(temp2_max_alarm, 0444, show_status, NULL, - TMP401_STATUS_REMOTE_HIGH), - SENSOR_ATTR(temp2_crit_alarm, 0444, show_status, NULL, - TMP401_STATUS_REMOTE_CRIT), +static ssize_t show_update_interval(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tmp401_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 tmp401_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + int err, rate; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + /* + * For valid rates, interval can be calculated as + * interval = (1 << (7 - rate)) * 125; + * Rounded rate is therefore + * rate = 7 - __fls(interval * 4 / (125 * 3)); + * Use clamp_val() to avoid overflows, and to ensure valid input + * for __fls. + */ + val = clamp_val(val, 125, 16000); + rate = 7 - __fls(val * 4 / (125 * 3)); + mutex_lock(&data->update_lock); + i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, rate); + data->update_interval = (1 << (7 - rate)) * 125; + mutex_unlock(&data->update_lock); + + return count; +} + +static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0); +static SENSOR_DEVICE_ATTR_2(temp1_min, S_IWUSR | S_IRUGO, show_temp, + store_temp, 1, 0); +static SENSOR_DEVICE_ATTR_2(temp1_max, S_IWUSR | S_IRUGO, show_temp, + store_temp, 2, 0); +static SENSOR_DEVICE_ATTR_2(temp1_crit, S_IWUSR | S_IRUGO, show_temp, + store_temp, 3, 0); +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, + show_temp_crit_hyst, store_temp_crit_hyst, 0); +static SENSOR_DEVICE_ATTR_2(temp1_min_alarm, S_IRUGO, show_status, NULL, + 1, TMP432_STATUS_LOCAL); +static SENSOR_DEVICE_ATTR_2(temp1_max_alarm, S_IRUGO, show_status, NULL, + 2, TMP432_STATUS_LOCAL); +static SENSOR_DEVICE_ATTR_2(temp1_crit_alarm, S_IRUGO, show_status, NULL, + 3, TMP432_STATUS_LOCAL); +static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1); +static SENSOR_DEVICE_ATTR_2(temp2_min, S_IWUSR | S_IRUGO, show_temp, + store_temp, 1, 1); +static SENSOR_DEVICE_ATTR_2(temp2_max, S_IWUSR | S_IRUGO, show_temp, + store_temp, 2, 1); +static SENSOR_DEVICE_ATTR_2(temp2_crit, S_IWUSR | S_IRUGO, show_temp, + store_temp, 3, 1); +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, + NULL, 1); +static SENSOR_DEVICE_ATTR_2(temp2_fault, S_IRUGO, show_status, NULL, + 0, TMP432_STATUS_REMOTE1); +static SENSOR_DEVICE_ATTR_2(temp2_min_alarm, S_IRUGO, show_status, NULL, + 1, TMP432_STATUS_REMOTE1); +static SENSOR_DEVICE_ATTR_2(temp2_max_alarm, S_IRUGO, show_status, NULL, + 2, TMP432_STATUS_REMOTE1); +static SENSOR_DEVICE_ATTR_2(temp2_crit_alarm, S_IRUGO, show_status, NULL, + 3, TMP432_STATUS_REMOTE1); + +static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval, + set_update_interval); + +static struct attribute *tmp401_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + + &dev_attr_update_interval.attr, + + NULL +}; + +static const struct attribute_group tmp401_group = { + .attrs = tmp401_attributes, }; /* @@ -454,24 +553,74 @@ static struct sensor_device_attribute tmp401_attr[] = { * minimum and maximum register reset for both the local * and remote channels. */ -static struct sensor_device_attribute tmp411_attr[] = { - SENSOR_ATTR(temp1_highest, 0444, show_temp_highest, NULL, 0), - SENSOR_ATTR(temp1_lowest, 0444, show_temp_lowest, NULL, 0), - SENSOR_ATTR(temp2_highest, 0444, show_temp_highest, NULL, 1), - SENSOR_ATTR(temp2_lowest, 0444, show_temp_lowest, NULL, 1), - SENSOR_ATTR(temp_reset_history, 0200, NULL, reset_temp_history, 0), +static SENSOR_DEVICE_ATTR_2(temp1_lowest, S_IRUGO, show_temp, NULL, 4, 0); +static SENSOR_DEVICE_ATTR_2(temp1_highest, S_IRUGO, show_temp, NULL, 5, 0); +static SENSOR_DEVICE_ATTR_2(temp2_lowest, S_IRUGO, show_temp, NULL, 4, 1); +static SENSOR_DEVICE_ATTR_2(temp2_highest, S_IRUGO, show_temp, NULL, 5, 1); +static SENSOR_DEVICE_ATTR(temp_reset_history, S_IWUSR, NULL, reset_temp_history, + 0); + +static struct attribute *tmp411_attributes[] = { + &sensor_dev_attr_temp1_highest.dev_attr.attr, + &sensor_dev_attr_temp1_lowest.dev_attr.attr, + &sensor_dev_attr_temp2_highest.dev_attr.attr, + &sensor_dev_attr_temp2_lowest.dev_attr.attr, + &sensor_dev_attr_temp_reset_history.dev_attr.attr, + NULL +}; + +static const struct attribute_group tmp411_group = { + .attrs = tmp411_attributes, +}; + +static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2); +static SENSOR_DEVICE_ATTR_2(temp3_min, S_IWUSR | S_IRUGO, show_temp, + store_temp, 1, 2); +static SENSOR_DEVICE_ATTR_2(temp3_max, S_IWUSR | S_IRUGO, show_temp, + store_temp, 2, 2); +static SENSOR_DEVICE_ATTR_2(temp3_crit, S_IWUSR | S_IRUGO, show_temp, + store_temp, 3, 2); +static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, + NULL, 2); +static SENSOR_DEVICE_ATTR_2(temp3_fault, S_IRUGO, show_status, NULL, + 0, TMP432_STATUS_REMOTE2); +static SENSOR_DEVICE_ATTR_2(temp3_min_alarm, S_IRUGO, show_status, NULL, + 1, TMP432_STATUS_REMOTE2); +static SENSOR_DEVICE_ATTR_2(temp3_max_alarm, S_IRUGO, show_status, NULL, + 2, TMP432_STATUS_REMOTE2); +static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO, show_status, NULL, + 3, TMP432_STATUS_REMOTE2); + +static struct attribute *tmp432_attributes[] = { + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, + &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, + + NULL +}; + +static const struct attribute_group tmp432_group = { + .attrs = tmp432_attributes, }; /* * Begin non sysfs callback code (aka Real code) */ -static void tmp401_init_client(struct i2c_client *client) +static void tmp401_init_client(struct tmp401_data *data, + struct i2c_client *client) { int config, config_orig; /* Set the conversion rate to 2 Hz */ i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, 5); + data->update_interval = 500; /* Start conversions (disable shutdown if necessary) */ config = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ); @@ -506,11 +655,35 @@ static int tmp401_detect(struct i2c_client *client, switch (reg) { case TMP401_DEVICE_ID: + if (client->addr != 0x4c) + return -ENODEV; kind = tmp401; break; - case TMP411_DEVICE_ID: + case TMP411A_DEVICE_ID: + if (client->addr != 0x4c) + return -ENODEV; + kind = tmp411; + break; + case TMP411B_DEVICE_ID: + if (client->addr != 0x4d) + return -ENODEV; kind = tmp411; break; + case TMP411C_DEVICE_ID: + if (client->addr != 0x4e) + return -ENODEV; + kind = tmp411; + break; + case TMP431_DEVICE_ID: + if (client->addr == 0x4e) + return -ENODEV; + kind = tmp431; + break; + case TMP432_DEVICE_ID: + if (client->addr == 0x4e) + return -ENODEV; + kind = tmp432; + break; default: return -ENODEV; } @@ -524,7 +697,7 @@ static int tmp401_detect(struct i2c_client *client, if (reg > 15) return -ENODEV; - strlcpy(info->type, tmp401_id[kind - 1].name, I2C_NAME_SIZE); + strlcpy(info->type, tmp401_id[kind].name, I2C_NAME_SIZE); return 0; } @@ -532,155 +705,57 @@ static int tmp401_detect(struct i2c_client *client, static int tmp401_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int i, err = 0; + const char *names[] = { "TMP401", "TMP411", "TMP431", "TMP432" }; + struct device *dev = &client->dev; + struct device *hwmon_dev; struct tmp401_data *data; - const char *names[] = { "TMP401", "TMP411" }; + int groups = 0; - data = kzalloc(sizeof(struct tmp401_data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct tmp401_data), GFP_KERNEL); if (!data) return -ENOMEM; - i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->update_lock); data->kind = id->driver_data; /* Initialize the TMP401 chip */ - tmp401_init_client(client); + tmp401_init_client(data, client); /* Register sysfs hooks */ - for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++) { - err = device_create_file(&client->dev, - &tmp401_attr[i].dev_attr); - if (err) - goto exit_remove; - } + data->groups[groups++] = &tmp401_group; - /* Register aditional tmp411 sysfs hooks */ - if (data->kind == tmp411) { - for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++) { - err = device_create_file(&client->dev, - &tmp411_attr[i].dev_attr); - if (err) - goto exit_remove; - } - } - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - data->hwmon_dev = NULL; - goto exit_remove; - } + /* Register additional tmp411 sysfs hooks */ + if (data->kind == tmp411) + data->groups[groups++] = &tmp411_group; - dev_info(&client->dev, "Detected TI %s chip\n", - names[data->kind - 1]); - - return 0; - -exit_remove: - tmp401_remove(client); /* will also free data for us */ - return err; -} - -static int tmp401_remove(struct i2c_client *client) -{ - struct tmp401_data *data = i2c_get_clientdata(client); - int i; + /* Register additional tmp432 sysfs hooks */ + if (data->kind == tmp432) + data->groups[groups++] = &tmp432_group; - if (data->hwmon_dev) - hwmon_device_unregister(data->hwmon_dev); + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, data->groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); - for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++) - device_remove_file(&client->dev, &tmp401_attr[i].dev_attr); + dev_info(dev, "Detected TI %s chip\n", names[data->kind]); - if (data->kind == tmp411) { - for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++) - device_remove_file(&client->dev, - &tmp411_attr[i].dev_attr); - } - - kfree(data); return 0; } -static struct tmp401_data *tmp401_update_device_reg16( - struct i2c_client *client, struct tmp401_data *data) -{ - int i; - - for (i = 0; i < 2; i++) { - /* - * High byte must be read first immediately followed - * by the low byte - */ - data->temp[i] = i2c_smbus_read_byte_data(client, - TMP401_TEMP_MSB[i]) << 8; - data->temp[i] |= i2c_smbus_read_byte_data(client, - TMP401_TEMP_LSB[i]); - data->temp_low[i] = i2c_smbus_read_byte_data(client, - TMP401_TEMP_LOW_LIMIT_MSB_READ[i]) << 8; - data->temp_low[i] |= i2c_smbus_read_byte_data(client, - TMP401_TEMP_LOW_LIMIT_LSB[i]); - data->temp_high[i] = i2c_smbus_read_byte_data(client, - TMP401_TEMP_HIGH_LIMIT_MSB_READ[i]) << 8; - data->temp_high[i] |= i2c_smbus_read_byte_data(client, - TMP401_TEMP_HIGH_LIMIT_LSB[i]); - data->temp_crit[i] = i2c_smbus_read_byte_data(client, - TMP401_TEMP_CRIT_LIMIT[i]); - - if (data->kind == tmp411) { - data->temp_lowest[i] = i2c_smbus_read_byte_data(client, - TMP411_TEMP_LOWEST_MSB[i]) << 8; - data->temp_lowest[i] |= i2c_smbus_read_byte_data( - client, TMP411_TEMP_LOWEST_LSB[i]); - - data->temp_highest[i] = i2c_smbus_read_byte_data( - client, TMP411_TEMP_HIGHEST_MSB[i]) << 8; - data->temp_highest[i] |= i2c_smbus_read_byte_data( - client, TMP411_TEMP_HIGHEST_LSB[i]); - } - } - return data; -} - -static struct tmp401_data *tmp401_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct tmp401_data *data = i2c_get_clientdata(client); - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - data->status = i2c_smbus_read_byte_data(client, TMP401_STATUS); - data->config = i2c_smbus_read_byte_data(client, - TMP401_CONFIG_READ); - tmp401_update_device_reg16(client, data); - - data->temp_crit_hyst = i2c_smbus_read_byte_data(client, - TMP401_TEMP_CRIT_HYST); - - data->last_updated = jiffies; - data->valid = 1; - } - - mutex_unlock(&data->update_lock); - - return data; -} - -static int __init tmp401_init(void) -{ - return i2c_add_driver(&tmp401_driver); -} +static struct i2c_driver tmp401_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "tmp401", + }, + .probe = tmp401_probe, + .id_table = tmp401_id, + .detect = tmp401_detect, + .address_list = normal_i2c, +}; -static void __exit tmp401_exit(void) -{ - i2c_del_driver(&tmp401_driver); -} +module_i2c_driver(tmp401_driver); MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); MODULE_DESCRIPTION("Texas Instruments TMP401 temperature sensor driver"); MODULE_LICENSE("GPL"); - -module_init(tmp401_init); -module_exit(tmp401_exit); diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c index 4f7c051e2d7..7bab7a9bedc 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 }; @@ -61,33 +61,35 @@ static const u8 TMP421_TEMP_LSB[4] = { 0x10, 0x11, 0x12, 0x13 }; #define TMP423_DEVICE_ID 0x23 static const struct i2c_device_id tmp421_id[] = { - { "tmp421", tmp421 }, - { "tmp422", tmp422 }, - { "tmp423", tmp423 }, + { "tmp421", 2 }, + { "tmp422", 3 }, + { "tmp423", 4 }, { } }; MODULE_DEVICE_TABLE(i2c, tmp421_id); struct tmp421_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; char valid; unsigned long last_updated; - int kind; + int channels; u8 config; s16 temp[4]; }; static int temp_from_s16(s16 reg) { - int temp = reg; + /* Mask out status bits */ + int temp = reg & ~0xf; return (temp * 1000 + 128) / 256; } static int temp_from_u16(u16 reg) { - int temp = reg; + /* Mask out status bits */ + int temp = reg & ~0xf; /* Add offset for extended temperature range. */ temp -= 64 * 256; @@ -97,8 +99,8 @@ static int temp_from_u16(u16 reg) static struct tmp421_data *tmp421_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct tmp421_data *data = i2c_get_clientdata(client); + struct tmp421_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; int i; mutex_lock(&data->update_lock); @@ -107,7 +109,7 @@ static struct tmp421_data *tmp421_update_device(struct device *dev) data->config = i2c_smbus_read_byte_data(client, TMP421_CONFIG_REG_1); - for (i = 0; i <= data->kind; i++) { + for (i = 0; i < data->channels; i++) { data->temp[i] = i2c_smbus_read_byte_data(client, TMP421_TEMP_MSB[i]) << 8; data->temp[i] |= i2c_smbus_read_byte_data(client, @@ -155,7 +157,7 @@ static ssize_t show_fault(struct device *dev, return sprintf(buf, "0\n"); } -static mode_t tmp421_is_visible(struct kobject *kobj, struct attribute *a, +static umode_t tmp421_is_visible(struct kobject *kobj, struct attribute *a, int n) { struct device *dev = container_of(kobj, struct device, kobj); @@ -166,7 +168,7 @@ static mode_t tmp421_is_visible(struct kobject *kobj, struct attribute *a, devattr = container_of(a, struct device_attribute, attr); index = to_sensor_dev_attr(devattr)->index; - if (data->kind > index) + if (index < data->channels) return a->mode; return 0; @@ -196,6 +198,11 @@ static const struct attribute_group tmp421_group = { .is_visible = tmp421_is_visible, }; +static const struct attribute_group *tmp421_groups[] = { + &tmp421_group, + NULL +}; + static int tmp421_init_client(struct i2c_client *client) { int config, config_orig; @@ -206,9 +213,9 @@ static int tmp421_init_client(struct i2c_client *client) /* Start conversions (disable shutdown if necessary) */ config = i2c_smbus_read_byte_data(client, TMP421_CONFIG_REG_1); if (config < 0) { - dev_err(&client->dev, "Could not read configuration" - " register (%d)\n", config); - return -ENODEV; + dev_err(&client->dev, + "Could not read configuration register (%d)\n", config); + return config; } config_orig = config; @@ -252,9 +259,9 @@ static int tmp421_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, tmp421_id[kind - 1].name, I2C_NAME_SIZE); + strlcpy(info->type, tmp421_id[kind].name, I2C_NAME_SIZE); dev_info(&adapter->dev, "Detected TI %s chip at 0x%02x\n", - names[kind - 1], client->addr); + names[kind], client->addr); return 0; } @@ -262,54 +269,26 @@ static int tmp421_detect(struct i2c_client *client, static int tmp421_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; + struct device *hwmon_dev; struct tmp421_data *data; int err; - data = kzalloc(sizeof(struct tmp421_data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct tmp421_data), GFP_KERNEL); if (!data) return -ENOMEM; - i2c_set_clientdata(client, data); mutex_init(&data->update_lock); - data->kind = id->driver_data; + data->channels = id->driver_data; + data->client = client; err = tmp421_init_client(client); if (err) - goto exit_free; - - err = sysfs_create_group(&client->dev.kobj, &tmp421_group); - if (err) - goto exit_free; - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - data->hwmon_dev = NULL; - goto exit_remove; - } - return 0; - -exit_remove: - sysfs_remove_group(&client->dev.kobj, &tmp421_group); - -exit_free: - i2c_set_clientdata(client, NULL); - kfree(data); + return err; - return err; -} - -static int tmp421_remove(struct i2c_client *client) -{ - struct tmp421_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &tmp421_group); - - i2c_set_clientdata(client, NULL); - kfree(data); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, tmp421_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } static struct i2c_driver tmp421_driver = { @@ -318,26 +297,13 @@ static struct i2c_driver tmp421_driver = { .name = "tmp421", }, .probe = tmp421_probe, - .remove = tmp421_remove, .id_table = tmp421_id, .detect = tmp421_detect, .address_list = normal_i2c, }; -static int __init tmp421_init(void) -{ - return i2c_add_driver(&tmp421_driver); -} - -static void __exit tmp421_exit(void) -{ - i2c_del_driver(&tmp421_driver); -} +module_i2c_driver(tmp421_driver); MODULE_AUTHOR("Andre Prendel <andre.prendel@gmx.de>"); -MODULE_DESCRIPTION("Texas Instruments TMP421/422/423 temperature sensor" - " driver"); +MODULE_DESCRIPTION("Texas Instruments TMP421/422/423 temperature sensor driver"); MODULE_LICENSE("GPL"); - -module_init(tmp421_init); -module_exit(tmp421_exit); diff --git a/drivers/hwmon/twl4030-madc-hwmon.c b/drivers/hwmon/twl4030-madc-hwmon.c new file mode 100644 index 00000000000..6c6d440bb2d --- /dev/null +++ b/drivers/hwmon/twl4030-madc-hwmon.c @@ -0,0 +1,145 @@ +/* + * + * TWL4030 MADC Hwmon driver-This driver monitors the real time + * conversion of analog signals like battery temperature, + * battery type, battery level etc. User can ask for the conversion on a + * particular channel using the sysfs nodes. + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * J Keerthy <j-keerthy@ti.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/i2c/twl.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/i2c/twl4030-madc.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/stddef.h> +#include <linux/sysfs.h> +#include <linux/err.h> +#include <linux/types.h> + +/* + * sysfs hook function + */ +static ssize_t madc_read(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct twl4030_madc_request req = { + .channels = 1 << attr->index, + .method = TWL4030_MADC_SW2, + .type = TWL4030_MADC_WAIT, + }; + long val; + + val = twl4030_madc_conversion(&req); + if (val < 0) + return val; + + return sprintf(buf, "%d\n", req.rbuf[attr->index]); +} + +/* sysfs nodes to read individual channels from user side */ +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, madc_read, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, madc_read, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, madc_read, NULL, 2); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, madc_read, NULL, 3); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, madc_read, NULL, 4); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, madc_read, NULL, 5); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, madc_read, NULL, 6); +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, madc_read, NULL, 7); +static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, madc_read, NULL, 8); +static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, madc_read, NULL, 9); +static SENSOR_DEVICE_ATTR(curr10_input, S_IRUGO, madc_read, NULL, 10); +static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, madc_read, NULL, 11); +static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, madc_read, NULL, 12); +static SENSOR_DEVICE_ATTR(in15_input, S_IRUGO, madc_read, NULL, 15); + +static struct attribute *twl4030_madc_attributes[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in8_input.dev_attr.attr, + &sensor_dev_attr_in9_input.dev_attr.attr, + &sensor_dev_attr_curr10_input.dev_attr.attr, + &sensor_dev_attr_in11_input.dev_attr.attr, + &sensor_dev_attr_in12_input.dev_attr.attr, + &sensor_dev_attr_in15_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group twl4030_madc_group = { + .attrs = twl4030_madc_attributes, +}; + +static int twl4030_madc_hwmon_probe(struct platform_device *pdev) +{ + int ret; + struct device *hwmon; + + ret = sysfs_create_group(&pdev->dev.kobj, &twl4030_madc_group); + if (ret) + goto err_sysfs; + hwmon = hwmon_device_register(&pdev->dev); + if (IS_ERR(hwmon)) { + dev_err(&pdev->dev, "hwmon_device_register failed.\n"); + ret = PTR_ERR(hwmon); + goto err_reg; + } + + return 0; + +err_reg: + sysfs_remove_group(&pdev->dev.kobj, &twl4030_madc_group); +err_sysfs: + + return ret; +} + +static int twl4030_madc_hwmon_remove(struct platform_device *pdev) +{ + hwmon_device_unregister(&pdev->dev); + sysfs_remove_group(&pdev->dev.kobj, &twl4030_madc_group); + + return 0; +} + +static struct platform_driver twl4030_madc_hwmon_driver = { + .probe = twl4030_madc_hwmon_probe, + .remove = twl4030_madc_hwmon_remove, + .driver = { + .name = "twl4030_madc_hwmon", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(twl4030_madc_hwmon_driver); + +MODULE_DESCRIPTION("TWL4030 ADC Hwmon driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("J Keerthy"); +MODULE_ALIAS("platform:twl4030_madc_hwmon"); diff --git a/drivers/hwmon/ultra45_env.c b/drivers/hwmon/ultra45_env.c index 68e90abeba9..7d465863606 100644 --- a/drivers/hwmon/ultra45_env.c +++ b/drivers/hwmon/ultra45_env.c @@ -1,4 +1,5 @@ -/* ultra45_env.c: Driver for Ultra45 PIC16F747 environmental monitor. +/* + * ultra45_env.c: Driver for Ultra45 PIC16F747 environmental monitor. * * Copyright (C) 2008 David S. Miller <davem@davemloft.net> */ @@ -6,10 +7,12 @@ #include <linux/kernel.h> #include <linux/types.h> #include <linux/slab.h> +#include <linux/module.h> #include <linux/of_device.h> #include <linux/io.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> +#include <linux/err.h> #define DRV_MODULE_VERSION "0.1" @@ -81,7 +84,8 @@ static void env_write(struct env *p, u8 ireg, u8 val) spin_unlock(&p->lock); } -/* There seems to be a adr7462 providing these values, thus a lot +/* + * There seems to be a adr7462 providing these values, thus a lot * of these calculations are borrowed from the adt7470 driver. */ #define FAN_PERIOD_TO_RPM(x) ((90000 * 60) / (x)) @@ -89,7 +93,8 @@ static void env_write(struct env *p, u8 ireg, u8 val) #define FAN_PERIOD_INVALID (0xff << 8) #define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) -static ssize_t show_fan_speed(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_fan_speed(struct device *dev, struct device_attribute *attr, + char *buf) { int fan_nr = to_sensor_dev_attr(attr)->index; struct env *p = dev_get_drvdata(dev); @@ -110,10 +115,15 @@ static ssize_t set_fan_speed(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int fan_nr = to_sensor_dev_attr(attr)->index; - int rpm = simple_strtol(buf, NULL, 10); + unsigned long rpm; struct env *p = dev_get_drvdata(dev); int period; u8 val; + int err; + + err = kstrtoul(buf, 10, &rpm); + if (err) + return err; if (!rpm) return -EINVAL; @@ -125,7 +135,8 @@ static ssize_t set_fan_speed(struct device *dev, struct device_attribute *attr, return count; } -static ssize_t show_fan_fault(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_fan_fault(struct device *dev, struct device_attribute *attr, + char *buf) { int fan_nr = to_sensor_dev_attr(attr)->index; struct env *p = dev_get_drvdata(dev); @@ -147,7 +158,8 @@ fan(4); static SENSOR_DEVICE_ATTR(psu_fan_fault, S_IRUGO, show_fan_fault, NULL, 6); -static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, + char *buf) { int temp_nr = to_sensor_dev_attr(attr)->index; struct env *p = dev_get_drvdata(dev); @@ -167,7 +179,8 @@ static SENSOR_DEVICE_ATTR(lsi1064_local_temp, S_IRUGO, show_temp, NULL, 6); static SENSOR_DEVICE_ATTR(front_panel_temp, S_IRUGO, show_temp, NULL, 7); static SENSOR_DEVICE_ATTR(psu_temp, S_IRUGO, show_temp, NULL, 13); -static ssize_t show_stat_bit(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_stat_bit(struct device *dev, struct device_attribute *attr, + char *buf) { int index = to_sensor_dev_attr(attr)->index; struct env *p = dev_get_drvdata(dev); @@ -180,9 +193,11 @@ static ssize_t show_stat_bit(struct device *dev, struct device_attribute *attr, static SENSOR_DEVICE_ATTR(fan_failure, S_IRUGO, show_stat_bit, NULL, 0); static SENSOR_DEVICE_ATTR(env_bus_busy, S_IRUGO, show_stat_bit, NULL, 1); static SENSOR_DEVICE_ATTR(env_data_stale, S_IRUGO, show_stat_bit, NULL, 2); -static SENSOR_DEVICE_ATTR(tpm_self_test_passed, S_IRUGO, show_stat_bit, NULL, 3); +static SENSOR_DEVICE_ATTR(tpm_self_test_passed, S_IRUGO, show_stat_bit, NULL, + 3); -static ssize_t show_fwver(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_fwver(struct device *dev, struct device_attribute *attr, + char *buf) { struct env *p = dev_get_drvdata(dev); u8 val; @@ -193,7 +208,8 @@ static ssize_t show_fwver(struct device *dev, struct device_attribute *attr, cha static SENSOR_DEVICE_ATTR(firmware_version, S_IRUGO, show_fwver, NULL, 0); -static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_name(struct device *dev, struct device_attribute *attr, + char *buf) { return sprintf(buf, "ultra45\n"); } @@ -234,10 +250,9 @@ static const struct attribute_group env_group = { .attrs = env_attributes, }; -static int __devinit env_probe(struct of_device *op, - const struct of_device_id *match) +static int env_probe(struct platform_device *op) { - struct env *p = kzalloc(sizeof(*p), GFP_KERNEL); + struct env *p = devm_kzalloc(&op->dev, sizeof(*p), GFP_KERNEL); int err = -ENOMEM; if (!p) @@ -247,7 +262,7 @@ static int __devinit env_probe(struct of_device *op, p->regs = of_ioremap(&op->resource[0], 0, REG_SIZE, "pic16f747"); if (!p->regs) - goto out_free; + goto out; err = sysfs_create_group(&op->dev.kobj, &env_group); if (err) @@ -259,7 +274,7 @@ static int __devinit env_probe(struct of_device *op, goto out_sysfs_remove_group; } - dev_set_drvdata(&op->dev, p); + platform_set_drvdata(op, p); err = 0; out: @@ -271,20 +286,17 @@ out_sysfs_remove_group: out_iounmap: of_iounmap(&op->resource[0], p->regs, REG_SIZE); -out_free: - kfree(p); goto out; } -static int __devexit env_remove(struct of_device *op) +static int env_remove(struct platform_device *op) { - struct env *p = dev_get_drvdata(&op->dev); + struct env *p = platform_get_drvdata(op); if (p) { sysfs_remove_group(&op->dev.kobj, &env_group); hwmon_device_unregister(p->hwmon_dev); of_iounmap(&op->resource[0], p->regs, REG_SIZE); - kfree(p); } return 0; @@ -299,22 +311,14 @@ static const struct of_device_id env_match[] = { }; MODULE_DEVICE_TABLE(of, env_match); -static struct of_platform_driver env_driver = { - .name = "ultra45_env", - .match_table = env_match, +static struct platform_driver env_driver = { + .driver = { + .name = "ultra45_env", + .owner = THIS_MODULE, + .of_match_table = env_match, + }, .probe = env_probe, - .remove = __devexit_p(env_remove), + .remove = env_remove, }; -static int __init env_init(void) -{ - return of_register_driver(&env_driver, &of_bus_type); -} - -static void __exit env_exit(void) -{ - of_unregister_driver(&env_driver); -} - -module_init(env_init); -module_exit(env_exit); +module_platform_driver(env_driver); diff --git a/drivers/hwmon/vexpress.c b/drivers/hwmon/vexpress.c new file mode 100644 index 00000000000..c53619086f3 --- /dev/null +++ b/drivers/hwmon/vexpress.c @@ -0,0 +1,260 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * Copyright (C) 2012 ARM Limited + */ + +#define DRVNAME "vexpress-hwmon" +#define pr_fmt(fmt) DRVNAME ": " fmt + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/vexpress.h> + +struct vexpress_hwmon_data { + struct device *hwmon_dev; + struct regmap *reg; +}; + +static ssize_t vexpress_hwmon_label_show(struct device *dev, + struct device_attribute *dev_attr, char *buffer) +{ + const char *label = of_get_property(dev->of_node, "label", NULL); + + return snprintf(buffer, PAGE_SIZE, "%s\n", label); +} + +static ssize_t vexpress_hwmon_u32_show(struct device *dev, + struct device_attribute *dev_attr, char *buffer) +{ + struct vexpress_hwmon_data *data = dev_get_drvdata(dev); + int err; + u32 value; + + err = regmap_read(data->reg, 0, &value); + if (err) + return err; + + return snprintf(buffer, PAGE_SIZE, "%u\n", value / + to_sensor_dev_attr(dev_attr)->index); +} + +static ssize_t vexpress_hwmon_u64_show(struct device *dev, + struct device_attribute *dev_attr, char *buffer) +{ + struct vexpress_hwmon_data *data = dev_get_drvdata(dev); + int err; + u32 value_hi, value_lo; + + err = regmap_read(data->reg, 0, &value_lo); + if (err) + return err; + + err = regmap_read(data->reg, 1, &value_hi); + if (err) + return err; + + return snprintf(buffer, PAGE_SIZE, "%llu\n", + div_u64(((u64)value_hi << 32) | value_lo, + to_sensor_dev_attr(dev_attr)->index)); +} + +static umode_t vexpress_hwmon_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = kobj_to_dev(kobj); + struct device_attribute *dev_attr = container_of(attr, + struct device_attribute, attr); + + if (dev_attr->show == vexpress_hwmon_label_show && + !of_get_property(dev->of_node, "label", NULL)) + return 0; + + return attr->mode; +} + +struct vexpress_hwmon_type { + const char *name; + const struct attribute_group **attr_groups; +}; + +#if !defined(CONFIG_REGULATOR_VEXPRESS) +static DEVICE_ATTR(in1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, vexpress_hwmon_u32_show, + NULL, 1000); +static struct attribute *vexpress_hwmon_attrs_volt[] = { + &dev_attr_in1_label.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + NULL +}; +static struct attribute_group vexpress_hwmon_group_volt = { + .is_visible = vexpress_hwmon_attr_is_visible, + .attrs = vexpress_hwmon_attrs_volt, +}; +static struct vexpress_hwmon_type vexpress_hwmon_volt = { + .name = "vexpress_volt", + .attr_groups = (const struct attribute_group *[]) { + &vexpress_hwmon_group_volt, + NULL, + }, +}; +#endif + +static DEVICE_ATTR(curr1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, vexpress_hwmon_u32_show, + NULL, 1000); +static struct attribute *vexpress_hwmon_attrs_amp[] = { + &dev_attr_curr1_label.attr, + &sensor_dev_attr_curr1_input.dev_attr.attr, + NULL +}; +static struct attribute_group vexpress_hwmon_group_amp = { + .is_visible = vexpress_hwmon_attr_is_visible, + .attrs = vexpress_hwmon_attrs_amp, +}; +static struct vexpress_hwmon_type vexpress_hwmon_amp = { + .name = "vexpress_amp", + .attr_groups = (const struct attribute_group *[]) { + &vexpress_hwmon_group_amp, + NULL + }, +}; + +static DEVICE_ATTR(temp1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, vexpress_hwmon_u32_show, + NULL, 1000); +static struct attribute *vexpress_hwmon_attrs_temp[] = { + &dev_attr_temp1_label.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL +}; +static struct attribute_group vexpress_hwmon_group_temp = { + .is_visible = vexpress_hwmon_attr_is_visible, + .attrs = vexpress_hwmon_attrs_temp, +}; +static struct vexpress_hwmon_type vexpress_hwmon_temp = { + .name = "vexpress_temp", + .attr_groups = (const struct attribute_group *[]) { + &vexpress_hwmon_group_temp, + NULL + }, +}; + +static DEVICE_ATTR(power1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, vexpress_hwmon_u32_show, + NULL, 1); +static struct attribute *vexpress_hwmon_attrs_power[] = { + &dev_attr_power1_label.attr, + &sensor_dev_attr_power1_input.dev_attr.attr, + NULL +}; +static struct attribute_group vexpress_hwmon_group_power = { + .is_visible = vexpress_hwmon_attr_is_visible, + .attrs = vexpress_hwmon_attrs_power, +}; +static struct vexpress_hwmon_type vexpress_hwmon_power = { + .name = "vexpress_power", + .attr_groups = (const struct attribute_group *[]) { + &vexpress_hwmon_group_power, + NULL + }, +}; + +static DEVICE_ATTR(energy1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); +static SENSOR_DEVICE_ATTR(energy1_input, S_IRUGO, vexpress_hwmon_u64_show, + NULL, 1); +static struct attribute *vexpress_hwmon_attrs_energy[] = { + &dev_attr_energy1_label.attr, + &sensor_dev_attr_energy1_input.dev_attr.attr, + NULL +}; +static struct attribute_group vexpress_hwmon_group_energy = { + .is_visible = vexpress_hwmon_attr_is_visible, + .attrs = vexpress_hwmon_attrs_energy, +}; +static struct vexpress_hwmon_type vexpress_hwmon_energy = { + .name = "vexpress_energy", + .attr_groups = (const struct attribute_group *[]) { + &vexpress_hwmon_group_energy, + NULL + }, +}; + +static struct of_device_id vexpress_hwmon_of_match[] = { +#if !defined(CONFIG_REGULATOR_VEXPRESS) + { + .compatible = "arm,vexpress-volt", + .data = &vexpress_hwmon_volt, + }, +#endif + { + .compatible = "arm,vexpress-amp", + .data = &vexpress_hwmon_amp, + }, { + .compatible = "arm,vexpress-temp", + .data = &vexpress_hwmon_temp, + }, { + .compatible = "arm,vexpress-power", + .data = &vexpress_hwmon_power, + }, { + .compatible = "arm,vexpress-energy", + .data = &vexpress_hwmon_energy, + }, + {} +}; +MODULE_DEVICE_TABLE(of, vexpress_hwmon_of_match); + +static int vexpress_hwmon_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + struct vexpress_hwmon_data *data; + const struct vexpress_hwmon_type *type; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + platform_set_drvdata(pdev, data); + + match = of_match_device(vexpress_hwmon_of_match, &pdev->dev); + if (!match) + return -ENODEV; + type = match->data; + + data->reg = devm_regmap_init_vexpress_config(&pdev->dev); + if (IS_ERR(data->reg)) + return PTR_ERR(data->reg); + + data->hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, + type->name, data, type->attr_groups); + + return PTR_ERR_OR_ZERO(data->hwmon_dev); +} + +static struct platform_driver vexpress_hwmon_driver = { + .probe = vexpress_hwmon_probe, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + .of_match_table = vexpress_hwmon_of_match, + }, +}; + +module_platform_driver(vexpress_hwmon_driver); + +MODULE_AUTHOR("Pawel Moll <pawel.moll@arm.com>"); +MODULE_DESCRIPTION("Versatile Express hwmon sensors driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:vexpress-hwmon"); diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c index 7442cf75485..8df43c51de2 100644 --- a/drivers/hwmon/via-cputemp.c +++ b/drivers/hwmon/via-cputemp.c @@ -21,12 +21,13 @@ * 02110-1301 USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #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/hwmon-vid.h> #include <linux/sysfs.h> #include <linux/hwmon-sysfs.h> #include <linux/err.h> @@ -36,10 +37,11 @@ #include <linux/cpu.h> #include <asm/msr.h> #include <asm/processor.h> +#include <asm/cpu_device_id.h> #define DRVNAME "via_cputemp" -enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME } SHOW; +enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME }; /* * Functions declaration @@ -48,8 +50,10 @@ enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME } SHOW; struct via_cputemp_data { struct device *hwmon_dev; const char *name; + u8 vrm; u32 id; - u32 msr; + u32 msr_temp; + u32 msr_vid; }; /* @@ -77,13 +81,27 @@ static ssize_t show_temp(struct device *dev, u32 eax, edx; int err; - err = rdmsr_safe_on_cpu(data->id, data->msr, &eax, &edx); + err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx); if (err) return -EAGAIN; return sprintf(buf, "%lu\n", ((unsigned long)eax & 0xffffff) * 1000); } +static ssize_t show_cpu_vid(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct via_cputemp_data *data = dev_get_drvdata(dev); + u32 eax, edx; + int err; + + err = rdmsr_safe_on_cpu(data->id, data->msr_vid, &eax, &edx); + if (err) + return -EAGAIN; + + return sprintf(buf, "%d\n", vid_from_reg(~edx & 0x7f, data->vrm)); +} + static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, SHOW_TEMP); static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL); @@ -100,19 +118,20 @@ static const struct attribute_group via_cputemp_group = { .attrs = via_cputemp_attributes, }; -static int __devinit via_cputemp_probe(struct platform_device *pdev) +/* Optional attributes */ +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_cpu_vid, NULL); + +static int via_cputemp_probe(struct platform_device *pdev) { struct via_cputemp_data *data; struct cpuinfo_x86 *c = &cpu_data(pdev->id); int err; u32 eax, edx; - data = kzalloc(sizeof(struct via_cputemp_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - dev_err(&pdev->dev, "Out of memory\n"); - goto exit; - } + data = devm_kzalloc(&pdev->dev, sizeof(struct via_cputemp_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; data->id = pdev->id; data->name = "via_cputemp"; @@ -122,30 +141,39 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev) /* C7 A */ case 0xD: /* C7 D */ - data->msr = 0x1169; + data->msr_temp = 0x1169; + data->msr_vid = 0x198; break; case 0xF: /* Nano */ - data->msr = 0x1423; + data->msr_temp = 0x1423; break; default: - err = -ENODEV; - goto exit_free; + return -ENODEV; } /* test if we can access the TEMPERATURE MSR */ - err = rdmsr_safe_on_cpu(data->id, data->msr, &eax, &edx); + err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx); if (err) { dev_err(&pdev->dev, "Unable to access TEMPERATURE MSR, giving up\n"); - goto exit_free; + return err; } platform_set_drvdata(pdev, data); err = sysfs_create_group(&pdev->dev.kobj, &via_cputemp_group); if (err) - goto exit_free; + return err; + + if (data->msr_vid) + data->vrm = vid_which_vrm(); + + if (data->vrm) { + err = device_create_file(&pdev->dev, &dev_attr_cpu0_vid); + if (err) + goto exit_remove; + } data->hwmon_dev = hwmon_device_register(&pdev->dev); if (IS_ERR(data->hwmon_dev)) { @@ -158,22 +186,20 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev) return 0; exit_remove: + if (data->vrm) + device_remove_file(&pdev->dev, &dev_attr_cpu0_vid); sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group); -exit_free: - platform_set_drvdata(pdev, NULL); - kfree(data); -exit: return err; } -static int __devexit via_cputemp_remove(struct platform_device *pdev) +static int via_cputemp_remove(struct platform_device *pdev) { struct via_cputemp_data *data = platform_get_drvdata(pdev); hwmon_device_unregister(data->hwmon_dev); + if (data->vrm) + device_remove_file(&pdev->dev, &dev_attr_cpu0_vid); sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group); - platform_set_drvdata(pdev, NULL); - kfree(data); return 0; } @@ -183,7 +209,7 @@ static struct platform_driver via_cputemp_driver = { .name = DRVNAME, }, .probe = via_cputemp_probe, - .remove = __devexit_p(via_cputemp_remove), + .remove = via_cputemp_remove, }; struct pdev_entry { @@ -195,7 +221,7 @@ struct pdev_entry { static LIST_HEAD(pdev_list); static DEFINE_MUTEX(pdev_list_mutex); -static int __cpuinit via_cputemp_device_add(unsigned int cpu) +static int via_cputemp_device_add(unsigned int cpu) { int err; struct platform_device *pdev; @@ -204,7 +230,7 @@ static int __cpuinit via_cputemp_device_add(unsigned int cpu) pdev = platform_device_alloc(DRVNAME, cpu); if (!pdev) { err = -ENOMEM; - printk(KERN_ERR DRVNAME ": Device allocation failed\n"); + pr_err("Device allocation failed\n"); goto exit; } @@ -216,8 +242,7 @@ static int __cpuinit via_cputemp_device_add(unsigned int cpu) err = platform_device_add(pdev); if (err) { - printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", - err); + pr_err("Device addition failed (%d)\n", err); goto exit_device_free; } @@ -237,23 +262,25 @@ exit: return err; } -#ifdef CONFIG_HOTPLUG_CPU static void via_cputemp_device_remove(unsigned int cpu) { - struct pdev_entry *p, *n; + struct pdev_entry *p; + mutex_lock(&pdev_list_mutex); - list_for_each_entry_safe(p, n, &pdev_list, list) { + list_for_each_entry(p, &pdev_list, list) { if (p->cpu == cpu) { platform_device_unregister(p->pdev); list_del(&p->list); + mutex_unlock(&pdev_list_mutex); kfree(p); + return; } } mutex_unlock(&pdev_list_mutex); } -static int __cpuinit via_cputemp_cpu_callback(struct notifier_block *nfb, - unsigned long action, void *hcpu) +static int via_cputemp_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) { unsigned int cpu = (unsigned long) hcpu; @@ -272,23 +299,27 @@ 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 const struct x86_cpu_id __initconst cputemp_ids[] = { + { X86_VENDOR_CENTAUR, 6, 0xa, }, /* C7 A */ + { X86_VENDOR_CENTAUR, 6, 0xd, }, /* C7 D */ + { X86_VENDOR_CENTAUR, 6, 0xf, }, /* Nano */ + {} +}; +MODULE_DEVICE_TABLE(x86cpu, cputemp_ids); static int __init via_cputemp_init(void) { int i, err; - struct pdev_entry *p, *n; - if (cpu_data(0).x86_vendor != X86_VENDOR_CENTAUR) { - printk(KERN_DEBUG DRVNAME ": Not a VIA CPU\n"); - err = -ENODEV; - goto exit; - } + if (!x86_match_cpu(cputemp_ids)) + return -ENODEV; err = platform_driver_register(&via_cputemp_driver); if (err) goto exit; + cpu_notifier_register_begin(); for_each_online_cpu(i) { struct cpuinfo_x86 *c = &cpu_data(i); @@ -299,35 +330,29 @@ static int __init via_cputemp_init(void) continue; if (c->x86_model > 0x0f) { - printk(KERN_WARNING DRVNAME ": Unknown CPU " - "model 0x%x\n", c->x86_model); + pr_warn("Unknown CPU model 0x%x\n", c->x86_model); continue; } - err = via_cputemp_device_add(i); - if (err) - goto exit_devices_unreg; + via_cputemp_device_add(i); } + +#ifndef CONFIG_HOTPLUG_CPU if (list_empty(&pdev_list)) { + cpu_notifier_register_done(); err = -ENODEV; goto exit_driver_unreg; } - -#ifdef CONFIG_HOTPLUG_CPU - register_hotcpu_notifier(&via_cputemp_cpu_notifier); #endif + + __register_hotcpu_notifier(&via_cputemp_cpu_notifier); + cpu_notifier_register_done(); 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(&via_cputemp_driver); +#endif exit: return err; } @@ -335,9 +360,9 @@ 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 + + cpu_notifier_register_begin(); + __unregister_hotcpu_notifier(&via_cputemp_cpu_notifier); mutex_lock(&pdev_list_mutex); list_for_each_entry_safe(p, n, &pdev_list, list) { platform_device_unregister(p->pdev); @@ -345,6 +370,7 @@ static void __exit via_cputemp_exit(void) kfree(p); } mutex_unlock(&pdev_list_mutex); + cpu_notifier_register_done(); platform_driver_unregister(&via_cputemp_driver); } diff --git a/drivers/hwmon/via686a.c b/drivers/hwmon/via686a.c index f397ce7ad59..babd732b4e1 100644 --- a/drivers/hwmon/via686a.c +++ b/drivers/hwmon/via686a.c @@ -1,34 +1,37 @@ /* - via686a.c - Part of lm_sensors, Linux kernel modules - for hardware monitoring - - Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>, - Kyösti Mälkki <kmalkki@cc.hut.fi>, - Mark Studebaker <mdsxyz123@yahoo.com>, - and Bob Dougherty <bobd@stanford.edu> - (Some conversion-factor data were contributed by Jonathan Teh Soon Yew - <j.teh@iname.com> and Alex van Kaam <darkside@chello.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. -*/ + * via686a.c - Part of lm_sensors, Linux kernel modules + * for hardware monitoring + * + * Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>, + * Kyösti Mälkki <kmalkki@cc.hut.fi>, + * Mark Studebaker <mdsxyz123@yahoo.com>, + * and Bob Dougherty <bobd@stanford.edu> + * + * (Some conversion-factor data were contributed by Jonathan Teh Soon Yew + * <j.teh@iname.com> and Alex van Kaam <darkside@chello.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. + */ /* - Supports the Via VT82C686A, VT82C686B south bridges. - Reports all as a 686A. - Warning - only supports a single device. -*/ + * Supports the Via VT82C686A, VT82C686B south bridges. + * Reports all as a 686A. + * Warning - only supports a single device. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/slab.h> @@ -45,8 +48,10 @@ #include <linux/io.h> -/* If force_addr is set to anything different from 0, we forcibly enable - the device at the given address. */ +/* + * If force_addr is set to anything different from 0, we forcibly enable + * the device at the given address. + */ static unsigned short force_addr; module_param(force_addr, ushort, 0); MODULE_PARM_DESC(force_addr, @@ -55,9 +60,9 @@ MODULE_PARM_DESC(force_addr, static struct platform_device *pdev; /* - The Via 686a southbridge has a LM78-like chip integrated on the same IC. - This driver is a customized copy of lm78.c -*/ + * The Via 686a southbridge has a LM78-like chip integrated on the same IC. + * This driver is a customized copy of lm78.c + */ /* Many VIA686A constants specified below */ @@ -89,117 +94,127 @@ static const u8 VIA686A_REG_TEMP_HYST[] = { 0x3a, 0x3e, 0x1e }; #define VIA686A_REG_ALARM2 0x42 #define VIA686A_REG_FANDIV 0x47 #define VIA686A_REG_CONFIG 0x40 -/* The following register sets temp interrupt mode (bits 1-0 for temp1, - 3-2 for temp2, 5-4 for temp3). Modes are: - 00 interrupt stays as long as value is out-of-range - 01 interrupt is cleared once register is read (default) - 10 comparator mode- like 00, but ignores hysteresis - 11 same as 00 */ +/* + * The following register sets temp interrupt mode (bits 1-0 for temp1, + * 3-2 for temp2, 5-4 for temp3). Modes are: + * 00 interrupt stays as long as value is out-of-range + * 01 interrupt is cleared once register is read (default) + * 10 comparator mode- like 00, but ignores hysteresis + * 11 same as 00 + */ #define VIA686A_REG_TEMP_MODE 0x4b /* We'll just assume that you want to set all 3 simultaneously: */ #define VIA686A_TEMP_MODE_MASK 0x3F #define VIA686A_TEMP_MODE_CONTINUOUS 0x00 -/* Conversions. Limit checking is only done on the TO_REG - variants. - -********* VOLTAGE CONVERSIONS (Bob Dougherty) ******** - From HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew): - voltagefactor[0]=1.25/2628; (2628/1.25=2102.4) // Vccp - voltagefactor[1]=1.25/2628; (2628/1.25=2102.4) // +2.5V - voltagefactor[2]=1.67/2628; (2628/1.67=1573.7) // +3.3V - voltagefactor[3]=2.6/2628; (2628/2.60=1010.8) // +5V - voltagefactor[4]=6.3/2628; (2628/6.30=417.14) // +12V - in[i]=(data[i+2]*25.0+133)*voltagefactor[i]; - That is: - volts = (25*regVal+133)*factor - regVal = (volts/factor-133)/25 - (These conversions were contributed by Jonathan Teh Soon Yew - <j.teh@iname.com>) */ -static inline u8 IN_TO_REG(long val, int inNum) +/* + * Conversions. Limit checking is only done on the TO_REG + * variants. + * + ******** VOLTAGE CONVERSIONS (Bob Dougherty) ******** + * From HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew): + * voltagefactor[0]=1.25/2628; (2628/1.25=2102.4) // Vccp + * voltagefactor[1]=1.25/2628; (2628/1.25=2102.4) // +2.5V + * voltagefactor[2]=1.67/2628; (2628/1.67=1573.7) // +3.3V + * voltagefactor[3]=2.6/2628; (2628/2.60=1010.8) // +5V + * voltagefactor[4]=6.3/2628; (2628/6.30=417.14) // +12V + * in[i]=(data[i+2]*25.0+133)*voltagefactor[i]; + * That is: + * volts = (25*regVal+133)*factor + * regVal = (volts/factor-133)/25 + * (These conversions were contributed by Jonathan Teh Soon Yew + * <j.teh@iname.com>) + */ +static inline u8 IN_TO_REG(long val, int in_num) { - /* To avoid floating point, we multiply constants by 10 (100 for +12V). - Rounding is done (120500 is actually 133000 - 12500). - Remember that val is expressed in 0.001V/bit, which is why we divide - by an additional 10000 (100000 for +12V): 1000 for val and 10 (100) - for the constants. */ - if (inNum <= 1) - return (u8) - SENSORS_LIMIT((val * 21024 - 1205000) / 250000, 0, 255); - else if (inNum == 2) - return (u8) - SENSORS_LIMIT((val * 15737 - 1205000) / 250000, 0, 255); - else if (inNum == 3) - return (u8) - SENSORS_LIMIT((val * 10108 - 1205000) / 250000, 0, 255); + /* + * To avoid floating point, we multiply constants by 10 (100 for +12V). + * Rounding is done (120500 is actually 133000 - 12500). + * Remember that val is expressed in 0.001V/bit, which is why we divide + * by an additional 10000 (100000 for +12V): 1000 for val and 10 (100) + * for the constants. + */ + if (in_num <= 1) + return (u8) clamp_val((val * 21024 - 1205000) / 250000, 0, 255); + else if (in_num == 2) + return (u8) clamp_val((val * 15737 - 1205000) / 250000, 0, 255); + else if (in_num == 3) + return (u8) clamp_val((val * 10108 - 1205000) / 250000, 0, 255); else - return (u8) - SENSORS_LIMIT((val * 41714 - 12050000) / 2500000, 0, 255); + return (u8) clamp_val((val * 41714 - 12050000) / 2500000, 0, + 255); } -static inline long IN_FROM_REG(u8 val, int inNum) +static inline long IN_FROM_REG(u8 val, int in_num) { - /* To avoid floating point, we multiply constants by 10 (100 for +12V). - We also multiply them by 1000 because we want 0.001V/bit for the - output value. Rounding is done. */ - if (inNum <= 1) + /* + * To avoid floating point, we multiply constants by 10 (100 for +12V). + * We also multiply them by 1000 because we want 0.001V/bit for the + * output value. Rounding is done. + */ + if (in_num <= 1) return (long) ((250000 * val + 1330000 + 21024 / 2) / 21024); - else if (inNum == 2) + else if (in_num == 2) return (long) ((250000 * val + 1330000 + 15737 / 2) / 15737); - else if (inNum == 3) + else if (in_num == 3) return (long) ((250000 * val + 1330000 + 10108 / 2) / 10108); else return (long) ((2500000 * val + 13300000 + 41714 / 2) / 41714); } /********* FAN RPM CONVERSIONS ********/ -/* Higher register values = slower fans (the fan's strobe gates a counter). - But this chip saturates back at 0, not at 255 like all the other chips. - So, 0 means 0 RPM */ +/* + * Higher register values = slower fans (the fan's strobe gates a counter). + * But this chip saturates back at 0, not at 255 like all the other chips. + * So, 0 means 0 RPM + */ static inline u8 FAN_TO_REG(long rpm, int div) { if (rpm == 0) return 0; - rpm = SENSORS_LIMIT(rpm, 1, 1000000); - return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 255); + rpm = clamp_val(rpm, 1, 1000000); + return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 255); } -#define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1350000/((val)*(div))) +#define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : (val) == 255 ? 0 : 1350000 / \ + ((val) * (div))) /******** TEMP CONVERSIONS (Bob Dougherty) *********/ -/* linear fits from HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew) - if(temp<169) - return double(temp)*0.427-32.08; - else if(temp>=169 && temp<=202) - return double(temp)*0.582-58.16; - else - return double(temp)*0.924-127.33; - - A fifth-order polynomial fits the unofficial data (provided by Alex van - Kaam <darkside@chello.nl>) a bit better. It also give more reasonable - numbers on my machine (ie. they agree with what my BIOS tells me). - Here's the fifth-order fit to the 8-bit data: - temp = 1.625093e-10*val^5 - 1.001632e-07*val^4 + 2.457653e-05*val^3 - - 2.967619e-03*val^2 + 2.175144e-01*val - 7.090067e+0. - - (2000-10-25- RFD: thanks to Uwe Andersen <uandersen@mayah.com> for - finding my typos in this formula!) - - Alas, none of the elegant function-fit solutions will work because we - aren't allowed to use floating point in the kernel and doing it with - integers doesn't provide enough precision. So we'll do boring old - look-up table stuff. The unofficial data (see below) have effectively - 7-bit resolution (they are rounded to the nearest degree). I'm assuming - that the transfer function of the device is monotonic and smooth, so a - smooth function fit to the data will allow us to get better precision. - I used the 5th-order poly fit described above and solved for - VIA register values 0-255. I *10 before rounding, so we get tenth-degree - precision. (I could have done all 1024 values for our 10-bit readings, - but the function is very linear in the useful range (0-80 deg C), so - we'll just use linear interpolation for 10-bit readings.) So, tempLUT - is the temp at via register values 0-255: */ -static const s16 tempLUT[] = -{ -709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519, +/* + * linear fits from HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew) + * if(temp<169) + * return double(temp)*0.427-32.08; + * else if(temp>=169 && temp<=202) + * return double(temp)*0.582-58.16; + * else + * return double(temp)*0.924-127.33; + * + * A fifth-order polynomial fits the unofficial data (provided by Alex van + * Kaam <darkside@chello.nl>) a bit better. It also give more reasonable + * numbers on my machine (ie. they agree with what my BIOS tells me). + * Here's the fifth-order fit to the 8-bit data: + * temp = 1.625093e-10*val^5 - 1.001632e-07*val^4 + 2.457653e-05*val^3 - + * 2.967619e-03*val^2 + 2.175144e-01*val - 7.090067e+0. + * + * (2000-10-25- RFD: thanks to Uwe Andersen <uandersen@mayah.com> for + * finding my typos in this formula!) + * + * Alas, none of the elegant function-fit solutions will work because we + * aren't allowed to use floating point in the kernel and doing it with + * integers doesn't provide enough precision. So we'll do boring old + * look-up table stuff. The unofficial data (see below) have effectively + * 7-bit resolution (they are rounded to the nearest degree). I'm assuming + * that the transfer function of the device is monotonic and smooth, so a + * smooth function fit to the data will allow us to get better precision. + * I used the 5th-order poly fit described above and solved for + * VIA register values 0-255. I *10 before rounding, so we get tenth-degree + * precision. (I could have done all 1024 values for our 10-bit readings, + * but the function is very linear in the useful range (0-80 deg C), so + * we'll just use linear interpolation for 10-bit readings.) So, temp_lut + * is the temp at via register values 0-255: + */ +static const s16 temp_lut[] = { + -709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519, -503, -487, -471, -456, -442, -428, -414, -400, -387, -375, -362, -350, -339, -327, -316, -305, -295, -285, -275, -265, -255, -246, -237, -229, -220, -212, -204, -196, -188, -180, @@ -223,29 +238,31 @@ static const s16 tempLUT[] = 1276, 1301, 1326, 1352, 1378, 1406, 1434, 1462 }; -/* the original LUT values from Alex van Kaam <darkside@chello.nl> - (for via register values 12-240): -{-50,-49,-47,-45,-43,-41,-39,-38,-37,-35,-34,-33,-32,-31, --30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15, --15,-14,-14,-13,-12,-12,-11,-11,-10,-9,-9,-8,-8,-7,-7,-6,-6,-5,-5,-4,-4,-3, --3,-2,-2,-1,-1,0,0,1,1,1,3,3,3,4,4,4,5,5,5,6,6,7,7,8,8,9,9,9,10,10,11,11,12, -12,12,13,13,13,14,14,15,15,16,16,16,17,17,18,18,19,19,20,20,21,21,21,22,22, -22,23,23,24,24,25,25,26,26,26,27,27,27,28,28,29,29,30,30,30,31,31,32,32,33, -33,34,34,35,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45, -45,46,46,47,48,48,49,49,50,51,51,52,52,53,53,54,55,55,56,57,57,58,59,59,60, -61,62,62,63,64,65,66,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,83,84, -85,86,88,89,91,92,94,96,97,99,101,103,105,107,109,110}; - - - Here's the reverse LUT. I got it by doing a 6-th order poly fit (needed - an extra term for a good fit to these inverse data!) and then - solving for each temp value from -50 to 110 (the useable range for - this chip). Here's the fit: - viaRegVal = -1.160370e-10*val^6 +3.193693e-08*val^5 - 1.464447e-06*val^4 - - 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01) - Note that n=161: */ -static const u8 viaLUT[] = -{ 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23, +/* + * the original LUT values from Alex van Kaam <darkside@chello.nl> + * (for via register values 12-240): + * {-50,-49,-47,-45,-43,-41,-39,-38,-37,-35,-34,-33,-32,-31, + * -30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15, + * -15,-14,-14,-13,-12,-12,-11,-11,-10,-9,-9,-8,-8,-7,-7,-6,-6,-5,-5,-4,-4,-3, + * -3,-2,-2,-1,-1,0,0,1,1,1,3,3,3,4,4,4,5,5,5,6,6,7,7,8,8,9,9,9,10,10,11,11,12, + * 12,12,13,13,13,14,14,15,15,16,16,16,17,17,18,18,19,19,20,20,21,21,21,22,22, + * 22,23,23,24,24,25,25,26,26,26,27,27,27,28,28,29,29,30,30,30,31,31,32,32,33, + * 33,34,34,35,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45, + * 45,46,46,47,48,48,49,49,50,51,51,52,52,53,53,54,55,55,56,57,57,58,59,59,60, + * 61,62,62,63,64,65,66,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,83,84, + * 85,86,88,89,91,92,94,96,97,99,101,103,105,107,109,110}; + * + * + * Here's the reverse LUT. I got it by doing a 6-th order poly fit (needed + * an extra term for a good fit to these inverse data!) and then + * solving for each temp value from -50 to 110 (the useable range for + * this chip). Here's the fit: + * viaRegVal = -1.160370e-10*val^6 +3.193693e-08*val^5 - 1.464447e-06*val^4 + * - 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01) + * Note that n=161: + */ +static const u8 via_lut[] = { + 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 39, 40, 41, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 60, 62, 64, 66, 69, 71, 73, 75, 77, 79, 82, 84, 86, 88, 91, 93, 95, 98, 100, @@ -260,38 +277,42 @@ static const u8 viaLUT[] = 239, 240 }; -/* Converting temps to (8-bit) hyst and over registers - No interpolation here. - The +50 is because the temps start at -50 */ +/* + * Converting temps to (8-bit) hyst and over registers + * No interpolation here. + * The +50 is because the temps start at -50 + */ static inline u8 TEMP_TO_REG(long val) { - return viaLUT[val <= -50000 ? 0 : val >= 110000 ? 160 : + return via_lut[val <= -50000 ? 0 : val >= 110000 ? 160 : (val < 0 ? val - 500 : val + 500) / 1000 + 50]; } /* for 8-bit temperature hyst and over registers */ -#define TEMP_FROM_REG(val) ((long)tempLUT[val] * 100) +#define TEMP_FROM_REG(val) ((long)temp_lut[val] * 100) /* for 10-bit temperature readings */ static inline long TEMP_FROM_REG10(u16 val) { - u16 eightBits = val >> 2; - u16 twoBits = val & 3; + u16 eight_bits = val >> 2; + u16 two_bits = val & 3; /* no interpolation for these */ - if (twoBits == 0 || eightBits == 255) - return TEMP_FROM_REG(eightBits); + if (two_bits == 0 || eight_bits == 255) + return TEMP_FROM_REG(eight_bits); /* do some linear interpolation */ - return (tempLUT[eightBits] * (4 - twoBits) + - tempLUT[eightBits + 1] * twoBits) * 25; + return (temp_lut[eight_bits] * (4 - two_bits) + + temp_lut[eight_bits + 1] * two_bits) * 25; } #define DIV_FROM_REG(val) (1 << (val)) -#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) +#define DIV_TO_REG(val) ((val) == 8 ? 3 : (val) == 4 ? 2 : (val) == 1 ? 0 : 1) -/* For each registered chip, we need to keep some data in memory. - The structure is dynamically allocated. */ +/* + * For each registered chip, we need to keep some data in memory. + * The structure is dynamically allocated. + */ struct via686a_data { unsigned short addr; const char *name; @@ -315,7 +336,7 @@ struct via686a_data { static struct pci_dev *s_bridge; /* pointer to the (only) via686a */ static int via686a_probe(struct platform_device *pdev); -static int __devexit via686a_remove(struct platform_device *pdev); +static int via686a_remove(struct platform_device *pdev); static inline int via686a_read_value(struct via686a_data *data, u8 reg) { @@ -363,7 +384,12 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *da, struct via686a_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int nr = attr->index; - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_min[nr] = IN_TO_REG(val, nr); @@ -377,7 +403,12 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *da, struct via686a_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int nr = attr->index; - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_max[nr] = IN_TO_REG(val, nr); @@ -427,7 +458,12 @@ static ssize_t set_temp_over(struct device *dev, struct device_attribute *da, struct via686a_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int nr = attr->index; - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_over[nr] = TEMP_TO_REG(val); @@ -441,7 +477,12 @@ static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *da, struct via686a_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int nr = attr->index; - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp_hyst[nr] = TEMP_TO_REG(val); @@ -469,7 +510,7 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *da, struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int nr = attr->index; return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], - DIV_FROM_REG(data->fan_div[nr])) ); + DIV_FROM_REG(data->fan_div[nr]))); } static ssize_t show_fan_min(struct device *dev, struct device_attribute *da, char *buf) { @@ -477,21 +518,27 @@ static ssize_t show_fan_min(struct device *dev, struct device_attribute *da, struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int nr = attr->index; return sprintf(buf, "%d\n", - FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])) ); + FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr]))); } static ssize_t show_fan_div(struct device *dev, struct device_attribute *da, char *buf) { struct via686a_data *data = via686a_update_device(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int nr = attr->index; - return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) ); + return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr])); } static ssize_t set_fan_min(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct via686a_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int nr = attr->index; - int val = simple_strtol(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); @@ -504,8 +551,13 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *da, struct via686a_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int nr = attr->index; - int val = simple_strtol(buf, NULL, 10); int old; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); old = via686a_read_value(data, VIA686A_REG_FANDIV); @@ -528,10 +580,13 @@ show_fan_offset(1); show_fan_offset(2); /* Alarms */ -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) { +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, + char *buf) +{ struct via686a_data *data = via686a_update_device(dev); return sprintf(buf, "%u\n", data->alarms); } + static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, @@ -619,12 +674,12 @@ static struct platform_driver via686a_driver = { .name = "via686a", }, .probe = via686a_probe, - .remove = __devexit_p(via686a_remove), + .remove = via686a_remove, }; /* This is called when the module is loaded */ -static int __devinit via686a_probe(struct platform_device *pdev) +static int via686a_probe(struct platform_device *pdev) { struct via686a_data *data; struct resource *res; @@ -632,17 +687,17 @@ static int __devinit via686a_probe(struct platform_device *pdev) /* Reserve the ISA region */ res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start, VIA686A_EXTENT, - via686a_driver.driver.name)) { + if (!devm_request_region(&pdev->dev, res->start, VIA686A_EXTENT, + via686a_driver.driver.name)) { dev_err(&pdev->dev, "Region 0x%lx-0x%lx already in use!\n", (unsigned long)res->start, (unsigned long)res->end); return -ENODEV; } - if (!(data = kzalloc(sizeof(struct via686a_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit_release; - } + data = devm_kzalloc(&pdev->dev, sizeof(struct via686a_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; platform_set_drvdata(pdev, data); data->addr = res->start; @@ -653,8 +708,9 @@ static int __devinit via686a_probe(struct platform_device *pdev) via686a_init_device(data); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&pdev->dev.kobj, &via686a_group))) - goto exit_free; + err = sysfs_create_group(&pdev->dev.kobj, &via686a_group); + if (err) + return err; data->hwmon_dev = hwmon_device_register(&pdev->dev); if (IS_ERR(data->hwmon_dev)) { @@ -666,28 +722,27 @@ static int __devinit via686a_probe(struct platform_device *pdev) exit_remove_files: sysfs_remove_group(&pdev->dev.kobj, &via686a_group); -exit_free: - kfree(data); -exit_release: - release_region(res->start, VIA686A_EXTENT); return err; } -static int __devexit via686a_remove(struct platform_device *pdev) +static int via686a_remove(struct platform_device *pdev) { struct via686a_data *data = platform_get_drvdata(pdev); hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&pdev->dev.kobj, &via686a_group); - release_region(data->addr, VIA686A_EXTENT); - platform_set_drvdata(pdev, NULL); - kfree(data); - return 0; } -static void __devinit via686a_init_device(struct via686a_data *data) +static void via686a_update_fan_div(struct via686a_data *data) +{ + int reg = via686a_read_value(data, VIA686A_REG_FANDIV); + data->fan_div[0] = (reg >> 4) & 0x03; + data->fan_div[1] = reg >> 6; +} + +static void via686a_init_device(struct via686a_data *data) { u8 reg; @@ -700,6 +755,9 @@ static void __devinit via686a_init_device(struct via686a_data *data) via686a_write_value(data, VIA686A_REG_TEMP_MODE, (reg & ~VIA686A_TEMP_MODE_MASK) | VIA686A_TEMP_MODE_CONTINUOUS); + + /* Pre-read fan clock divisor values */ + via686a_update_fan_div(data); } static struct via686a_data *via686a_update_device(struct device *dev) @@ -736,10 +794,11 @@ static struct via686a_data *via686a_update_device(struct device *dev) via686a_read_value(data, VIA686A_REG_TEMP_HYST[i]); } - /* add in lower 2 bits - temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1 - temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23 - temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23 + /* + * add in lower 2 bits + * temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1 + * temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23 + * temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23 */ data->temp[0] |= (via686a_read_value(data, VIA686A_REG_TEMP_LOW1) @@ -751,9 +810,7 @@ static struct via686a_data *via686a_update_device(struct device *dev) (via686a_read_value(data, VIA686A_REG_TEMP_LOW23) & 0xc0) >> 6; - i = via686a_read_value(data, VIA686A_REG_FANDIV); - data->fan_div[0] = (i >> 4) & 0x03; - data->fan_div[1] = i >> 6; + via686a_update_fan_div(data); data->alarms = via686a_read_value(data, VIA686A_REG_ALARM1) | @@ -769,12 +826,11 @@ static struct via686a_data *via686a_update_device(struct device *dev) static const struct pci_device_id via686a_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4) }, - { 0, } + { } }; - MODULE_DEVICE_TABLE(pci, via686a_pci_ids); -static int __devinit via686a_device_add(unsigned short address) +static int via686a_device_add(unsigned short address) { struct resource res = { .start = address, @@ -791,21 +847,19 @@ static int __devinit via686a_device_add(unsigned short address) pdev = platform_device_alloc("via686a", address); if (!pdev) { err = -ENOMEM; - printk(KERN_ERR "via686a: Device allocation failed\n"); + pr_err("Device allocation failed\n"); goto exit; } err = platform_device_add_resources(pdev, &res, 1); if (err) { - printk(KERN_ERR "via686a: Device resource addition failed " - "(%d)\n", err); + pr_err("Device resource addition failed (%d)\n", err); goto exit_device_put; } err = platform_device_add(pdev); if (err) { - printk(KERN_ERR "via686a: Device addition failed (%d)\n", - err); + pr_err("Device addition failed (%d)\n", err); goto exit_device_put; } @@ -817,7 +871,7 @@ exit: return err; } -static int __devinit via686a_pci_probe(struct pci_dev *dev, +static int via686a_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { u16 address, val; @@ -835,8 +889,8 @@ static int __devinit via686a_pci_probe(struct pci_dev *dev, address = val & ~(VIA686A_EXTENT - 1); if (address == 0) { - dev_err(&dev->dev, "base address not set - upgrade BIOS " - "or use force_addr=0xaddr\n"); + dev_err(&dev->dev, + "base address not set - upgrade BIOS or use force_addr=0xaddr\n"); return -ENODEV; } @@ -845,8 +899,9 @@ static int __devinit via686a_pci_probe(struct pci_dev *dev, return -ENODEV; if (!(val & 0x0001)) { if (!force_addr) { - dev_warn(&dev->dev, "Sensors disabled, enable " - "with force_addr=0x%x\n", address); + dev_warn(&dev->dev, + "Sensors disabled, enable with force_addr=0x%x\n", + address); return -ENODEV; } @@ -864,7 +919,8 @@ static int __devinit via686a_pci_probe(struct pci_dev *dev, if (via686a_device_add(address)) goto exit_unregister; - /* Always return failure here. This is to allow other drivers to bind + /* + * Always return failure here. This is to allow other drivers to bind * to this pci device. We don't really want to have control over the * pci device, we only wanted to read as few register values from it. */ diff --git a/drivers/hwmon/vt1211.c b/drivers/hwmon/vt1211.c index ae33bbb577c..344b22ec255 100644 --- a/drivers/hwmon/vt1211.c +++ b/drivers/hwmon/vt1211.c @@ -21,6 +21,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> @@ -149,25 +151,29 @@ struct vt1211_data { #define ISTEMP(ix, uch_config) ((ix) < 2 ? 1 : \ ((uch_config) >> (ix)) & 1) -/* in5 (ix = 5) is special. It's the internal 3.3V so it's scaled in the - driver according to the VT1211 BIOS porting guide */ +/* + * in5 (ix = 5) is special. It's the internal 3.3V so it's scaled in the + * driver according to the VT1211 BIOS porting guide + */ #define IN_FROM_REG(ix, reg) ((reg) < 3 ? 0 : (ix) == 5 ? \ (((reg) - 3) * 15882 + 479) / 958 : \ (((reg) - 3) * 10000 + 479) / 958) -#define IN_TO_REG(ix, val) (SENSORS_LIMIT((ix) == 5 ? \ +#define IN_TO_REG(ix, val) (clamp_val((ix) == 5 ? \ ((val) * 958 + 7941) / 15882 + 3 : \ ((val) * 958 + 5000) / 10000 + 3, 0, 255)) -/* temp1 (ix = 0) is an intel thermal diode which is scaled in user space. - temp2 (ix = 1) is the internal temp diode so it's scaled in the driver - according to some measurements that I took on an EPIA M10000. - temp3-7 are thermistor based so the driver returns the voltage measured at - the pin (range 0V - 2.2V). */ +/* + * temp1 (ix = 0) is an intel thermal diode which is scaled in user space. + * temp2 (ix = 1) is the internal temp diode so it's scaled in the driver + * according to some measurements that I took on an EPIA M10000. + * temp3-7 are thermistor based so the driver returns the voltage measured at + * the pin (range 0V - 2.2V). + */ #define TEMP_FROM_REG(ix, reg) ((ix) == 0 ? (reg) * 1000 : \ (ix) == 1 ? (reg) < 51 ? 0 : \ ((reg) - 51) * 1000 : \ ((253 - (reg)) * 2200 + 105) / 210) -#define TEMP_TO_REG(ix, val) SENSORS_LIMIT( \ +#define TEMP_TO_REG(ix, val) clamp_val( \ ((ix) == 0 ? ((val) + 500) / 1000 : \ (ix) == 1 ? ((val) + 500) / 1000 + 51 : \ 253 - ((val) * 210 + 1100) / 2200), 0, 255) @@ -177,15 +183,17 @@ struct vt1211_data { #define RPM_FROM_REG(reg, div) (((reg) == 0) || ((reg) == 255) ? 0 : \ 1310720 / (reg) / DIV_FROM_REG(div)) #define RPM_TO_REG(val, div) ((val) == 0 ? 255 : \ - SENSORS_LIMIT((1310720 / (val) / \ + clamp_val((1310720 / (val) / \ DIV_FROM_REG(div)), 1, 254)) /* --------------------------------------------------------------------- * Super-I/O constants and functions * --------------------------------------------------------------------- */ -/* Configuration index port registers - * The vt1211 can live at 2 different addresses so we need to probe both */ +/* + * Configuration index port registers + * The vt1211 can live at 2 different addresses so we need to probe both + */ #define SIO_REG_CIP1 0x2e #define SIO_REG_CIP2 0x4e @@ -375,7 +383,12 @@ static ssize_t set_in(struct device *dev, struct device_attribute *attr, to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; int fn = sensor_attr_2->nr; - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); switch (fn) { @@ -444,7 +457,12 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *attr, to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; int fn = sensor_attr_2->nr; - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); switch (fn) { @@ -515,8 +533,13 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr, to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; int fn = sensor_attr_2->nr; - long val = simple_strtol(buf, NULL, 10); int reg; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); @@ -534,16 +557,24 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr, break; case SHOW_SET_FAN_DIV: switch (val) { - case 1: data->fan_div[ix] = 0; break; - case 2: data->fan_div[ix] = 1; break; - case 4: data->fan_div[ix] = 2; break; - case 8: data->fan_div[ix] = 3; break; - default: - count = -EINVAL; - dev_warn(dev, "fan div value %ld not " - "supported. Choose one of 1, 2, " - "4, or 8.\n", val); - goto EXIT; + case 1: + data->fan_div[ix] = 0; + break; + case 2: + data->fan_div[ix] = 1; + break; + case 4: + data->fan_div[ix] = 2; + break; + case 8: + data->fan_div[ix] = 3; + break; + default: + count = -EINVAL; + dev_warn(dev, + "fan div value %ld not supported. Choose one of 1, 2, 4, or 8.\n", + val); + goto EXIT; } vt1211_write8(data, VT1211_REG_FAN_DIV, ((data->fan_div[1] << 6) | @@ -608,8 +639,13 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; int fn = sensor_attr_2->nr; - long val = simple_strtol(buf, NULL, 10); int tmp, reg; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); @@ -626,11 +662,12 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, switch (val) { case 0: data->pwm_ctl[ix] &= 7; - /* disable SmartGuardian if both PWM outputs are - * disabled */ - if ((data->pwm_ctl[ix ^ 1] & 1) == 0) { + /* + * disable SmartGuardian if both PWM outputs are + * disabled + */ + if ((data->pwm_ctl[ix ^ 1] & 1) == 0) data->fan_ctl &= 0xe; - } break; case 2: data->pwm_ctl[ix] |= 8; @@ -638,8 +675,9 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, break; default: count = -EINVAL; - dev_warn(dev, "pwm mode %ld not supported. " - "Choose one of 0 or 2.\n", val); + dev_warn(dev, + "pwm mode %ld not supported. Choose one of 0 or 2.\n", + val); goto EXIT; } vt1211_write8(data, VT1211_REG_PWM_CTL, @@ -651,22 +689,22 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, data->fan_ctl)); break; case SHOW_SET_PWM_FREQ: - val = 135000 / SENSORS_LIMIT(val, 135000 >> 7, 135000); + val = 135000 / clamp_val(val, 135000 >> 7, 135000); /* calculate tmp = log2(val) */ tmp = 0; - for (val >>= 1; val > 0; val >>= 1) { + for (val >>= 1; val > 0; val >>= 1) tmp++; - } /* sync the data cache */ reg = vt1211_read8(data, VT1211_REG_PWM_CLK); data->pwm_clk = (reg & 0xf8) | tmp; vt1211_write8(data, VT1211_REG_PWM_CLK, data->pwm_clk); break; case SHOW_SET_PWM_AUTO_CHANNELS_TEMP: - if ((val < 1) || (val > 7)) { + if (val < 1 || val > 7) { count = -EINVAL; - dev_warn(dev, "temp channel %ld not supported. " - "Choose a value between 1 and 7.\n", val); + dev_warn(dev, + "temp channel %ld not supported. Choose a value between 1 and 7.\n", + val); goto EXIT; } if (!ISTEMP(val - 1, data->uch_config)) { @@ -739,8 +777,14 @@ static ssize_t set_pwm_auto_point_temp(struct device *dev, to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; int ap = sensor_attr_2->nr; - long val = simple_strtol(buf, NULL, 10); int reg; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + mutex_lock(&data->update_lock); @@ -772,7 +816,7 @@ static ssize_t set_pwm_auto_point_temp(struct device *dev, * 1 1 : pwm2 low speed duty cycle (pwm_auto_pwm[1][1]) * 1 2 : pwm2 high speed duty cycle (pwm_auto_pwm[1][2]) * 1 3 : pwm2 full speed (pwm_auto_pwm[1][3], hard-wired to 255) -*/ + */ static ssize_t show_pwm_auto_point_pwm(struct device *dev, struct device_attribute *attr, @@ -796,16 +840,15 @@ static ssize_t set_pwm_auto_point_pwm(struct device *dev, to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; int ap = sensor_attr_2->nr; - long val = simple_strtol(buf, NULL, 10); + unsigned long val; + int err; - if ((val < 0) || (val > 255)) { - dev_err(dev, "pwm value %ld is out of range. " - "Choose a value between 0 and 255.\n" , val); - return -EINVAL; - } + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - data->pwm_auto_pwm[ix][ap] = val; + data->pwm_auto_pwm[ix][ap] = clamp_val(val, 0, 255); vt1211_write8(data, VT1211_REG_PWM_AUTO_PWM(ix, ap), data->pwm_auto_pwm[ix][ap]); mutex_unlock(&data->update_lock); @@ -829,7 +872,12 @@ static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct vt1211_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; data->vrm = val; @@ -864,112 +912,99 @@ static ssize_t show_alarms(struct device *dev, * Device attribute structs * --------------------------------------------------------------------- */ -#define SENSOR_ATTR_IN_INPUT(ix) \ - SENSOR_ATTR_2(in##ix##_input, S_IRUGO, \ - show_in, NULL, SHOW_IN_INPUT, ix) - -static struct sensor_device_attribute_2 vt1211_sysfs_in_input[] = { - SENSOR_ATTR_IN_INPUT(0), - SENSOR_ATTR_IN_INPUT(1), - SENSOR_ATTR_IN_INPUT(2), - SENSOR_ATTR_IN_INPUT(3), - SENSOR_ATTR_IN_INPUT(4), - SENSOR_ATTR_IN_INPUT(5), -}; - -#define SENSOR_ATTR_IN_MIN(ix) \ +#define SENSOR_ATTR_IN(ix) \ +{ SENSOR_ATTR_2(in##ix##_input, S_IRUGO, \ + show_in, NULL, SHOW_IN_INPUT, ix), \ SENSOR_ATTR_2(in##ix##_min, S_IRUGO | S_IWUSR, \ - show_in, set_in, SHOW_SET_IN_MIN, ix) - -static struct sensor_device_attribute_2 vt1211_sysfs_in_min[] = { - SENSOR_ATTR_IN_MIN(0), - SENSOR_ATTR_IN_MIN(1), - SENSOR_ATTR_IN_MIN(2), - SENSOR_ATTR_IN_MIN(3), - SENSOR_ATTR_IN_MIN(4), - SENSOR_ATTR_IN_MIN(5), -}; - -#define SENSOR_ATTR_IN_MAX(ix) \ + show_in, set_in, SHOW_SET_IN_MIN, ix), \ SENSOR_ATTR_2(in##ix##_max, S_IRUGO | S_IWUSR, \ - show_in, set_in, SHOW_SET_IN_MAX, ix) - -static struct sensor_device_attribute_2 vt1211_sysfs_in_max[] = { - SENSOR_ATTR_IN_MAX(0), - SENSOR_ATTR_IN_MAX(1), - SENSOR_ATTR_IN_MAX(2), - SENSOR_ATTR_IN_MAX(3), - SENSOR_ATTR_IN_MAX(4), - SENSOR_ATTR_IN_MAX(5), + show_in, set_in, SHOW_SET_IN_MAX, ix), \ + SENSOR_ATTR_2(in##ix##_alarm, S_IRUGO, \ + show_in, NULL, SHOW_IN_ALARM, ix) \ +} + +static struct sensor_device_attribute_2 vt1211_sysfs_in[][4] = { + SENSOR_ATTR_IN(0), + SENSOR_ATTR_IN(1), + SENSOR_ATTR_IN(2), + SENSOR_ATTR_IN(3), + SENSOR_ATTR_IN(4), + SENSOR_ATTR_IN(5) }; -#define SENSOR_ATTR_IN_ALARM(ix) \ - SENSOR_ATTR_2(in##ix##_alarm, S_IRUGO, \ - show_in, NULL, SHOW_IN_ALARM, ix) - -static struct sensor_device_attribute_2 vt1211_sysfs_in_alarm[] = { - SENSOR_ATTR_IN_ALARM(0), - SENSOR_ATTR_IN_ALARM(1), - SENSOR_ATTR_IN_ALARM(2), - SENSOR_ATTR_IN_ALARM(3), - SENSOR_ATTR_IN_ALARM(4), - SENSOR_ATTR_IN_ALARM(5), +#define IN_UNIT_ATTRS(X) \ +{ &vt1211_sysfs_in[X][0].dev_attr.attr, \ + &vt1211_sysfs_in[X][1].dev_attr.attr, \ + &vt1211_sysfs_in[X][2].dev_attr.attr, \ + &vt1211_sysfs_in[X][3].dev_attr.attr, \ + NULL \ +} + +static struct attribute *vt1211_in_attr[][5] = { + IN_UNIT_ATTRS(0), + IN_UNIT_ATTRS(1), + IN_UNIT_ATTRS(2), + IN_UNIT_ATTRS(3), + IN_UNIT_ATTRS(4), + IN_UNIT_ATTRS(5) }; -#define SENSOR_ATTR_TEMP_INPUT(ix) \ - SENSOR_ATTR_2(temp##ix##_input, S_IRUGO, \ - show_temp, NULL, SHOW_TEMP_INPUT, ix-1) - -static struct sensor_device_attribute_2 vt1211_sysfs_temp_input[] = { - SENSOR_ATTR_TEMP_INPUT(1), - SENSOR_ATTR_TEMP_INPUT(2), - SENSOR_ATTR_TEMP_INPUT(3), - SENSOR_ATTR_TEMP_INPUT(4), - SENSOR_ATTR_TEMP_INPUT(5), - SENSOR_ATTR_TEMP_INPUT(6), - SENSOR_ATTR_TEMP_INPUT(7), +static const struct attribute_group vt1211_in_attr_group[] = { + { .attrs = vt1211_in_attr[0] }, + { .attrs = vt1211_in_attr[1] }, + { .attrs = vt1211_in_attr[2] }, + { .attrs = vt1211_in_attr[3] }, + { .attrs = vt1211_in_attr[4] }, + { .attrs = vt1211_in_attr[5] } }; -#define SENSOR_ATTR_TEMP_MAX(ix) \ +#define SENSOR_ATTR_TEMP(ix) \ +{ SENSOR_ATTR_2(temp##ix##_input, S_IRUGO, \ + show_temp, NULL, SHOW_TEMP_INPUT, ix-1), \ SENSOR_ATTR_2(temp##ix##_max, S_IRUGO | S_IWUSR, \ - show_temp, set_temp, SHOW_SET_TEMP_MAX, ix-1) - -static struct sensor_device_attribute_2 vt1211_sysfs_temp_max[] = { - SENSOR_ATTR_TEMP_MAX(1), - SENSOR_ATTR_TEMP_MAX(2), - SENSOR_ATTR_TEMP_MAX(3), - SENSOR_ATTR_TEMP_MAX(4), - SENSOR_ATTR_TEMP_MAX(5), - SENSOR_ATTR_TEMP_MAX(6), - SENSOR_ATTR_TEMP_MAX(7), + show_temp, set_temp, SHOW_SET_TEMP_MAX, ix-1), \ + SENSOR_ATTR_2(temp##ix##_max_hyst, S_IRUGO | S_IWUSR, \ + show_temp, set_temp, SHOW_SET_TEMP_MAX_HYST, ix-1), \ + SENSOR_ATTR_2(temp##ix##_alarm, S_IRUGO, \ + show_temp, NULL, SHOW_TEMP_ALARM, ix-1) \ +} + +static struct sensor_device_attribute_2 vt1211_sysfs_temp[][4] = { + SENSOR_ATTR_TEMP(1), + SENSOR_ATTR_TEMP(2), + SENSOR_ATTR_TEMP(3), + SENSOR_ATTR_TEMP(4), + SENSOR_ATTR_TEMP(5), + SENSOR_ATTR_TEMP(6), + SENSOR_ATTR_TEMP(7), }; -#define SENSOR_ATTR_TEMP_MAX_HYST(ix) \ - SENSOR_ATTR_2(temp##ix##_max_hyst, S_IRUGO | S_IWUSR, \ - show_temp, set_temp, SHOW_SET_TEMP_MAX_HYST, ix-1) - -static struct sensor_device_attribute_2 vt1211_sysfs_temp_max_hyst[] = { - SENSOR_ATTR_TEMP_MAX_HYST(1), - SENSOR_ATTR_TEMP_MAX_HYST(2), - SENSOR_ATTR_TEMP_MAX_HYST(3), - SENSOR_ATTR_TEMP_MAX_HYST(4), - SENSOR_ATTR_TEMP_MAX_HYST(5), - SENSOR_ATTR_TEMP_MAX_HYST(6), - SENSOR_ATTR_TEMP_MAX_HYST(7), +#define TEMP_UNIT_ATTRS(X) \ +{ &vt1211_sysfs_temp[X][0].dev_attr.attr, \ + &vt1211_sysfs_temp[X][1].dev_attr.attr, \ + &vt1211_sysfs_temp[X][2].dev_attr.attr, \ + &vt1211_sysfs_temp[X][3].dev_attr.attr, \ + NULL \ +} + +static struct attribute *vt1211_temp_attr[][5] = { + TEMP_UNIT_ATTRS(0), + TEMP_UNIT_ATTRS(1), + TEMP_UNIT_ATTRS(2), + TEMP_UNIT_ATTRS(3), + TEMP_UNIT_ATTRS(4), + TEMP_UNIT_ATTRS(5), + TEMP_UNIT_ATTRS(6) }; -#define SENSOR_ATTR_TEMP_ALARM(ix) \ - SENSOR_ATTR_2(temp##ix##_alarm, S_IRUGO, \ - show_temp, NULL, SHOW_TEMP_ALARM, ix-1) - -static struct sensor_device_attribute_2 vt1211_sysfs_temp_alarm[] = { - SENSOR_ATTR_TEMP_ALARM(1), - SENSOR_ATTR_TEMP_ALARM(2), - SENSOR_ATTR_TEMP_ALARM(3), - SENSOR_ATTR_TEMP_ALARM(4), - SENSOR_ATTR_TEMP_ALARM(5), - SENSOR_ATTR_TEMP_ALARM(6), - SENSOR_ATTR_TEMP_ALARM(7), +static const struct attribute_group vt1211_temp_attr_group[] = { + { .attrs = vt1211_temp_attr[0] }, + { .attrs = vt1211_temp_attr[1] }, + { .attrs = vt1211_temp_attr[2] }, + { .attrs = vt1211_temp_attr[3] }, + { .attrs = vt1211_temp_attr[4] }, + { .attrs = vt1211_temp_attr[5] }, + { .attrs = vt1211_temp_attr[6] } }; #define SENSOR_ATTR_FAN(ix) \ @@ -1054,7 +1089,7 @@ static struct device_attribute vt1211_sysfs_misc[] = { * Device registration and initialization * --------------------------------------------------------------------- */ -static void __devinit vt1211_init_device(struct vt1211_data *data) +static void vt1211_init_device(struct vt1211_data *data) { /* set VRM */ data->vrm = vid_which_vrm(); @@ -1067,7 +1102,8 @@ static void __devinit vt1211_init_device(struct vt1211_data *data) vt1211_write8(data, VT1211_REG_UCH_CONFIG, data->uch_config); } - /* Initialize the interrupt mode (if request at module load time). + /* + * Initialize the interrupt mode (if request at module load time). * The VT1211 implements 3 different modes for clearing interrupts: * 0: Clear INT when status register is read. Regenerate INT as long * as temp stays above hysteresis limit. @@ -1077,7 +1113,8 @@ static void __devinit vt1211_init_device(struct vt1211_data *data) * 2: Clear INT when temp falls below max limit. * * The driver only allows to force mode 0 since that's the only one - * that makes sense for 'sensors' */ + * that makes sense for 'sensors' + */ if (int_mode == 0) { vt1211_write8(data, VT1211_REG_TEMP1_CONFIG, 0); vt1211_write8(data, VT1211_REG_TEMP2_CONFIG, 0); @@ -1093,54 +1130,37 @@ static void vt1211_remove_sysfs(struct platform_device *pdev) struct device *dev = &pdev->dev; int i; - for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_in_input); i++) { - device_remove_file(dev, - &vt1211_sysfs_in_input[i].dev_attr); - device_remove_file(dev, - &vt1211_sysfs_in_min[i].dev_attr); - device_remove_file(dev, - &vt1211_sysfs_in_max[i].dev_attr); - device_remove_file(dev, - &vt1211_sysfs_in_alarm[i].dev_attr); - } - for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_temp_input); i++) { - device_remove_file(dev, - &vt1211_sysfs_temp_input[i].dev_attr); - device_remove_file(dev, - &vt1211_sysfs_temp_max[i].dev_attr); - device_remove_file(dev, - &vt1211_sysfs_temp_max_hyst[i].dev_attr); - device_remove_file(dev, - &vt1211_sysfs_temp_alarm[i].dev_attr); - } + for (i = 0; i < ARRAY_SIZE(vt1211_in_attr_group); i++) + sysfs_remove_group(&dev->kobj, &vt1211_in_attr_group[i]); + + for (i = 0; i < ARRAY_SIZE(vt1211_temp_attr_group); i++) + sysfs_remove_group(&dev->kobj, &vt1211_temp_attr_group[i]); + for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_fan_pwm); i++) { device_remove_file(dev, &vt1211_sysfs_fan_pwm[i].dev_attr); } - for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_misc); i++) { + for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_misc); i++) device_remove_file(dev, &vt1211_sysfs_misc[i]); - } } -static int __devinit vt1211_probe(struct platform_device *pdev) +static int vt1211_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct vt1211_data *data; struct resource *res; int i, err; - if (!(data = kzalloc(sizeof(struct vt1211_data), GFP_KERNEL))) { - err = -ENOMEM; - dev_err(dev, "Out of memory\n"); - goto EXIT; - } + data = devm_kzalloc(dev, sizeof(struct vt1211_data), GFP_KERNEL); + if (!data) + return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start, resource_size(res), DRVNAME)) { - err = -EBUSY; + if (!devm_request_region(dev, res->start, resource_size(res), + DRVNAME)) { dev_err(dev, "Failed to request region 0x%lx-0x%lx\n", (unsigned long)res->start, (unsigned long)res->end); - goto EXIT_KFREE; + return -EBUSY; } data->addr = res->start; data->name = DRVNAME; @@ -1152,47 +1172,33 @@ static int __devinit vt1211_probe(struct platform_device *pdev) vt1211_init_device(data); /* Create sysfs interface files */ - for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_in_input); i++) { + for (i = 0; i < ARRAY_SIZE(vt1211_in_attr_group); i++) { if (ISVOLT(i, data->uch_config)) { - if ((err = device_create_file(dev, - &vt1211_sysfs_in_input[i].dev_attr)) || - (err = device_create_file(dev, - &vt1211_sysfs_in_min[i].dev_attr)) || - (err = device_create_file(dev, - &vt1211_sysfs_in_max[i].dev_attr)) || - (err = device_create_file(dev, - &vt1211_sysfs_in_alarm[i].dev_attr))) { + err = sysfs_create_group(&dev->kobj, + &vt1211_in_attr_group[i]); + if (err) goto EXIT_DEV_REMOVE; - } } } - for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_temp_input); i++) { + for (i = 0; i < ARRAY_SIZE(vt1211_temp_attr_group); i++) { if (ISTEMP(i, data->uch_config)) { - if ((err = device_create_file(dev, - &vt1211_sysfs_temp_input[i].dev_attr)) || - (err = device_create_file(dev, - &vt1211_sysfs_temp_max[i].dev_attr)) || - (err = device_create_file(dev, - &vt1211_sysfs_temp_max_hyst[i].dev_attr)) || - (err = device_create_file(dev, - &vt1211_sysfs_temp_alarm[i].dev_attr))) { + err = sysfs_create_group(&dev->kobj, + &vt1211_temp_attr_group[i]); + if (err) goto EXIT_DEV_REMOVE; - } } } for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_fan_pwm); i++) { err = device_create_file(dev, &vt1211_sysfs_fan_pwm[i].dev_attr); - if (err) { + if (err) goto EXIT_DEV_REMOVE; - } } for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_misc); i++) { err = device_create_file(dev, &vt1211_sysfs_misc[i]); - if (err) { + if (err) goto EXIT_DEV_REMOVE; - } } /* Register device */ @@ -1209,26 +1215,15 @@ EXIT_DEV_REMOVE: dev_err(dev, "Sysfs interface creation failed (%d)\n", err); EXIT_DEV_REMOVE_SILENT: vt1211_remove_sysfs(pdev); - release_region(res->start, resource_size(res)); -EXIT_KFREE: - platform_set_drvdata(pdev, NULL); - kfree(data); -EXIT: return err; } -static int __devexit vt1211_remove(struct platform_device *pdev) +static int vt1211_remove(struct platform_device *pdev) { struct vt1211_data *data = platform_get_drvdata(pdev); - struct resource *res; hwmon_device_unregister(data->hwmon_dev); vt1211_remove_sysfs(pdev); - platform_set_drvdata(pdev, NULL); - kfree(data); - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - release_region(res->start, resource_size(res)); return 0; } @@ -1239,7 +1234,7 @@ static struct platform_driver vt1211_driver = { .name = DRVNAME, }, .probe = vt1211_probe, - .remove = __devexit_p(vt1211_remove), + .remove = vt1211_remove, }; static int __init vt1211_device_add(unsigned short address) @@ -1254,8 +1249,7 @@ static int __init vt1211_device_add(unsigned short address) pdev = platform_device_alloc(DRVNAME, address); if (!pdev) { err = -ENOMEM; - printk(KERN_ERR DRVNAME ": Device allocation failed (%d)\n", - err); + pr_err("Device allocation failed (%d)\n", err); goto EXIT; } @@ -1266,15 +1260,13 @@ static int __init vt1211_device_add(unsigned short address) err = platform_device_add_resources(pdev, &res, 1); if (err) { - printk(KERN_ERR DRVNAME ": Device resource addition failed " - "(%d)\n", err); + pr_err("Device resource addition failed (%d)\n", err); goto EXIT_DEV_PUT; } err = platform_device_add(pdev); if (err) { - printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", - err); + pr_err("Device addition failed (%d)\n", err); goto EXIT_DEV_PUT; } @@ -1294,30 +1286,26 @@ static int __init vt1211_find(int sio_cip, unsigned short *address) superio_enter(sio_cip); devid = force_id ? force_id : superio_inb(sio_cip, SIO_VT1211_DEVID); - if (devid != SIO_VT1211_ID) { + if (devid != SIO_VT1211_ID) goto EXIT; - } superio_select(sio_cip, SIO_VT1211_LDN_HWMON); if ((superio_inb(sio_cip, SIO_VT1211_ACTIVE) & 1) == 0) { - printk(KERN_WARNING DRVNAME ": HW monitor is disabled, " - "skipping\n"); + pr_warn("HW monitor is disabled, skipping\n"); goto EXIT; } *address = ((superio_inb(sio_cip, SIO_VT1211_BADDR) << 8) | (superio_inb(sio_cip, SIO_VT1211_BADDR + 1))) & 0xff00; if (*address == 0) { - printk(KERN_WARNING DRVNAME ": Base address is not set, " - "skipping\n"); + pr_warn("Base address is not set, skipping\n"); goto EXIT; } err = 0; - printk(KERN_INFO DRVNAME ": Found VT1211 chip at 0x%04x, " - "revision %u\n", *address, - superio_inb(sio_cip, SIO_VT1211_DEVREV)); + pr_info("Found VT1211 chip at 0x%04x, revision %u\n", + *address, superio_inb(sio_cip, SIO_VT1211_DEVREV)); EXIT: superio_exit(sio_cip); @@ -1329,35 +1317,35 @@ static int __init vt1211_init(void) int err; unsigned short address = 0; - if ((err = vt1211_find(SIO_REG_CIP1, &address)) && - (err = vt1211_find(SIO_REG_CIP2, &address))) { - goto EXIT; + err = vt1211_find(SIO_REG_CIP1, &address); + if (err) { + err = vt1211_find(SIO_REG_CIP2, &address); + if (err) + goto EXIT; } if ((uch_config < -1) || (uch_config > 31)) { err = -EINVAL; - printk(KERN_WARNING DRVNAME ": Invalid UCH configuration %d. " - "Choose a value between 0 and 31.\n", uch_config); - goto EXIT; + pr_warn("Invalid UCH configuration %d. Choose a value between 0 and 31.\n", + uch_config); + goto EXIT; } if ((int_mode < -1) || (int_mode > 0)) { err = -EINVAL; - printk(KERN_WARNING DRVNAME ": Invalid interrupt mode %d. " - "Only mode 0 is supported.\n", int_mode); - goto EXIT; + pr_warn("Invalid interrupt mode %d. Only mode 0 is supported.\n", + int_mode); + goto EXIT; } err = platform_driver_register(&vt1211_driver); - if (err) { + if (err) goto EXIT; - } /* Sets global pdev as a side effect */ err = vt1211_device_add(address); - if (err) { + if (err) goto EXIT_DRV_UNREGISTER; - } return 0; diff --git a/drivers/hwmon/vt8231.c b/drivers/hwmon/vt8231.c index d47b4c9949c..b3babe3326f 100644 --- a/drivers/hwmon/vt8231.c +++ b/drivers/hwmon/vt8231.c @@ -1,28 +1,31 @@ /* - vt8231.c - Part of lm_sensors, Linux kernel modules - for hardware monitoring + * vt8231.c - Part of lm_sensors, Linux kernel modules + * for hardware monitoring + * + * Copyright (c) 2005 Roger Lucas <vt8231@hiddenengine.co.uk> + * Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com> + * Aaron M. Marsh <amarsh@sdf.lonestar.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; 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. + */ - Copyright (c) 2005 Roger Lucas <vt8231@hiddenengine.co.uk> - Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com> - Aaron M. Marsh <amarsh@sdf.lonestar.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; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ +/* + * Supports VIA VT8231 South Bridge embedded sensors + */ -/* Supports VIA VT8231 South Bridge embedded sensors -*/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/init.h> @@ -48,26 +51,27 @@ static struct platform_device *pdev; #define VT8231_BASE_REG 0x70 #define VT8231_ENABLE_REG 0x74 -/* The VT8231 registers - - The reset value for the input channel configuration is used (Reg 0x4A=0x07) - which sets the selected inputs marked with '*' below if multiple options are - possible: - - Voltage Mode Temperature Mode - Sensor Linux Id Linux Id VIA Id - -------- -------- -------- ------ - CPU Diode N/A temp1 0 - UIC1 in0 temp2 * 1 - UIC2 in1 * temp3 2 - UIC3 in2 * temp4 3 - UIC4 in3 * temp5 4 - UIC5 in4 * temp6 5 - 3.3V in5 N/A - - Note that the BIOS may set the configuration register to a different value - to match the motherboard configuration. -*/ +/* + * The VT8231 registers + * + * The reset value for the input channel configuration is used (Reg 0x4A=0x07) + * which sets the selected inputs marked with '*' below if multiple options are + * possible: + * + * Voltage Mode Temperature Mode + * Sensor Linux Id Linux Id VIA Id + * -------- -------- -------- ------ + * CPU Diode N/A temp1 0 + * UIC1 in0 temp2 * 1 + * UIC2 in1 * temp3 2 + * UIC3 in2 * temp4 3 + * UIC4 in3 * temp5 4 + * UIC5 in4 * temp6 5 + * 3.3V in5 N/A + * + * Note that the BIOS may set the configuration register to a different value + * to match the motherboard configuration. + */ /* fans numbered 0-1 */ #define VT8231_REG_FAN_MIN(nr) (0x3b + (nr)) @@ -79,13 +83,14 @@ static const u8 regvolt[] = { 0x21, 0x22, 0x23, 0x24, 0x25, 0x26 }; static const u8 regvoltmax[] = { 0x3d, 0x2b, 0x2d, 0x2f, 0x31, 0x33 }; static const u8 regvoltmin[] = { 0x3e, 0x2c, 0x2e, 0x30, 0x32, 0x34 }; -/* Temperatures are numbered 1-6 according to the Linux kernel specification. -** -** In the VIA datasheet, however, the temperatures are numbered from zero. -** Since it is important that this driver can easily be compared to the VIA -** datasheet, we will use the VIA numbering within this driver and map the -** kernel sysfs device name to the VIA number in the sysfs callback. -*/ +/* + * Temperatures are numbered 1-6 according to the Linux kernel specification. + * + * In the VIA datasheet, however, the temperatures are numbered from zero. + * Since it is important that this driver can easily be compared to the VIA + * datasheet, we will use the VIA numbering within this driver and map the + * kernel sysfs device name to the VIA number in the sysfs callback. + */ #define VT8231_REG_TEMP_LOW01 0x49 #define VT8231_REG_TEMP_LOW25 0x4d @@ -106,9 +111,10 @@ static const u8 regtempmin[] = { 0x3a, 0x3e, 0x2c, 0x2e, 0x30, 0x32 }; #define VT8231_REG_TEMP1_CONFIG 0x4b #define VT8231_REG_TEMP2_CONFIG 0x4c -/* temps 0-5 as numbered in VIA datasheet - see later for mapping to Linux -** numbering -*/ +/* + * temps 0-5 as numbered in VIA datasheet - see later for mapping to Linux + * numbering + */ #define ISTEMP(i, ch_config) ((i) == 0 ? 1 : \ ((ch_config) >> ((i)+1)) & 0x01) /* voltages 0-5 */ @@ -117,29 +123,31 @@ static const u8 regtempmin[] = { 0x3a, 0x3e, 0x2c, 0x2e, 0x30, 0x32 }; #define DIV_FROM_REG(val) (1 << (val)) -/* NB The values returned here are NOT temperatures. The calibration curves -** for the thermistor curves are board-specific and must go in the -** sensors.conf file. Temperature sensors are actually ten bits, but the -** VIA datasheet only considers the 8 MSBs obtained from the regtemp[] -** register. The temperature value returned should have a magnitude of 3, -** so we use the VIA scaling as the "true" scaling and use the remaining 2 -** LSBs as fractional precision. -** -** All the on-chip hardware temperature comparisons for the alarms are only -** 8-bits wide, and compare against the 8 MSBs of the temperature. The bits -** in the registers VT8231_REG_TEMP_LOW01 and VT8231_REG_TEMP_LOW25 are -** ignored. -*/ - -/******** FAN RPM CONVERSIONS ******** -** This chip saturates back at 0, not at 255 like many the other chips. -** So, 0 means 0 RPM -*/ +/* + * NB The values returned here are NOT temperatures. The calibration curves + * for the thermistor curves are board-specific and must go in the + * sensors.conf file. Temperature sensors are actually ten bits, but the + * VIA datasheet only considers the 8 MSBs obtained from the regtemp[] + * register. The temperature value returned should have a magnitude of 3, + * so we use the VIA scaling as the "true" scaling and use the remaining 2 + * LSBs as fractional precision. + * + * All the on-chip hardware temperature comparisons for the alarms are only + * 8-bits wide, and compare against the 8 MSBs of the temperature. The bits + * in the registers VT8231_REG_TEMP_LOW01 and VT8231_REG_TEMP_LOW25 are + * ignored. + */ + +/* + ****** FAN RPM CONVERSIONS ******** + * This chip saturates back at 0, not at 255 like many the other chips. + * So, 0 means 0 RPM + */ static inline u8 FAN_TO_REG(long rpm, int div) { - if (rpm == 0) + if (rpm <= 0 || rpm > 1310720) return 0; - return SENSORS_LIMIT(1310720 / (rpm * div), 1, 255); + return clamp_val(1310720 / (rpm * div), 1, 255); } #define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : 1310720 / ((val) * (div))) @@ -168,7 +176,7 @@ struct vt8231_data { static struct pci_dev *s_bridge; static int vt8231_probe(struct platform_device *pdev); -static int __devexit vt8231_remove(struct platform_device *pdev); +static int vt8231_remove(struct platform_device *pdev); static struct vt8231_data *vt8231_update_device(struct device *dev); static void vt8231_init_device(struct vt8231_data *data); @@ -220,10 +228,15 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct vt8231_data *data = dev_get_drvdata(dev); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - data->in_min[nr] = SENSORS_LIMIT(((val * 958) / 10000) + 3, 0, 255); + data->in_min[nr] = clamp_val(((val * 958) / 10000) + 3, 0, 255); vt8231_write_value(data, regvoltmin[nr], data->in_min[nr]); mutex_unlock(&data->update_lock); return count; @@ -235,10 +248,15 @@ static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct vt8231_data *data = dev_get_drvdata(dev); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - data->in_max[nr] = SENSORS_LIMIT(((val * 958) / 10000) + 3, 0, 255); + data->in_max[nr] = clamp_val(((val * 958) / 10000) + 3, 0, 255); vt8231_write_value(data, regvoltmax[nr], data->in_max[nr]); mutex_unlock(&data->update_lock); return count; @@ -276,11 +294,16 @@ static ssize_t set_in5_min(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct vt8231_data *data = dev_get_drvdata(dev); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - data->in_min[5] = SENSORS_LIMIT(((val * 958 * 34) / (10000 * 54)) + 3, - 0, 255); + data->in_min[5] = clamp_val(((val * 958 * 34) / (10000 * 54)) + 3, + 0, 255); vt8231_write_value(data, regvoltmin[5], data->in_min[5]); mutex_unlock(&data->update_lock); return count; @@ -290,11 +313,16 @@ static ssize_t set_in5_max(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct vt8231_data *data = dev_get_drvdata(dev); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - data->in_max[5] = SENSORS_LIMIT(((val * 958 * 34) / (10000 * 54)) + 3, - 0, 255); + data->in_max[5] = clamp_val(((val * 958 * 34) / (10000 * 54)) + 3, + 0, 255); vt8231_write_value(data, regvoltmax[5], data->in_max[5]); mutex_unlock(&data->update_lock); return count; @@ -344,10 +372,15 @@ static ssize_t set_temp0_max(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct vt8231_data *data = dev_get_drvdata(dev); - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - data->temp_max[0] = SENSORS_LIMIT((val + 500) / 1000, 0, 255); + data->temp_max[0] = clamp_val((val + 500) / 1000, 0, 255); vt8231_write_value(data, regtempmax[0], data->temp_max[0]); mutex_unlock(&data->update_lock); return count; @@ -356,10 +389,15 @@ static ssize_t set_temp0_min(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct vt8231_data *data = dev_get_drvdata(dev); - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - data->temp_min[0] = SENSORS_LIMIT((val + 500) / 1000, 0, 255); + data->temp_min[0] = clamp_val((val + 500) / 1000, 0, 255); vt8231_write_value(data, regtempmin[0], data->temp_min[0]); mutex_unlock(&data->update_lock); return count; @@ -398,10 +436,15 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct vt8231_data *data = dev_get_drvdata(dev); - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - data->temp_max[nr] = SENSORS_LIMIT(TEMP_MAXMIN_TO_REG(val), 0, 255); + data->temp_max[nr] = clamp_val(TEMP_MAXMIN_TO_REG(val), 0, 255); vt8231_write_value(data, regtempmax[nr], data->temp_max[nr]); mutex_unlock(&data->update_lock); return count; @@ -412,18 +455,24 @@ static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct vt8231_data *data = dev_get_drvdata(dev); - int val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - data->temp_min[nr] = SENSORS_LIMIT(TEMP_MAXMIN_TO_REG(val), 0, 255); + data->temp_min[nr] = clamp_val(TEMP_MAXMIN_TO_REG(val), 0, 255); vt8231_write_value(data, regtempmin[nr], data->temp_min[nr]); mutex_unlock(&data->update_lock); return count; } -/* Note that these map the Linux temperature sensor numbering (1-6) to the VIA -** temperature sensor numbering (0-5) -*/ +/* + * Note that these map the Linux temperature sensor numbering (1-6) to the VIA + * temperature sensor numbering (0-5) + */ #define define_temperature_sysfs(offset) \ static SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO, \ show_temp, NULL, offset - 1); \ @@ -434,7 +483,8 @@ static SENSOR_DEVICE_ATTR(temp##offset##_max_hyst, S_IRUGO | S_IWUSR, \ static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp0, NULL); static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, show_temp0_max, set_temp0_max); -static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp0_min, set_temp0_min); +static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp0_min, + set_temp0_min); define_temperature_sysfs(2); define_temperature_sysfs(3); @@ -478,7 +528,12 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct vt8231_data *data = dev_get_drvdata(dev); - int val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); @@ -492,21 +547,35 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, { struct vt8231_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; int nr = sensor_attr->index; int old = vt8231_read_value(data, VT8231_REG_FANDIV); long min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])); + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); switch (val) { - case 1: data->fan_div[nr] = 0; break; - case 2: data->fan_div[nr] = 1; break; - case 4: data->fan_div[nr] = 2; break; - case 8: data->fan_div[nr] = 3; break; + case 1: + data->fan_div[nr] = 0; + break; + case 2: + data->fan_div[nr] = 1; + break; + case 4: + data->fan_div[nr] = 2; + break; + case 8: + data->fan_div[nr] = 3; + break; default: - dev_err(dev, "fan_div value %ld not supported. " - "Choose one of 1, 2, 4 or 8!\n", val); + dev_err(dev, + "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n", + val); mutex_unlock(&data->update_lock); return -EINVAL; } @@ -694,7 +763,7 @@ static struct platform_driver vt8231_driver = { .name = "vt8231", }, .probe = vt8231_probe, - .remove = __devexit_p(vt8231_remove), + .remove = vt8231_remove, }; static const struct pci_device_id vt8231_pci_ids[] = { @@ -704,8 +773,8 @@ static const struct pci_device_id vt8231_pci_ids[] = { MODULE_DEVICE_TABLE(pci, vt8231_pci_ids); -static int __devinit vt8231_pci_probe(struct pci_dev *dev, - const struct pci_device_id *id); +static int vt8231_pci_probe(struct pci_dev *dev, + const struct pci_device_id *id); static struct pci_driver vt8231_pci_driver = { .name = "vt8231", @@ -721,17 +790,16 @@ static int vt8231_probe(struct platform_device *pdev) /* Reserve the ISA region */ res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start, VT8231_EXTENT, - vt8231_driver.driver.name)) { + if (!devm_request_region(&pdev->dev, res->start, VT8231_EXTENT, + vt8231_driver.driver.name)) { dev_err(&pdev->dev, "Region 0x%lx-0x%lx already in use!\n", (unsigned long)res->start, (unsigned long)res->end); return -ENODEV; } - if (!(data = kzalloc(sizeof(struct vt8231_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit_release; - } + data = devm_kzalloc(&pdev->dev, sizeof(struct vt8231_data), GFP_KERNEL); + if (!data) + return -ENOMEM; platform_set_drvdata(pdev, data); data->addr = res->start; @@ -741,24 +809,27 @@ static int vt8231_probe(struct platform_device *pdev) vt8231_init_device(data); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&pdev->dev.kobj, &vt8231_group))) - goto exit_free; + err = sysfs_create_group(&pdev->dev.kobj, &vt8231_group); + if (err) + return err; /* Must update device information to find out the config field */ data->uch_config = vt8231_read_value(data, VT8231_REG_UCH_CONFIG); for (i = 0; i < ARRAY_SIZE(vt8231_group_temps); i++) { if (ISTEMP(i, data->uch_config)) { - if ((err = sysfs_create_group(&pdev->dev.kobj, - &vt8231_group_temps[i]))) + err = sysfs_create_group(&pdev->dev.kobj, + &vt8231_group_temps[i]); + if (err) goto exit_remove_files; } } for (i = 0; i < ARRAY_SIZE(vt8231_group_volts); i++) { if (ISVOLT(i, data->uch_config)) { - if ((err = sysfs_create_group(&pdev->dev.kobj, - &vt8231_group_volts[i]))) + err = sysfs_create_group(&pdev->dev.kobj, + &vt8231_group_volts[i]); + if (err) goto exit_remove_files; } } @@ -778,17 +849,10 @@ exit_remove_files: sysfs_remove_group(&pdev->dev.kobj, &vt8231_group_temps[i]); sysfs_remove_group(&pdev->dev.kobj, &vt8231_group); - -exit_free: - platform_set_drvdata(pdev, NULL); - kfree(data); - -exit_release: - release_region(res->start, VT8231_EXTENT); return err; } -static int __devexit vt8231_remove(struct platform_device *pdev) +static int vt8231_remove(struct platform_device *pdev) { struct vt8231_data *data = platform_get_drvdata(pdev); int i; @@ -803,9 +867,6 @@ static int __devexit vt8231_remove(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &vt8231_group); - release_region(data->addr, VT8231_EXTENT); - platform_set_drvdata(pdev, NULL); - kfree(data); return 0; } @@ -864,17 +925,15 @@ static struct vt8231_data *vt8231_update_device(struct device *dev) (vt8231_read_value(data, VT8231_REG_ALARM2) << 8); /* Set alarm flags correctly */ - if (!data->fan[0] && data->fan_min[0]) { + if (!data->fan[0] && data->fan_min[0]) data->alarms |= 0x40; - } else if (data->fan[0] && !data->fan_min[0]) { + else if (data->fan[0] && !data->fan_min[0]) data->alarms &= ~0x40; - } - if (!data->fan[1] && data->fan_min[1]) { + if (!data->fan[1] && data->fan_min[1]) data->alarms |= 0x80; - } else if (data->fan[1] && !data->fan_min[1]) { + else if (data->fan[1] && !data->fan_min[1]) data->alarms &= ~0x80; - } data->last_updated = jiffies; data->valid = 1; @@ -885,7 +944,7 @@ static struct vt8231_data *vt8231_update_device(struct device *dev) return data; } -static int __devinit vt8231_device_add(unsigned short address) +static int vt8231_device_add(unsigned short address) { struct resource res = { .start = address, @@ -902,21 +961,19 @@ static int __devinit vt8231_device_add(unsigned short address) pdev = platform_device_alloc("vt8231", address); if (!pdev) { err = -ENOMEM; - printk(KERN_ERR "vt8231: Device allocation failed\n"); + pr_err("Device allocation failed\n"); goto exit; } err = platform_device_add_resources(pdev, &res, 1); if (err) { - printk(KERN_ERR "vt8231: Device resource addition failed " - "(%d)\n", err); + pr_err("Device resource addition failed (%d)\n", err); goto exit_device_put; } err = platform_device_add(pdev); if (err) { - printk(KERN_ERR "vt8231: Device addition failed (%d)\n", - err); + pr_err("Device addition failed (%d)\n", err); goto exit_device_put; } @@ -928,7 +985,7 @@ exit: return err; } -static int __devinit vt8231_pci_probe(struct pci_dev *dev, +static int vt8231_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { u16 address, val; @@ -948,8 +1005,7 @@ static int __devinit vt8231_pci_probe(struct pci_dev *dev, address = val & ~(VT8231_EXTENT - 1); if (address == 0) { - dev_err(&dev->dev, "base address not set -\ - upgrade BIOS or use force_addr=0xaddr\n"); + dev_err(&dev->dev, "base address not set - upgrade BIOS or use force_addr=0xaddr\n"); return -ENODEV; } @@ -972,13 +1028,16 @@ static int __devinit vt8231_pci_probe(struct pci_dev *dev, if (vt8231_device_add(address)) goto exit_unregister; - /* Always return failure here. This is to allow other drivers to bind + /* + * Always return failure here. This is to allow other drivers to bind * to this pci device. We don't really want to have control over the * pci device, we only wanted to read as few register values from it. */ - /* We do, however, mark ourselves as using the PCI device to stop it - getting unloaded. */ + /* + * We do, however, mark ourselves as using the PCI device to stop it + * getting unloaded. + */ s_bridge = pci_dev_get(dev); return -ENODEV; diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 0dcaba9b718..f0ab61db7a0 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -1,45 +1,51 @@ /* - w83627ehf - Driver for the hardware monitoring functionality of - the Winbond W83627EHF Super-I/O chip - Copyright (C) 2005 Jean Delvare <khali@linux-fr.org> - Copyright (C) 2006 Yuan Mu (Winbond), - Rudolf Marek <r.marek@assembler.cz> - David Hubbard <david.c.hubbard@gmail.com> - Daniel J Blueman <daniel.blueman@gmail.com> - - Shamelessly ripped from the w83627hf driver - Copyright (C) 2003 Mark Studebaker - - Thanks to Leon Moonen, Steve Cliffe and Grant Coady for their help - in testing and debugging this driver. - - This driver also supports the W83627EHG, which is the lead-free - version of the W83627EHF. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - - Supports the following chips: + * w83627ehf - Driver for the hardware monitoring functionality of + * the Winbond W83627EHF Super-I/O chip + * Copyright (C) 2005-2012 Jean Delvare <jdelvare@suse.de> + * Copyright (C) 2006 Yuan Mu (Winbond), + * Rudolf Marek <r.marek@assembler.cz> + * David Hubbard <david.c.hubbard@gmail.com> + * Daniel J Blueman <daniel.blueman@gmail.com> + * Copyright (C) 2010 Sheng-Yuan Huang (Nuvoton) (PS00) + * + * Shamelessly ripped from the w83627hf driver + * Copyright (C) 2003 Mark Studebaker + * + * Thanks to Leon Moonen, Steve Cliffe and Grant Coady for their help + * in testing and debugging this driver. + * + * This driver also supports the W83627EHG, which is the lead-free + * version of the W83627EHF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Supports the following chips: + * + * Chip #vin #fan #pwm #temp chip IDs man ID + * w83627ehf 10 5 4 3 0x8850 0x88 0x5ca3 + * 0x8860 0xa1 + * w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3 + * w83627dhg-p 9 5 4 3 0xb070 0xc1 0x5ca3 + * w83627uhg 8 2 2 3 0xa230 0xc1 0x5ca3 + * w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3 + * w83667hg-b 9 5 3 4 0xb350 0xc1 0x5ca3 + * nct6775f 9 4 3 9 0xb470 0xc1 0x5ca3 + * nct6776f 9 5 3 9 0xC330 0xc1 0x5ca3 + */ - Chip #vin #fan #pwm #temp chip IDs man ID - w83627ehf 10 5 4 3 0x8850 0x88 0x5ca3 - 0x8860 0xa1 - w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3 - w83627dhg-p 9 5 4 3 0xb070 0xc1 0x5ca3 - w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3 -*/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/init.h> @@ -55,20 +61,31 @@ #include <linux/io.h> #include "lm75.h" -enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83667hg }; +enum kinds { + w83627ehf, w83627dhg, w83627dhg_p, w83627uhg, + w83667hg, w83667hg_b, nct6775, nct6776, +}; /* used to set data->name = w83627ehf_device_names[data->sio_kind] */ -static const char * w83627ehf_device_names[] = { +static const char * const w83627ehf_device_names[] = { "w83627ehf", "w83627dhg", "w83627dhg", + "w83627uhg", "w83667hg", + "w83667hg", + "nct6775", + "nct6776", }; static unsigned short force_id; module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); +static unsigned short fan_debounce; +module_param(fan_debounce, ushort, 0); +MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); + #define DRVNAME "w83627ehf" /* @@ -76,7 +93,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID"); */ #define W83627EHF_LD_HWM 0x0b -#define W83667HG_LD_VID 0x0d +#define W83667HG_LD_VID 0x0d #define SIO_REG_LDSEL 0x07 /* Logical device select */ #define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ @@ -90,7 +107,11 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID"); #define SIO_W83627EHG_ID 0x8860 #define SIO_W83627DHG_ID 0xa020 #define SIO_W83627DHG_P_ID 0xb070 -#define SIO_W83667HG_ID 0xa510 +#define SIO_W83627UHG_ID 0xa230 +#define SIO_W83667HG_ID 0xa510 +#define SIO_W83667HG_B_ID 0xb350 +#define SIO_NCT6775_ID 0xb470 +#define SIO_NCT6776_ID 0xc330 #define SIO_ID_MASK 0xFFF0 static inline void @@ -124,6 +145,7 @@ superio_enter(int ioreg) static inline void superio_exit(int ioreg) { + outb(0xaa, ioreg); outb(0x02, ioreg); outb(0x02, ioreg + 1); } @@ -132,7 +154,7 @@ superio_exit(int ioreg) * ISA constants */ -#define IOREGION_ALIGNMENT ~7 +#define IOREGION_ALIGNMENT (~7) #define IOREGION_OFFSET 5 #define IOREGION_LENGTH 2 #define ADDR_REG_OFFSET 0 @@ -141,11 +163,13 @@ superio_exit(int ioreg) #define W83627EHF_REG_BANK 0x4E #define W83627EHF_REG_CONFIG 0x40 -/* Not currently used: +/* + * Not currently used: * REG_MAN_ID has the value 0x5ca3 for all supported chips. * REG_CHIP_ID == 0x88/0xa1/0xc1 depending on chip model. * REG_MAN_ID is at port 0x4f - * REG_CHIP_ID is at port 0x58 */ + * REG_CHIP_ID is at port 0x58 + */ static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 }; static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c }; @@ -158,13 +182,10 @@ static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c }; #define W83627EHF_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \ (0x550 + (nr) - 7)) -#define W83627EHF_REG_TEMP1 0x27 -#define W83627EHF_REG_TEMP1_HYST 0x3a -#define W83627EHF_REG_TEMP1_OVER 0x39 -static const u16 W83627EHF_REG_TEMP[] = { 0x150, 0x250 }; -static const u16 W83627EHF_REG_TEMP_HYST[] = { 0x153, 0x253 }; -static const u16 W83627EHF_REG_TEMP_OVER[] = { 0x155, 0x255 }; -static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 }; +static const u16 W83627EHF_REG_TEMP[] = { 0x27, 0x150, 0x250, 0x7e }; +static const u16 W83627EHF_REG_TEMP_HYST[] = { 0x3a, 0x153, 0x253, 0 }; +static const u16 W83627EHF_REG_TEMP_OVER[] = { 0x39, 0x155, 0x255, 0 }; +static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0, 0x152, 0x252, 0 }; /* Fan clock dividers are spread over the following five registers */ #define W83627EHF_REG_FANDIV1 0x47 @@ -173,10 +194,18 @@ static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 }; #define W83627EHF_REG_DIODE 0x59 #define W83627EHF_REG_SMI_OVT 0x4C +/* NCT6775F has its own fan divider registers */ +#define NCT6775_REG_FANDIV1 0x506 +#define NCT6775_REG_FANDIV2 0x507 +#define NCT6775_REG_FAN_DEBOUNCE 0xf0 + #define W83627EHF_REG_ALARM1 0x459 #define W83627EHF_REG_ALARM2 0x45A #define W83627EHF_REG_ALARM3 0x45B +#define W83627EHF_REG_CASEOPEN_DET 0x42 /* SMI STATUS #2 */ +#define W83627EHF_REG_CASEOPEN_CLR 0x46 /* SMI MASK #3 */ + /* SmartFan registers */ #define W83627EHF_REG_FAN_STEPUP_TIME 0x0f #define W83627EHF_REG_FAN_STEPDOWN_TIME 0x0e @@ -193,16 +222,125 @@ static const u8 W83627EHF_PWM_MODE_SHIFT[] = { 0, 1, 0, 6 }; static const u8 W83627EHF_PWM_ENABLE_SHIFT[] = { 2, 4, 1, 4 }; /* FAN Duty Cycle, be used to control */ -static const u8 W83627EHF_REG_PWM[] = { 0x01, 0x03, 0x11, 0x61 }; -static const u8 W83627EHF_REG_TARGET[] = { 0x05, 0x06, 0x13, 0x63 }; +static const u16 W83627EHF_REG_PWM[] = { 0x01, 0x03, 0x11, 0x61 }; +static const u16 W83627EHF_REG_TARGET[] = { 0x05, 0x06, 0x13, 0x63 }; static const u8 W83627EHF_REG_TOLERANCE[] = { 0x07, 0x07, 0x14, 0x62 }; /* Advanced Fan control, some values are common for all fans */ -static const u8 W83627EHF_REG_FAN_START_OUTPUT[] = { 0x0a, 0x0b, 0x16, 0x65 }; -static const u8 W83627EHF_REG_FAN_STOP_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 }; -static const u8 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0c, 0x0d, 0x17, 0x66 }; -static const u8 W83627EHF_REG_FAN_MAX_OUTPUT[] = { 0xff, 0x67, 0xff, 0x69 }; -static const u8 W83627EHF_REG_FAN_STEP_OUTPUT[] = { 0xff, 0x68, 0xff, 0x6a }; +static const u16 W83627EHF_REG_FAN_START_OUTPUT[] = { 0x0a, 0x0b, 0x16, 0x65 }; +static const u16 W83627EHF_REG_FAN_STOP_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 }; +static const u16 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0c, 0x0d, 0x17, 0x66 }; + +static const u16 W83627EHF_REG_FAN_MAX_OUTPUT_COMMON[] + = { 0xff, 0x67, 0xff, 0x69 }; +static const u16 W83627EHF_REG_FAN_STEP_OUTPUT_COMMON[] + = { 0xff, 0x68, 0xff, 0x6a }; + +static const u16 W83627EHF_REG_FAN_MAX_OUTPUT_W83667_B[] = { 0x67, 0x69, 0x6b }; +static const u16 W83627EHF_REG_FAN_STEP_OUTPUT_W83667_B[] + = { 0x68, 0x6a, 0x6c }; + +static const u16 W83627EHF_REG_TEMP_OFFSET[] = { 0x454, 0x455, 0x456 }; + +static const u16 NCT6775_REG_TARGET[] = { 0x101, 0x201, 0x301 }; +static const u16 NCT6775_REG_FAN_MODE[] = { 0x102, 0x202, 0x302 }; +static const u16 NCT6775_REG_FAN_STOP_OUTPUT[] = { 0x105, 0x205, 0x305 }; +static const u16 NCT6775_REG_FAN_START_OUTPUT[] = { 0x106, 0x206, 0x306 }; +static const u16 NCT6775_REG_FAN_STOP_TIME[] = { 0x107, 0x207, 0x307 }; +static const u16 NCT6775_REG_PWM[] = { 0x109, 0x209, 0x309 }; +static const u16 NCT6775_REG_FAN_MAX_OUTPUT[] = { 0x10a, 0x20a, 0x30a }; +static const u16 NCT6775_REG_FAN_STEP_OUTPUT[] = { 0x10b, 0x20b, 0x30b }; +static const u16 NCT6775_REG_FAN[] = { 0x630, 0x632, 0x634, 0x636, 0x638 }; +static const u16 NCT6776_REG_FAN_MIN[] = { 0x63a, 0x63c, 0x63e, 0x640, 0x642}; + +static const u16 NCT6775_REG_TEMP[] + = { 0x27, 0x150, 0x250, 0x73, 0x75, 0x77, 0x62b, 0x62c, 0x62d }; +static const u16 NCT6775_REG_TEMP_CONFIG[] + = { 0, 0x152, 0x252, 0, 0, 0, 0x628, 0x629, 0x62A }; +static const u16 NCT6775_REG_TEMP_HYST[] + = { 0x3a, 0x153, 0x253, 0, 0, 0, 0x673, 0x678, 0x67D }; +static const u16 NCT6775_REG_TEMP_OVER[] + = { 0x39, 0x155, 0x255, 0, 0, 0, 0x672, 0x677, 0x67C }; +static const u16 NCT6775_REG_TEMP_SOURCE[] + = { 0x621, 0x622, 0x623, 0x100, 0x200, 0x300, 0x624, 0x625, 0x626 }; + +static const char *const w83667hg_b_temp_label[] = { + "SYSTIN", + "CPUTIN", + "AUXTIN", + "AMDTSI", + "PECI Agent 1", + "PECI Agent 2", + "PECI Agent 3", + "PECI Agent 4" +}; + +static const char *const nct6775_temp_label[] = { + "", + "SYSTIN", + "CPUTIN", + "AUXTIN", + "AMD SB-TSI", + "PECI Agent 0", + "PECI Agent 1", + "PECI Agent 2", + "PECI Agent 3", + "PECI Agent 4", + "PECI Agent 5", + "PECI Agent 6", + "PECI Agent 7", + "PCH_CHIP_CPU_MAX_TEMP", + "PCH_CHIP_TEMP", + "PCH_CPU_TEMP", + "PCH_MCH_TEMP", + "PCH_DIM0_TEMP", + "PCH_DIM1_TEMP", + "PCH_DIM2_TEMP", + "PCH_DIM3_TEMP" +}; + +static const char *const nct6776_temp_label[] = { + "", + "SYSTIN", + "CPUTIN", + "AUXTIN", + "SMBUSMASTER 0", + "SMBUSMASTER 1", + "SMBUSMASTER 2", + "SMBUSMASTER 3", + "SMBUSMASTER 4", + "SMBUSMASTER 5", + "SMBUSMASTER 6", + "SMBUSMASTER 7", + "PECI Agent 0", + "PECI Agent 1", + "PCH_CHIP_CPU_MAX_TEMP", + "PCH_CHIP_TEMP", + "PCH_CPU_TEMP", + "PCH_MCH_TEMP", + "PCH_DIM0_TEMP", + "PCH_DIM1_TEMP", + "PCH_DIM2_TEMP", + "PCH_DIM3_TEMP", + "BYTE_TEMP" +}; + +#define NUM_REG_TEMP ARRAY_SIZE(NCT6775_REG_TEMP) + +static int is_word_sized(u16 reg) +{ + return ((((reg & 0xff00) == 0x100 + || (reg & 0xff00) == 0x200) + && ((reg & 0x00ff) == 0x50 + || (reg & 0x00ff) == 0x53 + || (reg & 0x00ff) == 0x55)) + || (reg & 0xfff0) == 0x630 + || reg == 0x640 || reg == 0x642 + || ((reg & 0xfff0) == 0x650 + && (reg & 0x000f) >= 0x06) + || reg == 0x73 || reg == 0x75 || reg == 0x77 + ); +} /* * Conversions @@ -216,54 +354,67 @@ static inline unsigned int step_time_from_reg(u8 reg, u8 mode) static inline u8 step_time_to_reg(unsigned int msec, u8 mode) { - return SENSORS_LIMIT((mode ? (msec + 50) / 100 : - (msec + 200) / 400), 1, 255); + return clamp_val((mode ? (msec + 50) / 100 : (msec + 200) / 400), + 1, 255); } -static inline unsigned int -fan_from_reg(u8 reg, unsigned int div) +static unsigned int fan_from_reg8(u16 reg, unsigned int divreg) { if (reg == 0 || reg == 255) return 0; - return 1350000U / (reg * div); + return 1350000U / (reg << divreg); } -static inline unsigned int -div_from_reg(u8 reg) +static unsigned int fan_from_reg13(u16 reg, unsigned int divreg) { - return 1 << reg; + if ((reg & 0xff1f) == 0xff1f) + return 0; + + reg = (reg & 0x1f) | ((reg & 0xff00) >> 3); + + if (reg == 0) + return 0; + + return 1350000U / reg; } -static inline int -temp1_from_reg(s8 reg) +static unsigned int fan_from_reg16(u16 reg, unsigned int divreg) { - return reg * 1000; + if (reg == 0 || reg == 0xffff) + return 0; + + /* + * Even though the registers are 16 bit wide, the fan divisor + * still applies. + */ + return 1350000U / (reg << divreg); } -static inline s8 -temp1_to_reg(long temp, int min, int max) +static inline unsigned int +div_from_reg(u8 reg) { - if (temp <= min) - return min / 1000; - if (temp >= max) - return max / 1000; - if (temp < 0) - return (temp - 500) / 1000; - return (temp + 500) / 1000; + return 1 << reg; } -/* Some of analog inputs have internal scaling (2x), 8mV is ADC LSB */ - -static u8 scale_in[10] = { 8, 8, 16, 16, 8, 8, 8, 16, 16, 8 }; +/* + * Some of the voltage inputs have internal scaling, the tables below + * contain 8 (the ADC LSB in mV) * scaling factor * 100 + */ +static const u16 scale_in_common[10] = { + 800, 800, 1600, 1600, 800, 800, 800, 1600, 1600, 800 +}; +static const u16 scale_in_w83627uhg[9] = { + 800, 800, 3328, 3424, 800, 800, 0, 3328, 3400 +}; -static inline long in_from_reg(u8 reg, u8 nr) +static inline long in_from_reg(u8 reg, u8 nr, const u16 *scale_in) { - return reg * scale_in[nr]; + return DIV_ROUND_CLOSEST(reg * scale_in[nr], 100); } -static inline u8 in_to_reg(u32 val, u8 nr) +static inline u8 in_to_reg(u32 val, u8 nr, const u16 *scale_in) { - return SENSORS_LIMIT(((val + (scale_in[nr] / 2)) / scale_in[nr]), 0, 255); + return clamp_val(DIV_ROUND_CLOSEST(val * 100, scale_in[nr]), 0, 255); } /* @@ -277,33 +428,61 @@ struct w83627ehf_data { struct device *hwmon_dev; struct mutex lock; + u16 reg_temp[NUM_REG_TEMP]; + u16 reg_temp_over[NUM_REG_TEMP]; + u16 reg_temp_hyst[NUM_REG_TEMP]; + u16 reg_temp_config[NUM_REG_TEMP]; + u8 temp_src[NUM_REG_TEMP]; + const char * const *temp_label; + + const u16 *REG_PWM; + const u16 *REG_TARGET; + const u16 *REG_FAN; + const u16 *REG_FAN_MIN; + const u16 *REG_FAN_START_OUTPUT; + const u16 *REG_FAN_STOP_OUTPUT; + const u16 *REG_FAN_STOP_TIME; + const u16 *REG_FAN_MAX_OUTPUT; + const u16 *REG_FAN_STEP_OUTPUT; + const u16 *scale_in; + + unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg); + unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg); + struct mutex update_lock; char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ /* Register values */ + u8 bank; /* current register bank */ u8 in_num; /* number of in inputs we have */ u8 in[10]; /* Register value */ u8 in_max[10]; /* Register value */ u8 in_min[10]; /* Register value */ - u8 fan[5]; - u8 fan_min[5]; + unsigned int rpm[5]; + u16 fan_min[5]; u8 fan_div[5]; u8 has_fan; /* some fan inputs can be disabled */ + u8 has_fan_min; /* some fans don't have min register */ + bool has_fan_div; u8 temp_type[3]; - s8 temp1; - s8 temp1_max; - s8 temp1_max_hyst; - s16 temp[2]; - s16 temp_max[2]; - s16 temp_max_hyst[2]; + s8 temp_offset[3]; + s16 temp[9]; + s16 temp_max[9]; + s16 temp_max_hyst[9]; u32 alarms; + u8 caseopen; u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */ u8 pwm_enable[4]; /* 1->manual - 2->thermal cruise mode (also called SmartFan I) - 3->fan speed cruise mode - 4->variable thermal cruise (also called SmartFan III) */ + * 2->thermal cruise mode (also called SmartFan I) + * 3->fan speed cruise mode + * 4->variable thermal cruise (also called + * SmartFan III) + * 5->enhanced variable thermal cruise (also called + * SmartFan IV) + */ + u8 pwm_enable_orig[4]; /* original value of pwm_enable */ u8 pwm_num; /* number of pwm */ u8 pwm[4]; u8 target_temp[4]; @@ -318,8 +497,17 @@ struct w83627ehf_data { u8 vid; u8 vrm; - u8 temp3_disable; - u8 in6_skip; + u16 have_temp; + u16 have_temp_offset; + u8 in6_skip:1; + u8 temp3_val_only:1; + +#ifdef CONFIG_PM + /* Remember extra register values over suspend/resume */ + u8 vbat; + u8 fandiv1; + u8 fandiv2; +#endif }; struct w83627ehf_sio_data { @@ -327,30 +515,19 @@ struct w83627ehf_sio_data { enum kinds kind; }; -static inline int is_word_sized(u16 reg) -{ - return (((reg & 0xff00) == 0x100 - || (reg & 0xff00) == 0x200) - && ((reg & 0x00ff) == 0x50 - || (reg & 0x00ff) == 0x53 - || (reg & 0x00ff) == 0x55)); -} - -/* Registers 0x50-0x5f are banked */ +/* + * On older chips, only registers 0x50-0x5f are banked. + * On more recent chips, all registers are banked. + * Assume that is the case and set the bank number for each access. + * Cache the bank number so it only needs to be set if it changes. + */ static inline void w83627ehf_set_bank(struct w83627ehf_data *data, u16 reg) { - if ((reg & 0x00f0) == 0x50) { - outb_p(W83627EHF_REG_BANK, data->addr + ADDR_REG_OFFSET); - outb_p(reg >> 8, data->addr + DATA_REG_OFFSET); - } -} - -/* Not strictly necessary, but play it safe for now */ -static inline void w83627ehf_reset_bank(struct w83627ehf_data *data, u16 reg) -{ - if (reg & 0xff00) { + u8 bank = reg >> 8; + if (data->bank != bank) { outb_p(W83627EHF_REG_BANK, data->addr + ADDR_REG_OFFSET); - outb_p(0, data->addr + DATA_REG_OFFSET); + outb_p(bank, data->addr + DATA_REG_OFFSET); + data->bank = bank; } } @@ -368,14 +545,13 @@ static u16 w83627ehf_read_value(struct w83627ehf_data *data, u16 reg) data->addr + ADDR_REG_OFFSET); res = (res << 8) + inb_p(data->addr + DATA_REG_OFFSET); } - w83627ehf_reset_bank(data, reg); mutex_unlock(&data->lock); - return res; } -static int w83627ehf_write_value(struct w83627ehf_data *data, u16 reg, u16 value) +static int w83627ehf_write_value(struct w83627ehf_data *data, u16 reg, + u16 value) { int word_sized = is_word_sized(reg); @@ -389,12 +565,60 @@ static int w83627ehf_write_value(struct w83627ehf_data *data, u16 reg, u16 value data->addr + ADDR_REG_OFFSET); } outb_p(value & 0xff, data->addr + DATA_REG_OFFSET); - w83627ehf_reset_bank(data, reg); mutex_unlock(&data->lock); return 0; } +/* We left-align 8-bit temperature values to make the code simpler */ +static u16 w83627ehf_read_temp(struct w83627ehf_data *data, u16 reg) +{ + u16 res; + + res = w83627ehf_read_value(data, reg); + if (!is_word_sized(reg)) + res <<= 8; + + return res; +} + +static int w83627ehf_write_temp(struct w83627ehf_data *data, u16 reg, + u16 value) +{ + if (!is_word_sized(reg)) + value >>= 8; + return w83627ehf_write_value(data, reg, value); +} + +/* This function assumes that the caller holds data->update_lock */ +static void nct6775_write_fan_div(struct w83627ehf_data *data, int nr) +{ + u8 reg; + + switch (nr) { + case 0: + reg = (w83627ehf_read_value(data, NCT6775_REG_FANDIV1) & 0x70) + | (data->fan_div[0] & 0x7); + w83627ehf_write_value(data, NCT6775_REG_FANDIV1, reg); + break; + case 1: + reg = (w83627ehf_read_value(data, NCT6775_REG_FANDIV1) & 0x7) + | ((data->fan_div[1] << 4) & 0x70); + w83627ehf_write_value(data, NCT6775_REG_FANDIV1, reg); + break; + case 2: + reg = (w83627ehf_read_value(data, NCT6775_REG_FANDIV2) & 0x70) + | (data->fan_div[2] & 0x7); + w83627ehf_write_value(data, NCT6775_REG_FANDIV2, reg); + break; + case 3: + reg = (w83627ehf_read_value(data, NCT6775_REG_FANDIV2) & 0x7) + | ((data->fan_div[3] << 4) & 0x70); + w83627ehf_write_value(data, NCT6775_REG_FANDIV2, reg); + break; + } +} + /* This function assumes that the caller holds data->update_lock */ static void w83627ehf_write_fan_div(struct w83627ehf_data *data, int nr) { @@ -446,6 +670,32 @@ static void w83627ehf_write_fan_div(struct w83627ehf_data *data, int nr) } } +static void w83627ehf_write_fan_div_common(struct device *dev, + struct w83627ehf_data *data, int nr) +{ + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); + + if (sio_data->kind == nct6776) + ; /* no dividers, do nothing */ + else if (sio_data->kind == nct6775) + nct6775_write_fan_div(data, nr); + else + w83627ehf_write_fan_div(data, nr); +} + +static void nct6775_update_fan_div(struct w83627ehf_data *data) +{ + u8 i; + + i = w83627ehf_read_value(data, NCT6775_REG_FANDIV1); + data->fan_div[0] = i & 0x7; + data->fan_div[1] = (i & 0x70) >> 4; + i = w83627ehf_read_value(data, NCT6775_REG_FANDIV2); + data->fan_div[2] = i & 0x7; + if (data->has_fan & (1<<3)) + data->fan_div[3] = (i & 0x70) >> 4; +} + static void w83627ehf_update_fan_div(struct w83627ehf_data *data) { int i; @@ -471,10 +721,79 @@ static void w83627ehf_update_fan_div(struct w83627ehf_data *data) } } +static void w83627ehf_update_fan_div_common(struct device *dev, + struct w83627ehf_data *data) +{ + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); + + if (sio_data->kind == nct6776) + ; /* no dividers, do nothing */ + else if (sio_data->kind == nct6775) + nct6775_update_fan_div(data); + else + w83627ehf_update_fan_div(data); +} + +static void nct6775_update_pwm(struct w83627ehf_data *data) +{ + int i; + int pwmcfg, fanmodecfg; + + for (i = 0; i < data->pwm_num; i++) { + pwmcfg = w83627ehf_read_value(data, + W83627EHF_REG_PWM_ENABLE[i]); + fanmodecfg = w83627ehf_read_value(data, + NCT6775_REG_FAN_MODE[i]); + data->pwm_mode[i] = + ((pwmcfg >> W83627EHF_PWM_MODE_SHIFT[i]) & 1) ? 0 : 1; + data->pwm_enable[i] = ((fanmodecfg >> 4) & 7) + 1; + data->tolerance[i] = fanmodecfg & 0x0f; + data->pwm[i] = w83627ehf_read_value(data, data->REG_PWM[i]); + } +} + +static void w83627ehf_update_pwm(struct w83627ehf_data *data) +{ + int i; + int pwmcfg = 0, tolerance = 0; /* shut up the compiler */ + + for (i = 0; i < data->pwm_num; i++) { + if (!(data->has_fan & (1 << i))) + continue; + + /* pwmcfg, tolerance mapped for i=0, i=1 to same reg */ + if (i != 1) { + pwmcfg = w83627ehf_read_value(data, + W83627EHF_REG_PWM_ENABLE[i]); + tolerance = w83627ehf_read_value(data, + W83627EHF_REG_TOLERANCE[i]); + } + data->pwm_mode[i] = + ((pwmcfg >> W83627EHF_PWM_MODE_SHIFT[i]) & 1) ? 0 : 1; + data->pwm_enable[i] = ((pwmcfg >> W83627EHF_PWM_ENABLE_SHIFT[i]) + & 3) + 1; + data->pwm[i] = w83627ehf_read_value(data, data->REG_PWM[i]); + + data->tolerance[i] = (tolerance >> (i == 1 ? 4 : 0)) & 0x0f; + } +} + +static void w83627ehf_update_pwm_common(struct device *dev, + struct w83627ehf_data *data) +{ + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); + + if (sio_data->kind == nct6775 || sio_data->kind == nct6776) + nct6775_update_pwm(data); + else + w83627ehf_update_pwm(data); +} + static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) { struct w83627ehf_data *data = dev_get_drvdata(dev); - int pwmcfg = 0, tolerance = 0; /* shut up the compiler */ + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); + int i; mutex_lock(&data->update_lock); @@ -482,10 +801,13 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) if (time_after(jiffies, data->last_updated + HZ + HZ/2) || !data->valid) { /* Fan clock dividers */ - w83627ehf_update_fan_div(data); + w83627ehf_update_fan_div_common(dev, data); /* Measured voltages and limits */ for (i = 0; i < data->in_num; i++) { + if ((i == 6) && data->in6_skip) + continue; + data->in[i] = w83627ehf_read_value(data, W83627EHF_REG_IN(i)); data->in_min[i] = w83627ehf_read_value(data, @@ -496,78 +818,98 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) /* Measured fan speeds and limits */ for (i = 0; i < 5; i++) { + u16 reg; + if (!(data->has_fan & (1 << i))) continue; - data->fan[i] = w83627ehf_read_value(data, - W83627EHF_REG_FAN[i]); - data->fan_min[i] = w83627ehf_read_value(data, - W83627EHF_REG_FAN_MIN[i]); - - /* If we failed to measure the fan speed and clock - divider can be increased, let's try that for next - time */ - if (data->fan[i] == 0xff - && data->fan_div[i] < 0x07) { - dev_dbg(dev, "Increasing fan%d " - "clock divider from %u to %u\n", + reg = w83627ehf_read_value(data, data->REG_FAN[i]); + data->rpm[i] = data->fan_from_reg(reg, + data->fan_div[i]); + + if (data->has_fan_min & (1 << i)) + data->fan_min[i] = w83627ehf_read_value(data, + data->REG_FAN_MIN[i]); + + /* + * If we failed to measure the fan speed and clock + * divider can be increased, let's try that for next + * time + */ + if (data->has_fan_div + && (reg >= 0xff || (sio_data->kind == nct6775 + && reg == 0x00)) + && data->fan_div[i] < 0x07) { + dev_dbg(dev, + "Increasing fan%d clock divider from %u to %u\n", i + 1, div_from_reg(data->fan_div[i]), div_from_reg(data->fan_div[i] + 1)); data->fan_div[i]++; - w83627ehf_write_fan_div(data, i); + w83627ehf_write_fan_div_common(dev, data, i); /* Preserve min limit if possible */ - if (data->fan_min[i] >= 2 + if ((data->has_fan_min & (1 << i)) + && data->fan_min[i] >= 2 && data->fan_min[i] != 255) w83627ehf_write_value(data, - W83627EHF_REG_FAN_MIN[i], + data->REG_FAN_MIN[i], (data->fan_min[i] /= 2)); } } - for (i = 0; i < 4; i++) { - /* pwmcfg, tolerance mapped for i=0, i=1 to same reg */ - if (i != 1) { - pwmcfg = w83627ehf_read_value(data, - W83627EHF_REG_PWM_ENABLE[i]); - tolerance = w83627ehf_read_value(data, - W83627EHF_REG_TOLERANCE[i]); - } - data->pwm_mode[i] = - ((pwmcfg >> W83627EHF_PWM_MODE_SHIFT[i]) & 1) - ? 0 : 1; - data->pwm_enable[i] = - ((pwmcfg >> W83627EHF_PWM_ENABLE_SHIFT[i]) - & 3) + 1; - data->pwm[i] = w83627ehf_read_value(data, - W83627EHF_REG_PWM[i]); - data->fan_start_output[i] = w83627ehf_read_value(data, - W83627EHF_REG_FAN_START_OUTPUT[i]); - data->fan_stop_output[i] = w83627ehf_read_value(data, - W83627EHF_REG_FAN_STOP_OUTPUT[i]); - data->fan_stop_time[i] = w83627ehf_read_value(data, - W83627EHF_REG_FAN_STOP_TIME[i]); + w83627ehf_update_pwm_common(dev, data); + + for (i = 0; i < data->pwm_num; i++) { + if (!(data->has_fan & (1 << i))) + continue; + + data->fan_start_output[i] = + w83627ehf_read_value(data, + data->REG_FAN_START_OUTPUT[i]); + data->fan_stop_output[i] = + w83627ehf_read_value(data, + data->REG_FAN_STOP_OUTPUT[i]); + data->fan_stop_time[i] = + w83627ehf_read_value(data, + data->REG_FAN_STOP_TIME[i]); + + if (data->REG_FAN_MAX_OUTPUT && + data->REG_FAN_MAX_OUTPUT[i] != 0xff) + data->fan_max_output[i] = + w83627ehf_read_value(data, + data->REG_FAN_MAX_OUTPUT[i]); + + if (data->REG_FAN_STEP_OUTPUT && + data->REG_FAN_STEP_OUTPUT[i] != 0xff) + data->fan_step_output[i] = + w83627ehf_read_value(data, + data->REG_FAN_STEP_OUTPUT[i]); + data->target_temp[i] = w83627ehf_read_value(data, - W83627EHF_REG_TARGET[i]) & + data->REG_TARGET[i]) & (data->pwm_mode[i] == 1 ? 0x7f : 0xff); - data->tolerance[i] = (tolerance >> (i == 1 ? 4 : 0)) - & 0x0f; } /* Measured temperatures and limits */ - data->temp1 = w83627ehf_read_value(data, - W83627EHF_REG_TEMP1); - data->temp1_max = w83627ehf_read_value(data, - W83627EHF_REG_TEMP1_OVER); - data->temp1_max_hyst = w83627ehf_read_value(data, - W83627EHF_REG_TEMP1_HYST); - for (i = 0; i < 2; i++) { - data->temp[i] = w83627ehf_read_value(data, - W83627EHF_REG_TEMP[i]); - data->temp_max[i] = w83627ehf_read_value(data, - W83627EHF_REG_TEMP_OVER[i]); - data->temp_max_hyst[i] = w83627ehf_read_value(data, - W83627EHF_REG_TEMP_HYST[i]); + for (i = 0; i < NUM_REG_TEMP; i++) { + if (!(data->have_temp & (1 << i))) + continue; + data->temp[i] = w83627ehf_read_temp(data, + data->reg_temp[i]); + if (data->reg_temp_over[i]) + data->temp_max[i] + = w83627ehf_read_temp(data, + data->reg_temp_over[i]); + if (data->reg_temp_hyst[i]) + data->temp_max_hyst[i] + = w83627ehf_read_temp(data, + data->reg_temp_hyst[i]); + if (i > 2) + continue; + if (data->have_temp_offset & (1 << i)) + data->temp_offset[i] + = w83627ehf_read_value(data, + W83627EHF_REG_TEMP_OFFSET[i]); } data->alarms = w83627ehf_read_value(data, @@ -577,6 +919,9 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) (w83627ehf_read_value(data, W83627EHF_REG_ALARM3) << 16); + data->caseopen = w83627ehf_read_value(data, + W83627EHF_REG_CASEOPEN_DET); + data->last_updated = jiffies; data->valid = 1; } @@ -594,9 +939,11 @@ show_##reg(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ struct w83627ehf_data *data = w83627ehf_update_device(dev); \ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + struct sensor_device_attribute *sensor_attr = \ + to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ - return sprintf(buf, "%ld\n", in_from_reg(data->reg[nr], nr)); \ + return sprintf(buf, "%ld\n", in_from_reg(data->reg[nr], nr, \ + data->scale_in)); \ } show_in_reg(in) show_in_reg(in_min) @@ -604,16 +951,20 @@ show_in_reg(in_max) #define store_in_reg(REG, reg) \ static ssize_t \ -store_in_##reg (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ +store_in_##reg(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ { \ struct w83627ehf_data *data = dev_get_drvdata(dev); \ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + struct sensor_device_attribute *sensor_attr = \ + to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ - u32 val = simple_strtoul(buf, NULL, 10); \ - \ + unsigned long val; \ + int err; \ + err = kstrtoul(buf, 10, &val); \ + if (err < 0) \ + return err; \ mutex_lock(&data->update_lock); \ - data->in_##reg[nr] = in_to_reg(val, nr); \ + data->in_##reg[nr] = in_to_reg(val, nr, data->scale_in); \ w83627ehf_write_value(data, W83627EHF_REG_IN_##REG(nr), \ data->in_##reg[nr]); \ mutex_unlock(&data->update_lock); \ @@ -623,7 +974,8 @@ store_in_##reg (struct device *dev, struct device_attribute *attr, \ store_in_reg(MIN, min) store_in_reg(MAX, max) -static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, + char *buf) { struct w83627ehf_data *data = w83627ehf_update_device(dev); struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); @@ -658,45 +1010,50 @@ static struct sensor_device_attribute sda_in_alarm[] = { }; static struct sensor_device_attribute sda_in_min[] = { - SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 0), - SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 1), - SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 2), - SENSOR_ATTR(in3_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 3), - SENSOR_ATTR(in4_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 4), - SENSOR_ATTR(in5_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 5), - SENSOR_ATTR(in6_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 6), - SENSOR_ATTR(in7_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 7), - SENSOR_ATTR(in8_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 8), - SENSOR_ATTR(in9_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 9), + SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 0), + SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 1), + SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 2), + SENSOR_ATTR(in3_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 3), + SENSOR_ATTR(in4_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 4), + SENSOR_ATTR(in5_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 5), + SENSOR_ATTR(in6_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 6), + SENSOR_ATTR(in7_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 7), + SENSOR_ATTR(in8_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 8), + SENSOR_ATTR(in9_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 9), }; static struct sensor_device_attribute sda_in_max[] = { - SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 0), - SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 1), - SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 2), - SENSOR_ATTR(in3_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 3), - SENSOR_ATTR(in4_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 4), - SENSOR_ATTR(in5_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 5), - SENSOR_ATTR(in6_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 6), - SENSOR_ATTR(in7_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 7), - SENSOR_ATTR(in8_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 8), - SENSOR_ATTR(in9_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 9), + SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 0), + SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 1), + SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 2), + SENSOR_ATTR(in3_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 3), + SENSOR_ATTR(in4_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 4), + SENSOR_ATTR(in5_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 5), + SENSOR_ATTR(in6_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 6), + SENSOR_ATTR(in7_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 7), + SENSOR_ATTR(in8_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 8), + SENSOR_ATTR(in9_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 9), }; -#define show_fan_reg(reg) \ -static ssize_t \ -show_##reg(struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - struct w83627ehf_data *data = w83627ehf_update_device(dev); \ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ - int nr = sensor_attr->index; \ - return sprintf(buf, "%d\n", \ - fan_from_reg(data->reg[nr], \ - div_from_reg(data->fan_div[nr]))); \ +static ssize_t +show_fan(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83627ehf_data *data = w83627ehf_update_device(dev); + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + return sprintf(buf, "%d\n", data->rpm[nr]); +} + +static ssize_t +show_fan_min(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83627ehf_data *data = w83627ehf_update_device(dev); + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + return sprintf(buf, "%d\n", + data->fan_from_reg_min(data->fan_min[nr], + data->fan_div[nr])); } -show_fan_reg(fan); -show_fan_reg(fan_min); static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr, @@ -715,34 +1072,63 @@ store_fan_min(struct device *dev, struct device_attribute *attr, struct w83627ehf_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; - unsigned int val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; unsigned int reg; u8 new_div; + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + mutex_lock(&data->update_lock); + if (!data->has_fan_div) { + /* + * Only NCT6776F for now, so we know that this is a 13 bit + * register + */ + if (!val) { + val = 0xff1f; + } else { + if (val > 1350000U) + val = 135000U; + val = 1350000U / val; + val = (val & 0x1f) | ((val << 3) & 0xff00); + } + data->fan_min[nr] = val; + goto done; /* Leave fan divider alone */ + } if (!val) { /* No min limit, alarm disabled */ data->fan_min[nr] = 255; new_div = data->fan_div[nr]; /* No change */ dev_info(dev, "fan%u low limit and alarm disabled\n", nr + 1); } else if ((reg = 1350000U / val) >= 128 * 255) { - /* Speed below this value cannot possibly be represented, - even with the highest divider (128) */ + /* + * Speed below this value cannot possibly be represented, + * even with the highest divider (128) + */ data->fan_min[nr] = 254; new_div = 7; /* 128 == (1 << 7) */ - dev_warn(dev, "fan%u low limit %u below minimum %u, set to " - "minimum\n", nr + 1, val, fan_from_reg(254, 128)); + dev_warn(dev, + "fan%u low limit %lu below minimum %u, set to minimum\n", + nr + 1, val, data->fan_from_reg_min(254, 7)); } else if (!reg) { - /* Speed above this value cannot possibly be represented, - even with the lowest divider (1) */ + /* + * Speed above this value cannot possibly be represented, + * even with the lowest divider (1) + */ data->fan_min[nr] = 1; new_div = 0; /* 1 == (1 << 0) */ - dev_warn(dev, "fan%u low limit %u above maximum %u, set to " - "maximum\n", nr + 1, val, fan_from_reg(1, 1)); + dev_warn(dev, + "fan%u low limit %lu above maximum %u, set to maximum\n", + nr + 1, val, data->fan_from_reg_min(1, 0)); } else { - /* Automatically pick the best divider, i.e. the one such - that the min limit will correspond to a register value - in the 96..192 range */ + /* + * Automatically pick the best divider, i.e. the one such + * that the min limit will correspond to a register value + * in the 96..192 range + */ new_div = 0; while (reg > 192 && new_div < 7) { reg >>= 1; @@ -751,28 +1137,21 @@ store_fan_min(struct device *dev, struct device_attribute *attr, data->fan_min[nr] = reg; } - /* Write both the fan clock divider (if it changed) and the new - fan min (unconditionally) */ + /* + * Write both the fan clock divider (if it changed) and the new + * fan min (unconditionally) + */ if (new_div != data->fan_div[nr]) { - /* Preserve the fan speed reading */ - if (data->fan[nr] != 0xff) { - if (new_div > data->fan_div[nr]) - data->fan[nr] >>= new_div - data->fan_div[nr]; - else if (data->fan[nr] & 0x80) - data->fan[nr] = 0xff; - else - data->fan[nr] <<= data->fan_div[nr] - new_div; - } - dev_dbg(dev, "fan%u clock divider changed from %u to %u\n", nr + 1, div_from_reg(data->fan_div[nr]), div_from_reg(new_div)); data->fan_div[nr] = new_div; - w83627ehf_write_fan_div(data, nr); + w83627ehf_write_fan_div_common(dev, data, nr); /* Give the chip time to sample a new speed value */ data->last_updated = jiffies; } - w83627ehf_write_value(data, W83627EHF_REG_FAN_MIN[nr], +done: + w83627ehf_write_value(data, data->REG_FAN_MIN[nr], data->fan_min[nr]); mutex_unlock(&data->update_lock); @@ -816,70 +1195,85 @@ static struct sensor_device_attribute sda_fan_div[] = { SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4), }; -#define show_temp1_reg(reg) \ -static ssize_t \ -show_##reg(struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - struct w83627ehf_data *data = w83627ehf_update_device(dev); \ - return sprintf(buf, "%d\n", temp1_from_reg(data->reg)); \ -} -show_temp1_reg(temp1); -show_temp1_reg(temp1_max); -show_temp1_reg(temp1_max_hyst); - -#define store_temp1_reg(REG, reg) \ -static ssize_t \ -store_temp1_##reg(struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - struct w83627ehf_data *data = dev_get_drvdata(dev); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - mutex_lock(&data->update_lock); \ - data->temp1_##reg = temp1_to_reg(val, -128000, 127000); \ - w83627ehf_write_value(data, W83627EHF_REG_TEMP1_##REG, \ - data->temp1_##reg); \ - mutex_unlock(&data->update_lock); \ - return count; \ +static ssize_t +show_temp_label(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83627ehf_data *data = w83627ehf_update_device(dev); + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]); } -store_temp1_reg(OVER, max); -store_temp1_reg(HYST, max_hyst); -#define show_temp_reg(reg) \ +#define show_temp_reg(addr, reg) \ static ssize_t \ show_##reg(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ struct w83627ehf_data *data = w83627ehf_update_device(dev); \ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + struct sensor_device_attribute *sensor_attr = \ + to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ - return sprintf(buf, "%d\n", \ - LM75_TEMP_FROM_REG(data->reg[nr])); \ + return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->reg[nr])); \ } -show_temp_reg(temp); -show_temp_reg(temp_max); -show_temp_reg(temp_max_hyst); +show_temp_reg(reg_temp, temp); +show_temp_reg(reg_temp_over, temp_max); +show_temp_reg(reg_temp_hyst, temp_max_hyst); -#define store_temp_reg(REG, reg) \ +#define store_temp_reg(addr, reg) \ static ssize_t \ store_##reg(struct device *dev, struct device_attribute *attr, \ const char *buf, size_t count) \ { \ struct w83627ehf_data *data = dev_get_drvdata(dev); \ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + struct sensor_device_attribute *sensor_attr = \ + to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ - long val = simple_strtol(buf, NULL, 10); \ - \ + int err; \ + long val; \ + err = kstrtol(buf, 10, &val); \ + if (err < 0) \ + return err; \ mutex_lock(&data->update_lock); \ data->reg[nr] = LM75_TEMP_TO_REG(val); \ - w83627ehf_write_value(data, W83627EHF_REG_TEMP_##REG[nr], \ - data->reg[nr]); \ + w83627ehf_write_temp(data, data->addr[nr], data->reg[nr]); \ mutex_unlock(&data->update_lock); \ return count; \ } -store_temp_reg(OVER, temp_max); -store_temp_reg(HYST, temp_max_hyst); +store_temp_reg(reg_temp_over, temp_max); +store_temp_reg(reg_temp_hyst, temp_max_hyst); + +static ssize_t +show_temp_offset(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83627ehf_data *data = w83627ehf_update_device(dev); + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + + return sprintf(buf, "%d\n", + data->temp_offset[sensor_attr->index] * 1000); +} + +static ssize_t +store_temp_offset(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct w83627ehf_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127); + + mutex_lock(&data->update_lock); + data->temp_offset[nr] = val; + w83627ehf_write_value(data, W83627EHF_REG_TEMP_OFFSET[nr], val); + mutex_unlock(&data->update_lock); + return count; +} static ssize_t show_temp_type(struct device *dev, struct device_attribute *attr, char *buf) @@ -891,27 +1285,69 @@ show_temp_type(struct device *dev, struct device_attribute *attr, char *buf) } static struct sensor_device_attribute sda_temp_input[] = { - SENSOR_ATTR(temp1_input, S_IRUGO, show_temp1, NULL, 0), - SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 0), - SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 1), + SENSOR_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0), + SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1), + SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2), + SENSOR_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3), + SENSOR_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4), + SENSOR_ATTR(temp6_input, S_IRUGO, show_temp, NULL, 5), + SENSOR_ATTR(temp7_input, S_IRUGO, show_temp, NULL, 6), + SENSOR_ATTR(temp8_input, S_IRUGO, show_temp, NULL, 7), + SENSOR_ATTR(temp9_input, S_IRUGO, show_temp, NULL, 8), +}; + +static struct sensor_device_attribute sda_temp_label[] = { + SENSOR_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL, 0), + SENSOR_ATTR(temp2_label, S_IRUGO, show_temp_label, NULL, 1), + SENSOR_ATTR(temp3_label, S_IRUGO, show_temp_label, NULL, 2), + SENSOR_ATTR(temp4_label, S_IRUGO, show_temp_label, NULL, 3), + SENSOR_ATTR(temp5_label, S_IRUGO, show_temp_label, NULL, 4), + SENSOR_ATTR(temp6_label, S_IRUGO, show_temp_label, NULL, 5), + SENSOR_ATTR(temp7_label, S_IRUGO, show_temp_label, NULL, 6), + SENSOR_ATTR(temp8_label, S_IRUGO, show_temp_label, NULL, 7), + SENSOR_ATTR(temp9_label, S_IRUGO, show_temp_label, NULL, 8), }; static struct sensor_device_attribute sda_temp_max[] = { - SENSOR_ATTR(temp1_max, S_IRUGO | S_IWUSR, show_temp1_max, - store_temp1_max, 0), - SENSOR_ATTR(temp2_max, S_IRUGO | S_IWUSR, show_temp_max, + SENSOR_ATTR(temp1_max, S_IRUGO | S_IWUSR, show_temp_max, store_temp_max, 0), - SENSOR_ATTR(temp3_max, S_IRUGO | S_IWUSR, show_temp_max, + SENSOR_ATTR(temp2_max, S_IRUGO | S_IWUSR, show_temp_max, store_temp_max, 1), + SENSOR_ATTR(temp3_max, S_IRUGO | S_IWUSR, show_temp_max, + store_temp_max, 2), + SENSOR_ATTR(temp4_max, S_IRUGO | S_IWUSR, show_temp_max, + store_temp_max, 3), + SENSOR_ATTR(temp5_max, S_IRUGO | S_IWUSR, show_temp_max, + store_temp_max, 4), + SENSOR_ATTR(temp6_max, S_IRUGO | S_IWUSR, show_temp_max, + store_temp_max, 5), + SENSOR_ATTR(temp7_max, S_IRUGO | S_IWUSR, show_temp_max, + store_temp_max, 6), + SENSOR_ATTR(temp8_max, S_IRUGO | S_IWUSR, show_temp_max, + store_temp_max, 7), + SENSOR_ATTR(temp9_max, S_IRUGO | S_IWUSR, show_temp_max, + store_temp_max, 8), }; static struct sensor_device_attribute sda_temp_max_hyst[] = { - SENSOR_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp1_max_hyst, - store_temp1_max_hyst, 0), - SENSOR_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, + SENSOR_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, store_temp_max_hyst, 0), - SENSOR_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, + SENSOR_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, store_temp_max_hyst, 1), + SENSOR_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 2), + SENSOR_ATTR(temp4_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 3), + SENSOR_ATTR(temp5_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 4), + SENSOR_ATTR(temp6_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 5), + SENSOR_ATTR(temp7_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 6), + SENSOR_ATTR(temp8_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 7), + SENSOR_ATTR(temp9_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 8), }; static struct sensor_device_attribute sda_temp_alarm[] = { @@ -926,12 +1362,22 @@ static struct sensor_device_attribute sda_temp_type[] = { SENSOR_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2), }; +static struct sensor_device_attribute sda_temp_offset[] = { + SENSOR_ATTR(temp1_offset, S_IRUGO | S_IWUSR, show_temp_offset, + store_temp_offset, 0), + SENSOR_ATTR(temp2_offset, S_IRUGO | S_IWUSR, show_temp_offset, + store_temp_offset, 1), + SENSOR_ATTR(temp3_offset, S_IRUGO | S_IWUSR, show_temp_offset, + store_temp_offset, 2), +}; + #define show_pwm_reg(reg) \ -static ssize_t show_##reg (struct device *dev, struct device_attribute *attr, \ - char *buf) \ +static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ + char *buf) \ { \ struct w83627ehf_data *data = w83627ehf_update_device(dev); \ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + struct sensor_device_attribute *sensor_attr = \ + to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ return sprintf(buf, "%d\n", data->reg[nr]); \ } @@ -946,12 +1392,23 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr, { struct w83627ehf_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); int nr = sensor_attr->index; - u32 val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; u16 reg; + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + if (val > 1) return -EINVAL; + + /* On NCT67766F, DC mode is only supported for pwm1 */ + if (sio_data->kind == nct6776 && nr && val != 1) + return -EINVAL; + mutex_lock(&data->update_lock); reg = w83627ehf_read_value(data, W83627EHF_REG_PWM_ENABLE[nr]); data->pwm_mode[nr] = val; @@ -970,11 +1427,18 @@ store_pwm(struct device *dev, struct device_attribute *attr, struct w83627ehf_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; - u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 255); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + val = clamp_val(val, 0, 255); mutex_lock(&data->update_lock); data->pwm[nr] = val; - w83627ehf_write_value(data, W83627EHF_REG_PWM[nr], val); + w83627ehf_write_value(data, data->REG_PWM[nr], val); mutex_unlock(&data->update_lock); return count; } @@ -984,19 +1448,38 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct w83627ehf_data *data = dev_get_drvdata(dev); + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; - u32 val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; u16 reg; - if (!val || (val > 4)) + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + if (!val || (val > 4 && val != data->pwm_enable_orig[nr])) return -EINVAL; + /* SmartFan III mode is not supported on NCT6776F */ + if (sio_data->kind == nct6776 && val == 4) + return -EINVAL; + mutex_lock(&data->update_lock); - reg = w83627ehf_read_value(data, W83627EHF_REG_PWM_ENABLE[nr]); data->pwm_enable[nr] = val; - reg &= ~(0x03 << W83627EHF_PWM_ENABLE_SHIFT[nr]); - reg |= (val - 1) << W83627EHF_PWM_ENABLE_SHIFT[nr]; - w83627ehf_write_value(data, W83627EHF_REG_PWM_ENABLE[nr], reg); + if (sio_data->kind == nct6775 || sio_data->kind == nct6776) { + reg = w83627ehf_read_value(data, + NCT6775_REG_FAN_MODE[nr]); + reg &= 0x0f; + reg |= (val - 1) << 4; + w83627ehf_write_value(data, + NCT6775_REG_FAN_MODE[nr], reg); + } else { + reg = w83627ehf_read_value(data, W83627EHF_REG_PWM_ENABLE[nr]); + reg &= ~(0x03 << W83627EHF_PWM_ENABLE_SHIFT[nr]); + reg |= (val - 1) << W83627EHF_PWM_ENABLE_SHIFT[nr]; + w83627ehf_write_value(data, W83627EHF_REG_PWM_ENABLE[nr], reg); + } mutex_unlock(&data->update_lock); return count; } @@ -1007,9 +1490,10 @@ static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ struct w83627ehf_data *data = w83627ehf_update_device(dev); \ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + struct sensor_device_attribute *sensor_attr = \ + to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ - return sprintf(buf, "%d\n", temp1_from_reg(data->reg[nr])); \ + return sprintf(buf, "%d\n", data->reg[nr] * 1000); \ } show_tol_temp(tolerance) @@ -1022,11 +1506,18 @@ store_target_temp(struct device *dev, struct device_attribute *attr, struct w83627ehf_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; - u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 127000); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 127); mutex_lock(&data->update_lock); data->target_temp[nr] = val; - w83627ehf_write_value(data, W83627EHF_REG_TARGET[nr], val); + w83627ehf_write_value(data, data->REG_TARGET[nr], val); mutex_unlock(&data->update_lock); return count; } @@ -1036,20 +1527,37 @@ store_tolerance(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct w83627ehf_data *data = dev_get_drvdata(dev); + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; u16 reg; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + /* Limit the temp to 0C - 15C */ - u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 15000); + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 15); mutex_lock(&data->update_lock); - reg = w83627ehf_read_value(data, W83627EHF_REG_TOLERANCE[nr]); - data->tolerance[nr] = val; - if (nr == 1) - reg = (reg & 0x0f) | (val << 4); - else + if (sio_data->kind == nct6775 || sio_data->kind == nct6776) { + /* Limit tolerance further for NCT6776F */ + if (sio_data->kind == nct6776 && val > 7) + val = 7; + reg = w83627ehf_read_value(data, NCT6775_REG_FAN_MODE[nr]); reg = (reg & 0xf0) | val; - w83627ehf_write_value(data, W83627EHF_REG_TOLERANCE[nr], reg); + w83627ehf_write_value(data, NCT6775_REG_FAN_MODE[nr], reg); + } else { + reg = w83627ehf_read_value(data, W83627EHF_REG_TOLERANCE[nr]); + if (nr == 1) + reg = (reg & 0x0f) | (val << 4); + else + reg = (reg & 0xf0) | val; + w83627ehf_write_value(data, W83627EHF_REG_TOLERANCE[nr], reg); + } + data->tolerance[nr] = val; mutex_unlock(&data->update_lock); return count; } @@ -1112,21 +1620,28 @@ static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ struct w83627ehf_data *data = w83627ehf_update_device(dev); \ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + struct sensor_device_attribute *sensor_attr = \ + to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ return sprintf(buf, "%d\n", data->reg[nr]); \ -}\ +} \ static ssize_t \ store_##reg(struct device *dev, struct device_attribute *attr, \ const char *buf, size_t count) \ -{\ +{ \ struct w83627ehf_data *data = dev_get_drvdata(dev); \ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + struct sensor_device_attribute *sensor_attr = \ + to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ - u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 1, 255); \ + unsigned long val; \ + int err; \ + err = kstrtoul(buf, 10, &val); \ + if (err < 0) \ + return err; \ + val = clamp_val(val, 1, 255); \ mutex_lock(&data->update_lock); \ data->reg[nr] = val; \ - w83627ehf_write_value(data, W83627EHF_REG_##REG[nr], val); \ + w83627ehf_write_value(data, data->REG_##REG[nr], val); \ mutex_unlock(&data->update_lock); \ return count; \ } @@ -1141,10 +1656,12 @@ static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ struct w83627ehf_data *data = w83627ehf_update_device(dev); \ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + struct sensor_device_attribute *sensor_attr = \ + to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ return sprintf(buf, "%d\n", \ - step_time_from_reg(data->reg[nr], data->pwm_mode[nr])); \ + step_time_from_reg(data->reg[nr], \ + data->pwm_mode[nr])); \ } \ \ static ssize_t \ @@ -1152,13 +1669,18 @@ store_##reg(struct device *dev, struct device_attribute *attr, \ const char *buf, size_t count) \ { \ struct w83627ehf_data *data = dev_get_drvdata(dev); \ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + struct sensor_device_attribute *sensor_attr = \ + to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ - u8 val = step_time_to_reg(simple_strtoul(buf, NULL, 10), \ - data->pwm_mode[nr]); \ + unsigned long val; \ + int err; \ + err = kstrtoul(buf, 10, &val); \ + if (err < 0) \ + return err; \ + val = step_time_to_reg(val, data->pwm_mode[nr]); \ mutex_lock(&data->update_lock); \ data->reg[nr] = val; \ - w83627ehf_write_value(data, W83627EHF_REG_##REG[nr], val); \ + w83627ehf_write_value(data, data->REG_##REG[nr], val); \ mutex_unlock(&data->update_lock); \ return count; \ } \ @@ -1187,31 +1709,48 @@ static struct sensor_device_attribute sda_sf3_arrays_fan4[] = { store_fan_step_output, 3), }; +static struct sensor_device_attribute sda_sf3_arrays_fan3[] = { + SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, + store_fan_stop_time, 2), + SENSOR_ATTR(pwm3_start_output, S_IWUSR | S_IRUGO, show_fan_start_output, + store_fan_start_output, 2), + SENSOR_ATTR(pwm3_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output, + store_fan_stop_output, 2), +}; + static struct sensor_device_attribute sda_sf3_arrays[] = { SENSOR_ATTR(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, store_fan_stop_time, 0), SENSOR_ATTR(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, store_fan_stop_time, 1), - SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, - store_fan_stop_time, 2), SENSOR_ATTR(pwm1_start_output, S_IWUSR | S_IRUGO, show_fan_start_output, store_fan_start_output, 0), SENSOR_ATTR(pwm2_start_output, S_IWUSR | S_IRUGO, show_fan_start_output, store_fan_start_output, 1), - SENSOR_ATTR(pwm3_start_output, S_IWUSR | S_IRUGO, show_fan_start_output, - store_fan_start_output, 2), SENSOR_ATTR(pwm1_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output, store_fan_stop_output, 0), SENSOR_ATTR(pwm2_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output, store_fan_stop_output, 1), - SENSOR_ATTR(pwm3_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output, - store_fan_stop_output, 2), +}; - /* pwm1 and pwm3 don't support max and step settings */ + +/* + * pwm1 and pwm3 don't support max and step settings on all chips. + * Need to check support while generating/removing attribute files. + */ +static struct sensor_device_attribute sda_sf3_max_step_arrays[] = { + SENSOR_ATTR(pwm1_max_output, S_IWUSR | S_IRUGO, show_fan_max_output, + store_fan_max_output, 0), + SENSOR_ATTR(pwm1_step_output, S_IWUSR | S_IRUGO, show_fan_step_output, + store_fan_step_output, 0), SENSOR_ATTR(pwm2_max_output, S_IWUSR | S_IRUGO, show_fan_max_output, store_fan_max_output, 1), SENSOR_ATTR(pwm2_step_output, S_IWUSR | S_IRUGO, show_fan_step_output, store_fan_step_output, 1), + SENSOR_ATTR(pwm3_max_output, S_IWUSR | S_IRUGO, show_fan_max_output, + store_fan_max_output, 2), + SENSOR_ATTR(pwm3_step_output, S_IWUSR | S_IRUGO, show_fan_step_output, + store_fan_step_output, 2), }; static ssize_t @@ -1222,19 +1761,72 @@ show_vid(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); + +/* Case open detection */ + +static ssize_t +show_caseopen(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83627ehf_data *data = w83627ehf_update_device(dev); + + return sprintf(buf, "%d\n", + !!(data->caseopen & to_sensor_dev_attr_2(attr)->index)); +} + +static ssize_t +clear_caseopen(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct w83627ehf_data *data = dev_get_drvdata(dev); + unsigned long val; + u16 reg, mask; + + if (kstrtoul(buf, 10, &val) || val != 0) + return -EINVAL; + + mask = to_sensor_dev_attr_2(attr)->nr; + + mutex_lock(&data->update_lock); + reg = w83627ehf_read_value(data, W83627EHF_REG_CASEOPEN_CLR); + w83627ehf_write_value(data, W83627EHF_REG_CASEOPEN_CLR, reg | mask); + w83627ehf_write_value(data, W83627EHF_REG_CASEOPEN_CLR, reg & ~mask); + data->valid = 0; /* Force cache refresh */ + mutex_unlock(&data->update_lock); + + return count; +} + +static struct sensor_device_attribute_2 sda_caseopen[] = { + SENSOR_ATTR_2(intrusion0_alarm, S_IWUSR | S_IRUGO, show_caseopen, + clear_caseopen, 0x80, 0x10), + SENSOR_ATTR_2(intrusion1_alarm, S_IWUSR | S_IRUGO, show_caseopen, + clear_caseopen, 0x40, 0x40), +}; + /* * Driver and device management */ static void w83627ehf_device_remove_files(struct device *dev) { - /* some entries in the following arrays may not have been used in - * device_create_file(), but device_remove_file() will ignore them */ + /* + * some entries in the following arrays may not have been used in + * device_create_file(), but device_remove_file() will ignore them + */ int i; struct w83627ehf_data *data = dev_get_drvdata(dev); for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++) device_remove_file(dev, &sda_sf3_arrays[i].dev_attr); + for (i = 0; i < ARRAY_SIZE(sda_sf3_max_step_arrays); i++) { + struct sensor_device_attribute *attr = + &sda_sf3_max_step_arrays[i]; + if (data->REG_FAN_STEP_OUTPUT && + data->REG_FAN_STEP_OUTPUT[attr->index] != 0xff) + device_remove_file(dev, &attr->dev_attr); + } + for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan3); i++) + device_remove_file(dev, &sda_sf3_arrays_fan3[i].dev_attr); for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr); for (i = 0; i < data->in_num; i++) { @@ -1258,22 +1850,32 @@ static void w83627ehf_device_remove_files(struct device *dev) device_remove_file(dev, &sda_target_temp[i].dev_attr); device_remove_file(dev, &sda_tolerance[i].dev_attr); } - for (i = 0; i < 3; i++) { - if ((i == 2) && data->temp3_disable) + for (i = 0; i < NUM_REG_TEMP; i++) { + if (!(data->have_temp & (1 << i))) continue; device_remove_file(dev, &sda_temp_input[i].dev_attr); + device_remove_file(dev, &sda_temp_label[i].dev_attr); + if (i == 2 && data->temp3_val_only) + continue; device_remove_file(dev, &sda_temp_max[i].dev_attr); device_remove_file(dev, &sda_temp_max_hyst[i].dev_attr); + if (i > 2) + continue; device_remove_file(dev, &sda_temp_alarm[i].dev_attr); device_remove_file(dev, &sda_temp_type[i].dev_attr); + device_remove_file(dev, &sda_temp_offset[i].dev_attr); } + device_remove_file(dev, &sda_caseopen[0].dev_attr); + device_remove_file(dev, &sda_caseopen[1].dev_attr); + device_remove_file(dev, &dev_attr_name); device_remove_file(dev, &dev_attr_cpu0_vid); } /* Get the monitoring functions started */ -static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data) +static inline void w83627ehf_init_device(struct w83627ehf_data *data, + enum kinds kind) { int i; u8 tmp, diode; @@ -1284,15 +1886,17 @@ static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data) w83627ehf_write_value(data, W83627EHF_REG_CONFIG, tmp | 0x01); - /* Enable temp2 and temp3 if needed */ - for (i = 0; i < 2; i++) { - tmp = w83627ehf_read_value(data, - W83627EHF_REG_TEMP_CONFIG[i]); - if ((i == 1) && data->temp3_disable) + /* Enable temperature sensors if needed */ + for (i = 0; i < NUM_REG_TEMP; i++) { + if (!(data->have_temp & (1 << i))) continue; + if (!data->reg_temp_config[i]) + continue; + tmp = w83627ehf_read_value(data, + data->reg_temp_config[i]); if (tmp & 0x01) w83627ehf_write_value(data, - W83627EHF_REG_TEMP_CONFIG[i], + data->reg_temp_config[i], tmp & 0xfe); } @@ -1302,22 +1906,169 @@ static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data) w83627ehf_write_value(data, W83627EHF_REG_VBAT, tmp | 0x01); /* Get thermal sensor types */ - diode = w83627ehf_read_value(data, W83627EHF_REG_DIODE); + switch (kind) { + case w83627ehf: + diode = w83627ehf_read_value(data, W83627EHF_REG_DIODE); + break; + case w83627uhg: + diode = 0x00; + break; + default: + diode = 0x70; + } for (i = 0; i < 3; i++) { - if ((tmp & (0x02 << i))) - data->temp_type[i] = (diode & (0x10 << i)) ? 1 : 2; + const char *label = NULL; + + if (data->temp_label) + label = data->temp_label[data->temp_src[i]]; + + /* Digital source overrides analog type */ + if (label && strncmp(label, "PECI", 4) == 0) + data->temp_type[i] = 6; + else if (label && strncmp(label, "AMD", 3) == 0) + data->temp_type[i] = 5; + else if ((tmp & (0x02 << i))) + data->temp_type[i] = (diode & (0x10 << i)) ? 1 : 3; else data->temp_type[i] = 4; /* thermistor */ } } -static int __devinit w83627ehf_probe(struct platform_device *pdev) +static void w82627ehf_swap_tempreg(struct w83627ehf_data *data, + int r1, int r2) +{ + u16 tmp; + + tmp = data->temp_src[r1]; + data->temp_src[r1] = data->temp_src[r2]; + data->temp_src[r2] = tmp; + + tmp = data->reg_temp[r1]; + data->reg_temp[r1] = data->reg_temp[r2]; + data->reg_temp[r2] = tmp; + + tmp = data->reg_temp_over[r1]; + data->reg_temp_over[r1] = data->reg_temp_over[r2]; + data->reg_temp_over[r2] = tmp; + + tmp = data->reg_temp_hyst[r1]; + data->reg_temp_hyst[r1] = data->reg_temp_hyst[r2]; + data->reg_temp_hyst[r2] = tmp; + + tmp = data->reg_temp_config[r1]; + data->reg_temp_config[r1] = data->reg_temp_config[r2]; + data->reg_temp_config[r2] = tmp; +} + +static void +w83627ehf_set_temp_reg_ehf(struct w83627ehf_data *data, int n_temp) +{ + int i; + + for (i = 0; i < n_temp; i++) { + data->reg_temp[i] = W83627EHF_REG_TEMP[i]; + data->reg_temp_over[i] = W83627EHF_REG_TEMP_OVER[i]; + data->reg_temp_hyst[i] = W83627EHF_REG_TEMP_HYST[i]; + data->reg_temp_config[i] = W83627EHF_REG_TEMP_CONFIG[i]; + } +} + +static void +w83627ehf_check_fan_inputs(const struct w83627ehf_sio_data *sio_data, + struct w83627ehf_data *data) +{ + int fan3pin, fan4pin, fan4min, fan5pin, regval; + + /* The W83627UHG is simple, only two fan inputs, no config */ + if (sio_data->kind == w83627uhg) { + data->has_fan = 0x03; /* fan1 and fan2 */ + data->has_fan_min = 0x03; + return; + } + + superio_enter(sio_data->sioreg); + + /* fan4 and fan5 share some pins with the GPIO and serial flash */ + if (sio_data->kind == nct6775) { + /* On NCT6775, fan4 shares pins with the fdc interface */ + fan3pin = 1; + fan4pin = !(superio_inb(sio_data->sioreg, 0x2A) & 0x80); + fan4min = 0; + fan5pin = 0; + } else if (sio_data->kind == nct6776) { + bool gpok = superio_inb(sio_data->sioreg, 0x27) & 0x80; + + superio_select(sio_data->sioreg, W83627EHF_LD_HWM); + regval = superio_inb(sio_data->sioreg, SIO_REG_ENABLE); + + if (regval & 0x80) + fan3pin = gpok; + else + fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40); + + if (regval & 0x40) + fan4pin = gpok; + else + fan4pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x01); + + if (regval & 0x20) + fan5pin = gpok; + else + fan5pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x02); + + fan4min = fan4pin; + } else if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) { + fan3pin = 1; + fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40; + fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20; + fan4min = fan4pin; + } else { + fan3pin = 1; + fan4pin = !(superio_inb(sio_data->sioreg, 0x29) & 0x06); + fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x02); + fan4min = fan4pin; + } + + superio_exit(sio_data->sioreg); + + data->has_fan = data->has_fan_min = 0x03; /* fan1 and fan2 */ + data->has_fan |= (fan3pin << 2); + data->has_fan_min |= (fan3pin << 2); + + if (sio_data->kind == nct6775 || sio_data->kind == nct6776) { + /* + * NCT6775F and NCT6776F don't have the W83627EHF_REG_FANDIV1 + * register + */ + data->has_fan |= (fan4pin << 3) | (fan5pin << 4); + data->has_fan_min |= (fan4min << 3) | (fan5pin << 4); + } else { + /* + * It looks like fan4 and fan5 pins can be alternatively used + * as fan on/off switches, but fan5 control is write only :/ + * We assume that if the serial interface is disabled, designers + * connected fan5 as input unless they are emitting log 1, which + * is not the default. + */ + regval = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1); + if ((regval & (1 << 2)) && fan4pin) { + data->has_fan |= (1 << 3); + data->has_fan_min |= (1 << 3); + } + if (!(regval & (1 << 1)) && fan5pin) { + data->has_fan |= (1 << 4); + data->has_fan_min |= (1 << 4); + } + } +} + +static int w83627ehf_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct w83627ehf_sio_data *sio_data = dev->platform_data; + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); struct w83627ehf_data *data; struct resource *res; - u8 fan4pin, fan5pin, en_vrm10; + u8 en_vrm10; int i, err = 0; res = platform_get_resource(pdev, IORESOURCE_IO, 0); @@ -1329,7 +2080,9 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) goto exit; } - if (!(data = kzalloc(sizeof(struct w83627ehf_data), GFP_KERNEL))) { + data = devm_kzalloc(&pdev->dev, sizeof(struct w83627ehf_data), + GFP_KERNEL); + if (!data) { err = -ENOMEM; goto exit_release; } @@ -1338,56 +2091,320 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) mutex_init(&data->lock); mutex_init(&data->update_lock); data->name = w83627ehf_device_names[sio_data->kind]; + data->bank = 0xff; /* Force initial bank selection */ platform_set_drvdata(pdev, data); /* 627EHG and 627EHF have 10 voltage inputs; 627DHG and 667HG have 9 */ data->in_num = (sio_data->kind == w83627ehf) ? 10 : 9; - /* 667HG has 3 pwms */ - data->pwm_num = (sio_data->kind == w83667hg) ? 3 : 4; - - /* Check temp3 configuration bit for 667HG */ - if (sio_data->kind == w83667hg) { - data->temp3_disable = w83627ehf_read_value(data, - W83627EHF_REG_TEMP_CONFIG[1]) & 0x01; - data->in6_skip = !data->temp3_disable; + /* 667HG, NCT6775F, and NCT6776F have 3 pwms, and 627UHG has only 2 */ + switch (sio_data->kind) { + default: + data->pwm_num = 4; + break; + case w83667hg: + case w83667hg_b: + case nct6775: + case nct6776: + data->pwm_num = 3; + break; + case w83627uhg: + data->pwm_num = 2; + break; } + /* Default to 3 temperature inputs, code below will adjust as needed */ + data->have_temp = 0x07; + + /* Deal with temperature register setup first. */ + if (sio_data->kind == nct6775 || sio_data->kind == nct6776) { + int mask = 0; + + /* + * Display temperature sensor output only if it monitors + * a source other than one already reported. Always display + * first three temperature registers, though. + */ + for (i = 0; i < NUM_REG_TEMP; i++) { + u8 src; + + data->reg_temp[i] = NCT6775_REG_TEMP[i]; + data->reg_temp_over[i] = NCT6775_REG_TEMP_OVER[i]; + data->reg_temp_hyst[i] = NCT6775_REG_TEMP_HYST[i]; + data->reg_temp_config[i] = NCT6775_REG_TEMP_CONFIG[i]; + + src = w83627ehf_read_value(data, + NCT6775_REG_TEMP_SOURCE[i]); + src &= 0x1f; + if (src && !(mask & (1 << src))) { + data->have_temp |= 1 << i; + mask |= 1 << src; + } + + data->temp_src[i] = src; + + /* + * Now do some register swapping if index 0..2 don't + * point to SYSTIN(1), CPUIN(2), and AUXIN(3). + * Idea is to have the first three attributes + * report SYSTIN, CPUIN, and AUXIN if possible + * without overriding the basic system configuration. + */ + if (i > 0 && data->temp_src[0] != 1 + && data->temp_src[i] == 1) + w82627ehf_swap_tempreg(data, 0, i); + if (i > 1 && data->temp_src[1] != 2 + && data->temp_src[i] == 2) + w82627ehf_swap_tempreg(data, 1, i); + if (i > 2 && data->temp_src[2] != 3 + && data->temp_src[i] == 3) + w82627ehf_swap_tempreg(data, 2, i); + } + if (sio_data->kind == nct6776) { + /* + * On NCT6776, AUXTIN and VIN3 pins are shared. + * Only way to detect it is to check if AUXTIN is used + * as a temperature source, and if that source is + * enabled. + * + * If that is the case, disable in6, which reports VIN3. + * Otherwise disable temp3. + */ + if (data->temp_src[2] == 3) { + u8 reg; + + if (data->reg_temp_config[2]) + reg = w83627ehf_read_value(data, + data->reg_temp_config[2]); + else + reg = 0; /* Assume AUXTIN is used */ + + if (reg & 0x01) + data->have_temp &= ~(1 << 2); + else + data->in6_skip = 1; + } + data->temp_label = nct6776_temp_label; + } else { + data->temp_label = nct6775_temp_label; + } + data->have_temp_offset = data->have_temp & 0x07; + for (i = 0; i < 3; i++) { + if (data->temp_src[i] > 3) + data->have_temp_offset &= ~(1 << i); + } + } else if (sio_data->kind == w83667hg_b) { + u8 reg; + + w83627ehf_set_temp_reg_ehf(data, 4); + + /* + * Temperature sources are selected with bank 0, registers 0x49 + * and 0x4a. + */ + reg = w83627ehf_read_value(data, 0x4a); + data->temp_src[0] = reg >> 5; + reg = w83627ehf_read_value(data, 0x49); + data->temp_src[1] = reg & 0x07; + data->temp_src[2] = (reg >> 4) & 0x07; + + /* + * W83667HG-B has another temperature register at 0x7e. + * The temperature source is selected with register 0x7d. + * Support it if the source differs from already reported + * sources. + */ + reg = w83627ehf_read_value(data, 0x7d); + reg &= 0x07; + if (reg != data->temp_src[0] && reg != data->temp_src[1] + && reg != data->temp_src[2]) { + data->temp_src[3] = reg; + data->have_temp |= 1 << 3; + } + + /* + * Chip supports either AUXTIN or VIN3. Try to find out which + * one. + */ + reg = w83627ehf_read_value(data, W83627EHF_REG_TEMP_CONFIG[2]); + if (data->temp_src[2] == 2 && (reg & 0x01)) + data->have_temp &= ~(1 << 2); + + if ((data->temp_src[2] == 2 && (data->have_temp & (1 << 2))) + || (data->temp_src[3] == 2 && (data->have_temp & (1 << 3)))) + data->in6_skip = 1; + + data->temp_label = w83667hg_b_temp_label; + data->have_temp_offset = data->have_temp & 0x07; + for (i = 0; i < 3; i++) { + if (data->temp_src[i] > 2) + data->have_temp_offset &= ~(1 << i); + } + } else if (sio_data->kind == w83627uhg) { + u8 reg; + + w83627ehf_set_temp_reg_ehf(data, 3); + + /* + * Temperature sources for temp2 and temp3 are selected with + * bank 0, registers 0x49 and 0x4a. + */ + data->temp_src[0] = 0; /* SYSTIN */ + reg = w83627ehf_read_value(data, 0x49) & 0x07; + /* Adjust to have the same mapping as other source registers */ + if (reg == 0) + data->temp_src[1] = 1; + else if (reg >= 2 && reg <= 5) + data->temp_src[1] = reg + 2; + else /* should never happen */ + data->have_temp &= ~(1 << 1); + reg = w83627ehf_read_value(data, 0x4a); + data->temp_src[2] = reg >> 5; + + /* + * Skip temp3 if source is invalid or the same as temp1 + * or temp2. + */ + if (data->temp_src[2] == 2 || data->temp_src[2] == 3 || + data->temp_src[2] == data->temp_src[0] || + ((data->have_temp & (1 << 1)) && + data->temp_src[2] == data->temp_src[1])) + data->have_temp &= ~(1 << 2); + else + data->temp3_val_only = 1; /* No limit regs */ + + data->in6_skip = 1; /* No VIN3 */ + + data->temp_label = w83667hg_b_temp_label; + data->have_temp_offset = data->have_temp & 0x03; + for (i = 0; i < 3; i++) { + if (data->temp_src[i] > 1) + data->have_temp_offset &= ~(1 << i); + } + } else { + w83627ehf_set_temp_reg_ehf(data, 3); + + /* Temperature sources are fixed */ + + if (sio_data->kind == w83667hg) { + u8 reg; + + /* + * Chip supports either AUXTIN or VIN3. Try to find + * out which one. + */ + reg = w83627ehf_read_value(data, + W83627EHF_REG_TEMP_CONFIG[2]); + if (reg & 0x01) + data->have_temp &= ~(1 << 2); + else + data->in6_skip = 1; + } + data->have_temp_offset = data->have_temp & 0x07; + } + + if (sio_data->kind == nct6775) { + data->has_fan_div = true; + data->fan_from_reg = fan_from_reg16; + data->fan_from_reg_min = fan_from_reg8; + data->REG_PWM = NCT6775_REG_PWM; + data->REG_TARGET = NCT6775_REG_TARGET; + data->REG_FAN = NCT6775_REG_FAN; + data->REG_FAN_MIN = W83627EHF_REG_FAN_MIN; + data->REG_FAN_START_OUTPUT = NCT6775_REG_FAN_START_OUTPUT; + data->REG_FAN_STOP_OUTPUT = NCT6775_REG_FAN_STOP_OUTPUT; + data->REG_FAN_STOP_TIME = NCT6775_REG_FAN_STOP_TIME; + data->REG_FAN_MAX_OUTPUT = NCT6775_REG_FAN_MAX_OUTPUT; + data->REG_FAN_STEP_OUTPUT = NCT6775_REG_FAN_STEP_OUTPUT; + } else if (sio_data->kind == nct6776) { + data->has_fan_div = false; + data->fan_from_reg = fan_from_reg13; + data->fan_from_reg_min = fan_from_reg13; + data->REG_PWM = NCT6775_REG_PWM; + data->REG_TARGET = NCT6775_REG_TARGET; + data->REG_FAN = NCT6775_REG_FAN; + data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; + data->REG_FAN_START_OUTPUT = NCT6775_REG_FAN_START_OUTPUT; + data->REG_FAN_STOP_OUTPUT = NCT6775_REG_FAN_STOP_OUTPUT; + data->REG_FAN_STOP_TIME = NCT6775_REG_FAN_STOP_TIME; + } else if (sio_data->kind == w83667hg_b) { + data->has_fan_div = true; + data->fan_from_reg = fan_from_reg8; + data->fan_from_reg_min = fan_from_reg8; + data->REG_PWM = W83627EHF_REG_PWM; + data->REG_TARGET = W83627EHF_REG_TARGET; + data->REG_FAN = W83627EHF_REG_FAN; + data->REG_FAN_MIN = W83627EHF_REG_FAN_MIN; + data->REG_FAN_START_OUTPUT = W83627EHF_REG_FAN_START_OUTPUT; + data->REG_FAN_STOP_OUTPUT = W83627EHF_REG_FAN_STOP_OUTPUT; + data->REG_FAN_STOP_TIME = W83627EHF_REG_FAN_STOP_TIME; + data->REG_FAN_MAX_OUTPUT = + W83627EHF_REG_FAN_MAX_OUTPUT_W83667_B; + data->REG_FAN_STEP_OUTPUT = + W83627EHF_REG_FAN_STEP_OUTPUT_W83667_B; + } else { + data->has_fan_div = true; + data->fan_from_reg = fan_from_reg8; + data->fan_from_reg_min = fan_from_reg8; + data->REG_PWM = W83627EHF_REG_PWM; + data->REG_TARGET = W83627EHF_REG_TARGET; + data->REG_FAN = W83627EHF_REG_FAN; + data->REG_FAN_MIN = W83627EHF_REG_FAN_MIN; + data->REG_FAN_START_OUTPUT = W83627EHF_REG_FAN_START_OUTPUT; + data->REG_FAN_STOP_OUTPUT = W83627EHF_REG_FAN_STOP_OUTPUT; + data->REG_FAN_STOP_TIME = W83627EHF_REG_FAN_STOP_TIME; + data->REG_FAN_MAX_OUTPUT = + W83627EHF_REG_FAN_MAX_OUTPUT_COMMON; + data->REG_FAN_STEP_OUTPUT = + W83627EHF_REG_FAN_STEP_OUTPUT_COMMON; + } + + /* Setup input voltage scaling factors */ + if (sio_data->kind == w83627uhg) + data->scale_in = scale_in_w83627uhg; + else + data->scale_in = scale_in_common; + /* Initialize the chip */ - w83627ehf_init_device(data); + w83627ehf_init_device(data, sio_data->kind); data->vrm = vid_which_vrm(); superio_enter(sio_data->sioreg); /* Read VID value */ - if (sio_data->kind == w83667hg) { - /* W83667HG has different pins for VID input and output, so - we can get the VID input values directly at logical device D - 0xe3. */ + if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b || + sio_data->kind == nct6775 || sio_data->kind == nct6776) { + /* + * W83667HG has different pins for VID input and output, so + * we can get the VID input values directly at logical device D + * 0xe3. + */ superio_select(sio_data->sioreg, W83667HG_LD_VID); data->vid = superio_inb(sio_data->sioreg, 0xe3); err = device_create_file(dev, &dev_attr_cpu0_vid); if (err) goto exit_release; - } else { + } else if (sio_data->kind != w83627uhg) { superio_select(sio_data->sioreg, W83627EHF_LD_HWM); if (superio_inb(sio_data->sioreg, SIO_REG_VID_CTRL) & 0x80) { - /* Set VID input sensibility if needed. In theory the - BIOS should have set it, but in practice it's not - always the case. We only do it for the W83627EHF/EHG - because the W83627DHG is more complex in this - respect. */ + /* + * Set VID input sensibility if needed. In theory the + * BIOS should have set it, but in practice it's not + * always the case. We only do it for the W83627EHF/EHG + * because the W83627DHG is more complex in this + * respect. + */ if (sio_data->kind == w83627ehf) { en_vrm10 = superio_inb(sio_data->sioreg, SIO_REG_EN_VRM10); if ((en_vrm10 & 0x08) && data->vrm == 90) { - dev_warn(dev, "Setting VID input " - "voltage to TTL\n"); + dev_warn(dev, + "Setting VID input voltage to TTL\n"); superio_outb(sio_data->sioreg, SIO_REG_EN_VRM10, en_vrm10 & ~0x08); } else if (!(en_vrm10 & 0x08) && data->vrm == 100) { - dev_warn(dev, "Setting VID input " - "voltage to VRM10\n"); + dev_warn(dev, + "Setting VID input voltage to VRM10\n"); superio_outb(sio_data->sioreg, SIO_REG_EN_VRM10, en_vrm10 | 0x08); @@ -1403,48 +2420,68 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) if (err) goto exit_release; } else { - dev_info(dev, "VID pins in output mode, CPU VID not " - "available\n"); + dev_info(dev, + "VID pins in output mode, CPU VID not available\n"); } } - /* fan4 and fan5 share some pins with the GPIO and serial flash */ - if (sio_data->kind == w83667hg) { - fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20; - fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40; - } else { - fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x02); - fan4pin = !(superio_inb(sio_data->sioreg, 0x29) & 0x06); + if (fan_debounce && + (sio_data->kind == nct6775 || sio_data->kind == nct6776)) { + u8 tmp; + + superio_select(sio_data->sioreg, W83627EHF_LD_HWM); + tmp = superio_inb(sio_data->sioreg, NCT6775_REG_FAN_DEBOUNCE); + if (sio_data->kind == nct6776) + superio_outb(sio_data->sioreg, NCT6775_REG_FAN_DEBOUNCE, + 0x3e | tmp); + else + superio_outb(sio_data->sioreg, NCT6775_REG_FAN_DEBOUNCE, + 0x1e | tmp); + pr_info("Enabled fan debounce for chip %s\n", data->name); } + superio_exit(sio_data->sioreg); - /* It looks like fan4 and fan5 pins can be alternatively used - as fan on/off switches, but fan5 control is write only :/ - We assume that if the serial interface is disabled, designers - connected fan5 as input unless they are emitting log 1, which - is not the default. */ - - data->has_fan = 0x07; /* fan1, fan2 and fan3 */ - i = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1); - if ((i & (1 << 2)) && fan4pin) - data->has_fan |= (1 << 3); - if (!(i & (1 << 1)) && fan5pin) - data->has_fan |= (1 << 4); + w83627ehf_check_fan_inputs(sio_data, data); /* Read fan clock dividers immediately */ - w83627ehf_update_fan_div(data); + w83627ehf_update_fan_div_common(dev, data); + + /* Read pwm data to save original values */ + w83627ehf_update_pwm_common(dev, data); + for (i = 0; i < data->pwm_num; i++) + data->pwm_enable_orig[i] = data->pwm_enable[i]; /* Register sysfs hooks */ - for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++) - if ((err = device_create_file(dev, - &sda_sf3_arrays[i].dev_attr))) + for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++) { + err = device_create_file(dev, &sda_sf3_arrays[i].dev_attr); + if (err) goto exit_remove; + } - /* if fan4 is enabled create the sf3 files for it */ + for (i = 0; i < ARRAY_SIZE(sda_sf3_max_step_arrays); i++) { + struct sensor_device_attribute *attr = + &sda_sf3_max_step_arrays[i]; + if (data->REG_FAN_STEP_OUTPUT && + data->REG_FAN_STEP_OUTPUT[attr->index] != 0xff) { + err = device_create_file(dev, &attr->dev_attr); + if (err) + goto exit_remove; + } + } + /* if fan3 and fan4 are enabled create the sf3 files for them */ + if ((data->has_fan & (1 << 2)) && data->pwm_num >= 3) + for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan3); i++) { + err = device_create_file(dev, + &sda_sf3_arrays_fan3[i].dev_attr); + if (err) + goto exit_remove; + } if ((data->has_fan & (1 << 3)) && data->pwm_num >= 4) for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) { - if ((err = device_create_file(dev, - &sda_sf3_arrays_fan4[i].dev_attr))) + err = device_create_file(dev, + &sda_sf3_arrays_fan4[i].dev_attr); + if (err) goto exit_remove; } @@ -1466,12 +2503,20 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) if ((err = device_create_file(dev, &sda_fan_input[i].dev_attr)) || (err = device_create_file(dev, - &sda_fan_alarm[i].dev_attr)) - || (err = device_create_file(dev, - &sda_fan_div[i].dev_attr)) - || (err = device_create_file(dev, - &sda_fan_min[i].dev_attr))) + &sda_fan_alarm[i].dev_attr))) goto exit_remove; + if (sio_data->kind != nct6776) { + err = device_create_file(dev, + &sda_fan_div[i].dev_attr); + if (err) + goto exit_remove; + } + if (data->has_fan_min & (1 << i)) { + err = device_create_file(dev, + &sda_fan_min[i].dev_attr); + if (err) + goto exit_remove; + } if (i < data->pwm_num && ((err = device_create_file(dev, &sda_pwm[i].dev_attr)) @@ -1487,20 +2532,55 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) } } - for (i = 0; i < 3; i++) { - if ((i == 2) && data->temp3_disable) + for (i = 0; i < NUM_REG_TEMP; i++) { + if (!(data->have_temp & (1 << i))) + continue; + err = device_create_file(dev, &sda_temp_input[i].dev_attr); + if (err) + goto exit_remove; + if (data->temp_label) { + err = device_create_file(dev, + &sda_temp_label[i].dev_attr); + if (err) + goto exit_remove; + } + if (i == 2 && data->temp3_val_only) + continue; + if (data->reg_temp_over[i]) { + err = device_create_file(dev, + &sda_temp_max[i].dev_attr); + if (err) + goto exit_remove; + } + if (data->reg_temp_hyst[i]) { + err = device_create_file(dev, + &sda_temp_max_hyst[i].dev_attr); + if (err) + goto exit_remove; + } + if (i > 2) continue; if ((err = device_create_file(dev, - &sda_temp_input[i].dev_attr)) - || (err = device_create_file(dev, - &sda_temp_max[i].dev_attr)) - || (err = device_create_file(dev, - &sda_temp_max_hyst[i].dev_attr)) - || (err = device_create_file(dev, &sda_temp_alarm[i].dev_attr)) || (err = device_create_file(dev, &sda_temp_type[i].dev_attr))) goto exit_remove; + if (data->have_temp_offset & (1 << i)) { + err = device_create_file(dev, + &sda_temp_offset[i].dev_attr); + if (err) + goto exit_remove; + } + } + + err = device_create_file(dev, &sda_caseopen[0].dev_attr); + if (err) + goto exit_remove; + + if (sio_data->kind == nct6776) { + err = device_create_file(dev, &sda_caseopen[1].dev_attr); + if (err) + goto exit_remove; } err = device_create_file(dev, &dev_attr_name); @@ -1517,45 +2597,135 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) exit_remove: w83627ehf_device_remove_files(dev); - kfree(data); - platform_set_drvdata(pdev, NULL); exit_release: release_region(res->start, IOREGION_LENGTH); exit: return err; } -static int __devexit w83627ehf_remove(struct platform_device *pdev) +static int w83627ehf_remove(struct platform_device *pdev) { struct w83627ehf_data *data = platform_get_drvdata(pdev); hwmon_device_unregister(data->hwmon_dev); w83627ehf_device_remove_files(&pdev->dev); release_region(data->addr, IOREGION_LENGTH); - platform_set_drvdata(pdev, NULL); - kfree(data); return 0; } +#ifdef CONFIG_PM +static int w83627ehf_suspend(struct device *dev) +{ + struct w83627ehf_data *data = w83627ehf_update_device(dev); + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); + + mutex_lock(&data->update_lock); + data->vbat = w83627ehf_read_value(data, W83627EHF_REG_VBAT); + if (sio_data->kind == nct6775) { + data->fandiv1 = w83627ehf_read_value(data, NCT6775_REG_FANDIV1); + data->fandiv2 = w83627ehf_read_value(data, NCT6775_REG_FANDIV2); + } + mutex_unlock(&data->update_lock); + + return 0; +} + +static int w83627ehf_resume(struct device *dev) +{ + struct w83627ehf_data *data = dev_get_drvdata(dev); + struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); + int i; + + mutex_lock(&data->update_lock); + data->bank = 0xff; /* Force initial bank selection */ + + /* Restore limits */ + for (i = 0; i < data->in_num; i++) { + if ((i == 6) && data->in6_skip) + continue; + + w83627ehf_write_value(data, W83627EHF_REG_IN_MIN(i), + data->in_min[i]); + w83627ehf_write_value(data, W83627EHF_REG_IN_MAX(i), + data->in_max[i]); + } + + for (i = 0; i < 5; i++) { + if (!(data->has_fan_min & (1 << i))) + continue; + + w83627ehf_write_value(data, data->REG_FAN_MIN[i], + data->fan_min[i]); + } + + for (i = 0; i < NUM_REG_TEMP; i++) { + if (!(data->have_temp & (1 << i))) + continue; + + if (data->reg_temp_over[i]) + w83627ehf_write_temp(data, data->reg_temp_over[i], + data->temp_max[i]); + if (data->reg_temp_hyst[i]) + w83627ehf_write_temp(data, data->reg_temp_hyst[i], + data->temp_max_hyst[i]); + if (i > 2) + continue; + if (data->have_temp_offset & (1 << i)) + w83627ehf_write_value(data, + W83627EHF_REG_TEMP_OFFSET[i], + data->temp_offset[i]); + } + + /* Restore other settings */ + w83627ehf_write_value(data, W83627EHF_REG_VBAT, data->vbat); + if (sio_data->kind == nct6775) { + w83627ehf_write_value(data, NCT6775_REG_FANDIV1, data->fandiv1); + w83627ehf_write_value(data, NCT6775_REG_FANDIV2, data->fandiv2); + } + + /* Force re-reading all values */ + data->valid = 0; + mutex_unlock(&data->update_lock); + + return 0; +} + +static const struct dev_pm_ops w83627ehf_dev_pm_ops = { + .suspend = w83627ehf_suspend, + .resume = w83627ehf_resume, + .freeze = w83627ehf_suspend, + .restore = w83627ehf_resume, +}; + +#define W83627EHF_DEV_PM_OPS (&w83627ehf_dev_pm_ops) +#else +#define W83627EHF_DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + static struct platform_driver w83627ehf_driver = { .driver = { .owner = THIS_MODULE, .name = DRVNAME, + .pm = W83627EHF_DEV_PM_OPS, }, .probe = w83627ehf_probe, - .remove = __devexit_p(w83627ehf_remove), + .remove = w83627ehf_remove, }; /* w83627ehf_find() looks for a '627 in the Super-I/O config space */ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, struct w83627ehf_sio_data *sio_data) { - static const char __initdata sio_name_W83627EHF[] = "W83627EHF"; - static const char __initdata sio_name_W83627EHG[] = "W83627EHG"; - static const char __initdata sio_name_W83627DHG[] = "W83627DHG"; - static const char __initdata sio_name_W83627DHG_P[] = "W83627DHG-P"; - static const char __initdata sio_name_W83667HG[] = "W83667HG"; + static const char sio_name_W83627EHF[] __initconst = "W83627EHF"; + static const char sio_name_W83627EHG[] __initconst = "W83627EHG"; + static const char sio_name_W83627DHG[] __initconst = "W83627DHG"; + static const char sio_name_W83627DHG_P[] __initconst = "W83627DHG-P"; + static const char sio_name_W83627UHG[] __initconst = "W83627UHG"; + static const char sio_name_W83667HG[] __initconst = "W83667HG"; + static const char sio_name_W83667HG_B[] __initconst = "W83667HG-B"; + static const char sio_name_NCT6775[] __initconst = "NCT6775F"; + static const char sio_name_NCT6776[] __initconst = "NCT6776F"; u16 val; const char *sio_name; @@ -1584,14 +2754,29 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, sio_data->kind = w83627dhg_p; sio_name = sio_name_W83627DHG_P; break; + case SIO_W83627UHG_ID: + sio_data->kind = w83627uhg; + sio_name = sio_name_W83627UHG; + break; case SIO_W83667HG_ID: sio_data->kind = w83667hg; sio_name = sio_name_W83667HG; break; + case SIO_W83667HG_B_ID: + sio_data->kind = w83667hg_b; + sio_name = sio_name_W83667HG_B; + break; + case SIO_NCT6775_ID: + sio_data->kind = nct6775; + sio_name = sio_name_NCT6775; + break; + case SIO_NCT6776_ID: + sio_data->kind = nct6776; + sio_name = sio_name_NCT6776; + break; default: if (val != 0xffff) - pr_debug(DRVNAME ": unsupported chip ID: 0x%04x\n", - val); + pr_debug("unsupported chip ID: 0x%04x\n", val); superio_exit(sioaddr); return -ENODEV; } @@ -1602,8 +2787,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, | superio_inb(sioaddr, SIO_REG_ADDR + 1); *addr = val & IOREGION_ALIGNMENT; if (*addr == 0) { - printk(KERN_ERR DRVNAME ": Refusing to enable a Super-I/O " - "device with a base I/O port 0.\n"); + pr_err("Refusing to enable a Super-I/O device with a base I/O port 0\n"); superio_exit(sioaddr); return -ENODEV; } @@ -1611,22 +2795,23 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, /* Activate logical device if needed */ val = superio_inb(sioaddr, SIO_REG_ENABLE); if (!(val & 0x01)) { - printk(KERN_WARNING DRVNAME ": Forcibly enabling Super-I/O. " - "Sensor is probably unusable.\n"); + pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n"); superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01); } superio_exit(sioaddr); - pr_info(DRVNAME ": Found %s chip at %#x\n", sio_name, *addr); + pr_info("Found %s chip at %#x\n", sio_name, *addr); sio_data->sioreg = sioaddr; return 0; } -/* when Super-I/O functions move to a separate file, the Super-I/O +/* + * when Super-I/O functions move to a separate file, the Super-I/O * bus will manage the lifetime of the device and this module will only keep * track of the w83627ehf driver. But since we platform_device_alloc(), we - * must keep track of the device */ + * must keep track of the device + */ static struct platform_device *pdev; static int __init sensors_w83627ehf_init(void) @@ -1636,11 +2821,13 @@ static int __init sensors_w83627ehf_init(void) struct resource res; struct w83627ehf_sio_data sio_data; - /* initialize sio_data->kind and sio_data->sioreg. + /* + * initialize sio_data->kind and sio_data->sioreg. * * when Super-I/O functions move to a separate file, the Super-I/O * driver will probe 0x2e and 0x4e and auto-detect the presence of a - * w83627ehf hardware monitor, and call probe() */ + * w83627ehf hardware monitor, and call probe() + */ if (w83627ehf_find(0x2e, &address, &sio_data) && w83627ehf_find(0x4e, &address, &sio_data)) return -ENODEV; @@ -1649,16 +2836,17 @@ static int __init sensors_w83627ehf_init(void) if (err) goto exit; - if (!(pdev = platform_device_alloc(DRVNAME, address))) { + pdev = platform_device_alloc(DRVNAME, address); + if (!pdev) { err = -ENOMEM; - printk(KERN_ERR DRVNAME ": Device allocation failed\n"); + pr_err("Device allocation failed\n"); goto exit_unregister; } err = platform_device_add_data(pdev, &sio_data, sizeof(struct w83627ehf_sio_data)); if (err) { - printk(KERN_ERR DRVNAME ": Platform data allocation failed\n"); + pr_err("Platform data allocation failed\n"); goto exit_device_put; } @@ -1674,16 +2862,14 @@ static int __init sensors_w83627ehf_init(void) err = platform_device_add_resources(pdev, &res, 1); if (err) { - printk(KERN_ERR DRVNAME ": Device resource addition failed " - "(%d)\n", err); + pr_err("Device resource addition failed (%d)\n", err); goto exit_device_put; } /* platform_device_add calls probe() */ err = platform_device_add(pdev); if (err) { - printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", - err); + pr_err("Device addition failed (%d)\n", err); goto exit_device_put; } @@ -1703,7 +2889,7 @@ static void __exit sensors_w83627ehf_exit(void) platform_driver_unregister(&w83627ehf_driver); } -MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); +MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); MODULE_DESCRIPTION("W83627EHF driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index 38e28052307..c1726be3654 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -1,43 +1,45 @@ /* - w83627hf.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (c) 1998 - 2003 Frodo Looijaard <frodol@dds.nl>, - Philip Edelbrock <phil@netroedge.com>, - and Mark Studebaker <mdsxyz123@yahoo.com> - Ported to 2.6 by Bernhard C. Schrenk <clemy@clemy.org> - Copyright (c) 2007 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; 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. -*/ + * w83627hf.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (c) 1998 - 2003 Frodo Looijaard <frodol@dds.nl>, + * Philip Edelbrock <phil@netroedge.com>, + * and Mark Studebaker <mdsxyz123@yahoo.com> + * Ported to 2.6 by Bernhard C. Schrenk <clemy@clemy.org> + * Copyright (c) 2007 - 1012 Jean Delvare <jdelvare@suse.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ /* - Supports following chips: - - Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA - w83627hf 9 3 2 3 0x20 0x5ca3 no yes(LPC) - w83627thf 7 3 3 3 0x90 0x5ca3 no yes(LPC) - w83637hf 7 3 3 3 0x80 0x5ca3 no yes(LPC) - w83687thf 7 3 3 3 0x90 0x5ca3 no yes(LPC) - w83697hf 8 2 2 2 0x60 0x5ca3 no yes(LPC) - - For other winbond chips, and for i2c support in the above chips, - use w83781d.c. - - Note: automatic ("cruise") fan control for 697, 637 & 627thf not - supported yet. -*/ + * Supports following chips: + * + * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA + * w83627hf 9 3 2 3 0x20 0x5ca3 no yes(LPC) + * w83627thf 7 3 3 3 0x90 0x5ca3 no yes(LPC) + * w83637hf 7 3 3 3 0x80 0x5ca3 no yes(LPC) + * w83687thf 7 3 3 3 0x90 0x5ca3 no yes(LPC) + * w83697hf 8 2 2 2 0x60 0x5ca3 no yes(LPC) + * + * For other winbond chips, and for i2c support in the above chips, + * use w83781d.c. + * + * Note: automatic ("cruise") fan control for 697, 637 & 627thf not + * supported yet. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/init.h> @@ -69,7 +71,7 @@ module_param(force_i2c, byte, 0); MODULE_PARM_DESC(force_i2c, "Initialize the i2c address of the sensors"); -static int init = 1; +static bool init = 1; module_param(init, bool, 0); MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization"); @@ -78,7 +80,7 @@ module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); /* modified from kernel/include/traps.c */ -#define DEV 0x07 /* Register: Logical device select */ +#define DEV 0x07 /* Register: Logical device select */ /* logical device numbers for superio_select (below) */ #define W83627HF_LD_FDC 0x00 @@ -97,7 +99,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID"); #define W83627HF_LD_ACPI 0x0a #define W83627HF_LD_HWM 0x0b -#define DEVID 0x20 /* Register: Device ID */ +#define DEVID 0x20 /* Register: Device ID */ #define W83627THF_GPIO5_EN 0x30 /* w83627thf only */ #define W83627THF_GPIO5_IOSR 0xf3 /* w83627thf only */ @@ -246,32 +248,35 @@ static const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 }; static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 }; #define W83781D_DEFAULT_BETA 3435 -/* Conversions. Limit checking is only done on the TO_REG - variants. Note that you should be a bit careful with which arguments - these macros are called: arguments may be evaluated more than once. - Fixing this is just not worth it. */ -#define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8)/16),0,255)) +/* + * Conversions. Limit checking is only done on the TO_REG + * variants. Note that you should be a bit careful with which arguments + * these macros are called: arguments may be evaluated more than once. + * Fixing this is just not worth it. + */ +#define IN_TO_REG(val) (clamp_val((((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); + rpm = clamp_val(rpm, 1, 1000000); + return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254); } #define TEMP_MIN (-128000) #define TEMP_MAX ( 127000) -/* TEMP: 0.001C/bit (-128C to +127C) - REG: 1C/bit, two's complement */ +/* + * TEMP: 0.001C/bit (-128C to +127C) + * REG: 1C/bit, two's complement + */ static u8 TEMP_TO_REG(long temp) { - int ntemp = SENSORS_LIMIT(temp, TEMP_MIN, TEMP_MAX); - ntemp += (ntemp<0 ? -500 : 500); - return (u8)(ntemp / 1000); + int ntemp = clamp_val(temp, TEMP_MIN, TEMP_MAX); + ntemp += (ntemp < 0 ? -500 : 500); + return (u8)(ntemp / 1000); } static int TEMP_FROM_REG(u8 reg) @@ -281,7 +286,7 @@ static int TEMP_FROM_REG(u8 reg) #define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) -#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255)) +#define PWM_TO_REG(val) (clamp_val((val), 0, 255)) static inline unsigned long pwm_freq_from_reg_627hf(u8 reg) { @@ -292,8 +297,10 @@ static inline unsigned long pwm_freq_from_reg_627hf(u8 reg) static inline u8 pwm_freq_to_reg_627hf(unsigned long val) { u8 i; - /* Only 5 dividers (1 2 4 8 16) - Search for the nearest available frequency */ + /* + * Only 5 dividers (1 2 4 8 16) + * Search for the nearest available frequency + */ for (i = 0; i < 4; i++) { if (val > (((W83627HF_BASE_PWM_FREQ >> i) + (W83627HF_BASE_PWM_FREQ >> (i+1))) / 2)) @@ -311,7 +318,7 @@ static inline unsigned long pwm_freq_from_reg(u8 reg) /* This should not happen but anyway... */ if (reg == 0) reg++; - return (clock / (reg << 8)); + return clock / (reg << 8); } static inline u8 pwm_freq_to_reg(unsigned long val) { @@ -319,11 +326,11 @@ static inline u8 pwm_freq_to_reg(unsigned long val) if (val >= 93750) /* The highest we can do */ return 0x01; if (val >= 720) /* Use 24 MHz clock */ - return (24000000UL / (val << 8)); + return 24000000UL / (val << 8); if (val < 6) /* The lowest we can do */ return 0xFF; else /* Use 180 kHz clock */ - return (0x80 | (180000UL / (val << 8))); + return 0x80 | (180000UL / (val << 8)); } #define BEEP_MASK_FROM_REG(val) ((val) & 0xff7fff) @@ -334,17 +341,19 @@ static inline u8 pwm_freq_to_reg(unsigned long val) static inline u8 DIV_TO_REG(long val) { int i; - val = SENSORS_LIMIT(val, 1, 128) >> 1; + val = clamp_val(val, 1, 128) >> 1; for (i = 0; i < 7; i++) { if (val == 0) break; val >>= 1; } - return ((u8) i); + return (u8)i; } -/* For each registered chip, we need to keep some data in memory. - The structure is dynamically allocated. */ +/* + * For each registered chip, we need to keep some data in memory. + * The structure is dynamically allocated. + */ struct w83627hf_data { unsigned short addr; const char *name; @@ -370,18 +379,26 @@ struct w83627hf_data { u32 beep_mask; /* Register encoding, combined */ u8 pwm[3]; /* Register value */ u8 pwm_enable[3]; /* 1 = manual - 2 = thermal cruise (also called SmartFan I) - 3 = fan speed cruise */ + * 2 = thermal cruise (also called SmartFan I) + * 3 = fan speed cruise + */ u8 pwm_freq[3]; /* Register value */ u16 sens[3]; /* 1 = pentium diode; 2 = 3904 diode; - 4 = thermistor */ + * 4 = thermistor + */ u8 vrm; u8 vrm_ovt; /* Register value, 627THF/637HF/687THF only */ + +#ifdef CONFIG_PM + /* Remember extra register values over suspend/resume */ + u8 scfg1; + u8 scfg2; +#endif }; static int w83627hf_probe(struct platform_device *pdev); -static int __devexit w83627hf_remove(struct platform_device *pdev); +static int w83627hf_remove(struct platform_device *pdev); static int w83627hf_read_value(struct w83627hf_data *data, u16 reg); static int w83627hf_write_value(struct w83627hf_data *data, u16 reg, u16 value); @@ -389,13 +406,80 @@ static void w83627hf_update_fan_div(struct w83627hf_data *data); static struct w83627hf_data *w83627hf_update_device(struct device *dev); static void w83627hf_init_device(struct platform_device *pdev); +#ifdef CONFIG_PM +static int w83627hf_suspend(struct device *dev) +{ + struct w83627hf_data *data = w83627hf_update_device(dev); + + mutex_lock(&data->update_lock); + data->scfg1 = w83627hf_read_value(data, W83781D_REG_SCFG1); + data->scfg2 = w83627hf_read_value(data, W83781D_REG_SCFG2); + mutex_unlock(&data->update_lock); + + return 0; +} + +static int w83627hf_resume(struct device *dev) +{ + struct w83627hf_data *data = dev_get_drvdata(dev); + int i, num_temps = (data->type == w83697hf) ? 2 : 3; + + /* Restore limits */ + mutex_lock(&data->update_lock); + for (i = 0; i <= 8; i++) { + /* skip missing sensors */ + if (((data->type == w83697hf) && (i == 1)) || + ((data->type != w83627hf && data->type != w83697hf) + && (i == 5 || i == 6))) + continue; + w83627hf_write_value(data, W83781D_REG_IN_MAX(i), + data->in_max[i]); + w83627hf_write_value(data, W83781D_REG_IN_MIN(i), + data->in_min[i]); + } + for (i = 0; i <= 2; i++) + w83627hf_write_value(data, W83627HF_REG_FAN_MIN(i), + data->fan_min[i]); + for (i = 0; i < num_temps; i++) { + w83627hf_write_value(data, w83627hf_reg_temp_over[i], + data->temp_max[i]); + w83627hf_write_value(data, w83627hf_reg_temp_hyst[i], + data->temp_max_hyst[i]); + } + + /* Fixup BIOS bugs */ + if (data->type == w83627thf || data->type == w83637hf || + data->type == w83687thf) + w83627hf_write_value(data, W83627THF_REG_VRM_OVT_CFG, + data->vrm_ovt); + w83627hf_write_value(data, W83781D_REG_SCFG1, data->scfg1); + w83627hf_write_value(data, W83781D_REG_SCFG2, data->scfg2); + + /* Force re-reading all values */ + data->valid = 0; + mutex_unlock(&data->update_lock); + + return 0; +} + +static const struct dev_pm_ops w83627hf_dev_pm_ops = { + .suspend = w83627hf_suspend, + .resume = w83627hf_resume, +}; + +#define W83627HF_DEV_PM_OPS (&w83627hf_dev_pm_ops) +#else +#define W83627HF_DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + static struct platform_driver w83627hf_driver = { .driver = { .owner = THIS_MODULE, .name = DRVNAME, + .pm = W83627HF_DEV_PM_OPS, }, .probe = w83627hf_probe, - .remove = __devexit_p(w83627hf_remove), + .remove = w83627hf_remove, }; static ssize_t @@ -425,7 +509,12 @@ store_in_min(struct device *dev, struct device_attribute *devattr, { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_min[nr] = IN_TO_REG(val); @@ -439,7 +528,12 @@ store_in_max(struct device *dev, struct device_attribute *devattr, { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->in_max[nr] = IN_TO_REG(val); @@ -504,9 +598,12 @@ static ssize_t store_regs_in_min0(struct device *dev, struct device_attribute *a const char *buf, size_t count) { struct w83627hf_data *data = dev_get_drvdata(dev); - u32 val; + unsigned long val; + int err; - val = simple_strtoul(buf, NULL, 10); + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); @@ -516,8 +613,7 @@ static ssize_t store_regs_in_min0(struct device *dev, struct device_attribute *a /* use VRM9 calculation */ data->in_min[0] = - SENSORS_LIMIT(((val * 100) - 70000 + 244) / 488, 0, - 255); + clamp_val(((val * 100) - 70000 + 244) / 488, 0, 255); else /* use VRM8 (standard) calculation */ data->in_min[0] = IN_TO_REG(val); @@ -531,9 +627,12 @@ static ssize_t store_regs_in_max0(struct device *dev, struct device_attribute *a const char *buf, size_t count) { struct w83627hf_data *data = dev_get_drvdata(dev); - u32 val; + unsigned long val; + int err; - val = simple_strtoul(buf, NULL, 10); + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); @@ -543,8 +642,7 @@ static ssize_t store_regs_in_max0(struct device *dev, struct device_attribute *a /* use VRM9 calculation */ data->in_max[0] = - SENSORS_LIMIT(((val * 100) - 70000 + 244) / 488, 0, - 255); + clamp_val(((val * 100) - 70000 + 244) / 488, 0, 255); else /* use VRM8 (standard) calculation */ data->in_max[0] = IN_TO_REG(val); @@ -582,7 +680,12 @@ store_fan_min(struct device *dev, struct device_attribute *devattr, { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = dev_get_drvdata(dev); - u32 val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); @@ -643,9 +746,15 @@ store_temp_max(struct device *dev, struct device_attribute *devattr, { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10); - u16 tmp = (nr) ? LM75_TEMP_TO_REG(val) : TEMP_TO_REG(val); + u16 tmp; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + tmp = (nr) ? LM75_TEMP_TO_REG(val) : TEMP_TO_REG(val); mutex_lock(&data->update_lock); data->temp_max[nr] = tmp; w83627hf_write_value(data, w83627hf_reg_temp_over[nr], tmp); @@ -659,9 +768,15 @@ store_temp_max_hyst(struct device *dev, struct device_attribute *devattr, { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = dev_get_drvdata(dev); - long val = simple_strtol(buf, NULL, 10); - u16 tmp = (nr) ? LM75_TEMP_TO_REG(val) : TEMP_TO_REG(val); + u16 tmp; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + tmp = (nr) ? LM75_TEMP_TO_REG(val) : TEMP_TO_REG(val); mutex_lock(&data->update_lock); data->temp_max_hyst[nr] = tmp; w83627hf_write_value(data, w83627hf_reg_temp_hyst[nr], tmp); @@ -699,9 +814,12 @@ static ssize_t store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct w83627hf_data *data = dev_get_drvdata(dev); - u32 val; + unsigned long val; + int err; - val = simple_strtoul(buf, NULL, 10); + err = kstrtoul(buf, 10, &val); + if (err) + return err; data->vrm = val; return count; @@ -753,8 +871,11 @@ store_beep_mask(struct device *dev, struct device_attribute *attr, { struct w83627hf_data *data = dev_get_drvdata(dev); unsigned long val; + int err; - val = simple_strtoul(buf, NULL, 10); + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); @@ -789,10 +910,14 @@ store_beep(struct device *dev, struct device_attribute *attr, { struct w83627hf_data *data = dev_get_drvdata(dev); int bitnr = to_sensor_dev_attr(attr)->index; - unsigned long bit; u8 reg; + unsigned long bit; + int err; + + err = kstrtoul(buf, 10, &bit); + if (err) + return err; - bit = simple_strtoul(buf, NULL, 10); if (bit & ~1) return -EINVAL; @@ -870,10 +995,12 @@ show_fan_div(struct device *dev, struct device_attribute *devattr, char *buf) return sprintf(buf, "%ld\n", (long) DIV_FROM_REG(data->fan_div[nr])); } -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan divisor. This follows the principle of - least surprise; the user doesn't expect the fan minimum to change just - because the divisor changed. */ +/* + * Note: we save and restore the fan minimum here, because its value is + * determined in part by the fan divisor. This follows the principle of + * least surprise; the user doesn't expect the fan minimum to change just + * because the divisor changed. + */ static ssize_t store_fan_div(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -882,7 +1009,12 @@ store_fan_div(struct device *dev, struct device_attribute *devattr, struct w83627hf_data *data = dev_get_drvdata(dev); unsigned long min; u8 reg; - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); @@ -931,7 +1063,12 @@ store_pwm(struct device *dev, struct device_attribute *devattr, { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = dev_get_drvdata(dev); - u32 val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); @@ -972,10 +1109,15 @@ store_pwm_enable(struct device *dev, struct device_attribute *devattr, { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = dev_get_drvdata(dev); - unsigned long val = simple_strtoul(buf, NULL, 10); u8 reg; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; - if (!val || (val > 3)) /* modes 1, 2 and 3 are supported */ + if (!val || val > 3) /* modes 1, 2 and 3 are supported */ return -EINVAL; mutex_lock(&data->update_lock); data->pwm_enable[nr] = val; @@ -1014,9 +1156,12 @@ store_pwm_freq(struct device *dev, struct device_attribute *devattr, int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = dev_get_drvdata(dev); static const u8 mask[]={0xF8, 0x8F}; - u32 val; + unsigned long val; + int err; - val = simple_strtoul(buf, NULL, 10); + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); @@ -1058,9 +1203,13 @@ store_temp_type(struct device *dev, struct device_attribute *devattr, { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = dev_get_drvdata(dev); - u32 val, tmp; + unsigned long val; + u32 tmp; + int err; - val = simple_strtoul(buf, NULL, 10); + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); @@ -1127,7 +1276,7 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr, int err = -ENODEV; u16 val; - static const __initdata char *names[] = { + static __initconst char *const names[] = { "W83627HF", "W83627THF", "W83697HF", @@ -1166,14 +1315,13 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr, superio_inb(sio_data, WINB_BASE_REG + 1); *addr = val & WINB_ALIGNMENT; if (*addr == 0) { - printk(KERN_WARNING DRVNAME ": Base address not set, " - "skipping\n"); + pr_warn("Base address not set, skipping\n"); goto exit; } val = superio_inb(sio_data, WINB_ACT_REG); if (!(val & 0x01)) { - printk(KERN_WARNING DRVNAME ": Enabling HWM logical device\n"); + pr_warn("Enabling HWM logical device\n"); superio_outb(sio_data, WINB_ACT_REG, val | 0x01); } @@ -1264,10 +1412,10 @@ static const struct attribute_group w83627hf_group_opt = { .attrs = w83627hf_attributes_opt, }; -static int __devinit w83627hf_probe(struct platform_device *pdev) +static int w83627hf_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct w83627hf_sio_data *sio_data = dev->platform_data; + struct w83627hf_sio_data *sio_data = dev_get_platdata(dev); struct w83627hf_data *data; struct resource *res; int err, i; @@ -1281,18 +1429,17 @@ static int __devinit w83627hf_probe(struct platform_device *pdev) }; res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start, WINB_REGION_SIZE, DRVNAME)) { + if (!devm_request_region(dev, res->start, WINB_REGION_SIZE, DRVNAME)) { dev_err(dev, "Failed to request region 0x%lx-0x%lx\n", (unsigned long)res->start, (unsigned long)(res->start + WINB_REGION_SIZE - 1)); - err = -EBUSY; - goto ERROR0; + return -EBUSY; } - if (!(data = kzalloc(sizeof(struct w83627hf_data), GFP_KERNEL))) { - err = -ENOMEM; - goto ERROR1; - } + data = devm_kzalloc(dev, sizeof(struct w83627hf_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + data->addr = res->start; data->type = sio_data->type; data->name = names[sio_data->type]; @@ -1310,8 +1457,9 @@ static int __devinit w83627hf_probe(struct platform_device *pdev) w83627hf_update_fan_div(data); /* Register common device attributes */ - if ((err = sysfs_create_group(&dev->kobj, &w83627hf_group))) - goto ERROR3; + err = sysfs_create_group(&dev->kobj, &w83627hf_group); + if (err) + return err; /* Register chip-specific device attributes */ if (data->type == w83627hf || data->type == w83697hf) @@ -1339,7 +1487,7 @@ static int __devinit w83627hf_probe(struct platform_device *pdev) &sensor_dev_attr_pwm1_freq.dev_attr)) || (err = device_create_file(dev, &sensor_dev_attr_pwm2_freq.dev_attr))) - goto ERROR4; + goto error; if (data->type != w83697hf) if ((err = device_create_file(dev, @@ -1374,7 +1522,7 @@ static int __devinit w83627hf_probe(struct platform_device *pdev) &sensor_dev_attr_temp3_beep.dev_attr)) || (err = device_create_file(dev, &sensor_dev_attr_temp3_type.dev_attr))) - goto ERROR4; + goto error; if (data->type != w83697hf && data->vid != 0xff) { /* Convert VID to voltage based on VRM */ @@ -1382,14 +1530,15 @@ static int __devinit w83627hf_probe(struct platform_device *pdev) if ((err = device_create_file(dev, &dev_attr_cpu0_vid)) || (err = device_create_file(dev, &dev_attr_vrm))) - goto ERROR4; + goto error; } if (data->type == w83627thf || data->type == w83637hf - || data->type == w83687thf) - if ((err = device_create_file(dev, - &sensor_dev_attr_pwm3.dev_attr))) - goto ERROR4; + || data->type == w83687thf) { + err = device_create_file(dev, &sensor_dev_attr_pwm3.dev_attr); + if (err) + goto error; + } if (data->type == w83637hf || data->type == w83687thf) if ((err = device_create_file(dev, @@ -1398,55 +1547,45 @@ static int __devinit w83627hf_probe(struct platform_device *pdev) &sensor_dev_attr_pwm2_freq.dev_attr)) || (err = device_create_file(dev, &sensor_dev_attr_pwm3_freq.dev_attr))) - goto ERROR4; + goto error; if (data->type != w83627hf) if ((err = device_create_file(dev, &sensor_dev_attr_pwm1_enable.dev_attr)) || (err = device_create_file(dev, &sensor_dev_attr_pwm2_enable.dev_attr))) - goto ERROR4; + goto error; if (data->type == w83627thf || data->type == w83637hf - || data->type == w83687thf) - if ((err = device_create_file(dev, - &sensor_dev_attr_pwm3_enable.dev_attr))) - goto ERROR4; + || data->type == w83687thf) { + err = device_create_file(dev, + &sensor_dev_attr_pwm3_enable.dev_attr); + if (err) + goto error; + } data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); - goto ERROR4; + goto error; } return 0; - ERROR4: + error: sysfs_remove_group(&dev->kobj, &w83627hf_group); sysfs_remove_group(&dev->kobj, &w83627hf_group_opt); - ERROR3: - platform_set_drvdata(pdev, NULL); - kfree(data); - ERROR1: - release_region(res->start, WINB_REGION_SIZE); - ERROR0: return err; } -static int __devexit w83627hf_remove(struct platform_device *pdev) +static int w83627hf_remove(struct platform_device *pdev) { struct w83627hf_data *data = platform_get_drvdata(pdev); - struct resource *res; hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&pdev->dev.kobj, &w83627hf_group); sysfs_remove_group(&pdev->dev.kobj, &w83627hf_group_opt); - platform_set_drvdata(pdev, NULL); - kfree(data); - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - release_region(res->start, WINB_REGION_SIZE); return 0; } @@ -1495,9 +1634,9 @@ static int w83627hf_read_value(struct w83627hf_data *data, u16 reg) return res; } -static int __devinit w83627thf_read_gpio5(struct platform_device *pdev) +static int w83627thf_read_gpio5(struct platform_device *pdev) { - struct w83627hf_sio_data *sio_data = pdev->dev.platform_data; + struct w83627hf_sio_data *sio_data = dev_get_platdata(&pdev->dev); int res = 0xff, sel; superio_enter(sio_data); @@ -1509,8 +1648,10 @@ static int __devinit w83627thf_read_gpio5(struct platform_device *pdev) goto exit; } - /* Make sure the pins are configured for input - There must be at least five (VRM 9), and possibly 6 (VRM 10) */ + /* + * Make sure the pins are configured for input + * There must be at least five (VRM 9), and possibly 6 (VRM 10) + */ sel = superio_inb(sio_data, W83627THF_GPIO5_IOSR) & 0x3f; if ((sel & 0x1f) != 0x1f) { dev_dbg(&pdev->dev, "GPIO5 not configured for VID " @@ -1526,9 +1667,9 @@ exit: return res; } -static int __devinit w83687thf_read_vid(struct platform_device *pdev) +static int w83687thf_read_vid(struct platform_device *pdev) { - struct w83627hf_sio_data *sio_data = pdev->dev.platform_data; + struct w83627hf_sio_data *sio_data = dev_get_platdata(&pdev->dev); int res = 0xff; superio_enter(sio_data); @@ -1578,7 +1719,7 @@ static int w83627hf_write_value(struct w83627hf_data *data, u16 reg, u16 value) return 0; } -static void __devinit w83627hf_init_device(struct platform_device *pdev) +static void w83627hf_init_device(struct platform_device *pdev) { struct w83627hf_data *data = platform_get_drvdata(pdev); int i; @@ -1588,8 +1729,10 @@ static void __devinit w83627hf_init_device(struct platform_device *pdev) /* Minimize conflicts with other winbond i2c-only clients... */ /* disable i2c subclients... how to disable main i2c client?? */ /* force i2c address to relatively uncommon address */ - w83627hf_write_value(data, W83781D_REG_I2C_SUBADDR, 0x89); - w83627hf_write_value(data, W83781D_REG_I2C_ADDR, force_i2c); + if (type == w83627hf) { + w83627hf_write_value(data, W83781D_REG_I2C_SUBADDR, 0x89); + w83627hf_write_value(data, W83781D_REG_I2C_ADDR, force_i2c); + } /* Read VID only once */ if (type == w83627hf || type == w83637hf) { @@ -1789,28 +1932,26 @@ static int __init w83627hf_device_add(unsigned short address, pdev = platform_device_alloc(DRVNAME, address); if (!pdev) { err = -ENOMEM; - printk(KERN_ERR DRVNAME ": Device allocation failed\n"); + pr_err("Device allocation failed\n"); goto exit; } err = platform_device_add_resources(pdev, &res, 1); if (err) { - printk(KERN_ERR DRVNAME ": Device resource addition failed " - "(%d)\n", err); + pr_err("Device resource addition failed (%d)\n", err); goto exit_device_put; } err = platform_device_add_data(pdev, sio_data, sizeof(struct w83627hf_sio_data)); if (err) { - printk(KERN_ERR DRVNAME ": Platform data allocation failed\n"); + pr_err("Platform data allocation failed\n"); goto exit_device_put; } err = platform_device_add(pdev); if (err) { - printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", - err); + pr_err("Device addition failed (%d)\n", err); goto exit_device_put; } diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index 05f9225b6f9..84911616d8c 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -1,37 +1,39 @@ /* - w83781d.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (c) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>, - Philip Edelbrock <phil@netroedge.com>, - and Mark Studebaker <mdsxyz123@yahoo.com> - Copyright (c) 2007 - 2008 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; 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. -*/ + * w83781d.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (c) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>, + * Philip Edelbrock <phil@netroedge.com>, + * and Mark Studebaker <mdsxyz123@yahoo.com> + * Copyright (c) 2007 - 2008 Jean Delvare <jdelvare@suse.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ /* - Supports following chips: - - Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA - as99127f 7 3 0 3 0x31 0x12c3 yes no - as99127f rev.2 (type_name = as99127f) 0x31 0x5ca3 yes no - w83781d 7 3 0 3 0x10-1 0x5ca3 yes yes - w83782d 9 3 2-4 3 0x30 0x5ca3 yes yes - w83783s 5-6 3 2 1-2 0x40 0x5ca3 yes no + * Supports following chips: + * + * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA + * as99127f 7 3 0 3 0x31 0x12c3 yes no + * as99127f rev.2 (type_name = as99127f) 0x31 0x5ca3 yes no + * w83781d 7 3 0 3 0x10-1 0x5ca3 yes yes + * w83782d 9 3 2-4 3 0x30 0x5ca3 yes yes + * w83783s 5-6 3 2 1-2 0x40 0x5ca3 yes no + * + */ -*/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/init.h> @@ -62,14 +64,14 @@ enum chips { w83781d, w83782d, w83783s, as99127f }; /* Insmod parameters */ static unsigned short force_subclients[4]; module_param_array(force_subclients, short, NULL, 0); -MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " - "{bus, clientaddr, subclientaddr1, subclientaddr2}"); +MODULE_PARM_DESC(force_subclients, + "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}"); -static int reset; +static bool reset; module_param(reset, bool, 0); MODULE_PARM_DESC(reset, "Set to one to reset chip on load"); -static int init = 1; +static bool init = 1; module_param(init, bool, 0); MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization"); @@ -143,8 +145,10 @@ static const u8 W83781D_REG_PWM[] = { 0x5B, 0x5A, 0x5E, 0x5F }; #define W83781D_REG_I2C_ADDR 0x48 #define W83781D_REG_I2C_SUBADDR 0x4A -/* The following are undocumented in the data sheets however we - received the information in an email from Winbond tech support */ +/* + * The following are undocumented in the data sheets however we + * received the information in an email from Winbond tech support + */ /* Sensor selection - not on 781d */ #define W83781D_REG_SCFG1 0x5D static const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 }; @@ -155,7 +159,7 @@ static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 }; #define W83781D_DEFAULT_BETA 3435 /* Conversions */ -#define IN_TO_REG(val) SENSORS_LIMIT(((val) + 8) / 16, 0, 255) +#define IN_TO_REG(val) clamp_val(((val) + 8) / 16, 0, 255) #define IN_FROM_REG(val) ((val) * 16) static inline u8 @@ -163,8 +167,8 @@ 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); + rpm = clamp_val(rpm, 1, 1000000); + return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254); } static inline long @@ -177,12 +181,12 @@ FAN_FROM_REG(u8 val, int div) return 1350000 / (val * div); } -#define TEMP_TO_REG(val) SENSORS_LIMIT((val) / 1000, -127, 128) +#define TEMP_TO_REG(val) clamp_val((val) / 1000, -127, 128) #define TEMP_FROM_REG(val) ((val) * 1000) -#define BEEP_MASK_FROM_REG(val,type) ((type) == as99127f ? \ +#define BEEP_MASK_FROM_REG(val, type) ((type) == as99127f ? \ (~(val)) & 0x7fff : (val) & 0xff7fff) -#define BEEP_MASK_TO_REG(val,type) ((type) == as99127f ? \ +#define BEEP_MASK_TO_REG(val, type) ((type) == as99127f ? \ (~(val)) & 0x7fff : (val) & 0xff7fff) #define DIV_FROM_REG(val) (1 << (val)) @@ -191,9 +195,8 @@ static inline u8 DIV_TO_REG(long val, enum chips type) { int i; - val = SENSORS_LIMIT(val, 1, - ((type == w83781d - || type == as99127f) ? 8 : 128)) >> 1; + val = clamp_val(val, 1, + ((type == w83781d || type == as99127f) ? 8 : 128)) >> 1; for (i = 0; i < 7; i++) { if (val == 0) break; @@ -236,9 +239,11 @@ struct w83781d_data { u32 beep_mask; /* Register encoding, combined */ u8 pwm[4]; /* Register value */ u8 pwm2_enable; /* Boolean */ - u16 sens[3]; /* 782D/783S only. - 1 = pentium diode; 2 = 3904 diode; - 4 = thermistor */ + u16 sens[3]; /* + * 782D/783S only. + * 1 = pentium diode; 2 = 3904 diode; + * 4 = thermistor + */ u8 vrm; }; @@ -252,7 +257,7 @@ static void w83781d_init_device(struct device *dev); /* following are the sysfs callback functions */ #define show_in_reg(reg) \ -static ssize_t show_##reg (struct device *dev, struct device_attribute *da, \ +static ssize_t show_##reg(struct device *dev, struct device_attribute *da, \ char *buf) \ { \ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \ @@ -265,20 +270,21 @@ show_in_reg(in_min); show_in_reg(in_max); #define store_in_reg(REG, reg) \ -static ssize_t store_in_##reg (struct device *dev, struct device_attribute \ +static ssize_t store_in_##reg(struct device *dev, struct device_attribute \ *da, const char *buf, size_t count) \ { \ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \ struct w83781d_data *data = dev_get_drvdata(dev); \ int nr = attr->index; \ - u32 val; \ - \ - val = simple_strtoul(buf, NULL, 10); \ - \ + unsigned long val; \ + int err = kstrtoul(buf, 10, &val); \ + if (err) \ + return err; \ mutex_lock(&data->update_lock); \ data->in_##reg[nr] = IN_TO_REG(val); \ - w83781d_write_value(data, W83781D_REG_IN_##REG(nr), data->in_##reg[nr]); \ - \ + w83781d_write_value(data, W83781D_REG_IN_##REG(nr), \ + data->in_##reg[nr]); \ + \ mutex_unlock(&data->update_lock); \ return count; \ } @@ -304,12 +310,12 @@ sysfs_in_offsets(7); sysfs_in_offsets(8); #define show_fan_reg(reg) \ -static ssize_t show_##reg (struct device *dev, struct device_attribute *da, \ +static ssize_t show_##reg(struct device *dev, struct device_attribute *da, \ char *buf) \ { \ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \ struct w83781d_data *data = w83781d_update_device(dev); \ - return sprintf(buf,"%ld\n", \ + return sprintf(buf, "%ld\n", \ FAN_FROM_REG(data->reg[attr->index], \ DIV_FROM_REG(data->fan_div[attr->index]))); \ } @@ -323,9 +329,12 @@ store_fan_min(struct device *dev, struct device_attribute *da, struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct w83781d_data *data = dev_get_drvdata(dev); int nr = attr->index; - u32 val; + unsigned long val; + int err; - val = simple_strtoul(buf, NULL, 10); + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->fan_min[nr] = @@ -348,17 +357,17 @@ static SENSOR_DEVICE_ATTR(fan3_min, S_IRUGO | S_IWUSR, show_fan_min, store_fan_min, 2); #define show_temp_reg(reg) \ -static ssize_t show_##reg (struct device *dev, struct device_attribute *da, \ +static ssize_t show_##reg(struct device *dev, struct device_attribute *da, \ char *buf) \ { \ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \ struct w83781d_data *data = w83781d_update_device(dev); \ int nr = attr->index; \ if (nr >= 2) { /* TEMP2 and TEMP3 */ \ - return sprintf(buf,"%d\n", \ + return sprintf(buf, "%d\n", \ LM75_TEMP_FROM_REG(data->reg##_add[nr-2])); \ } else { /* TEMP1 */ \ - return sprintf(buf,"%ld\n", (long)TEMP_FROM_REG(data->reg)); \ + return sprintf(buf, "%ld\n", (long)TEMP_FROM_REG(data->reg)); \ } \ } show_temp_reg(temp); @@ -366,16 +375,16 @@ show_temp_reg(temp_max); show_temp_reg(temp_max_hyst); #define store_temp_reg(REG, reg) \ -static ssize_t store_temp_##reg (struct device *dev, \ +static ssize_t store_temp_##reg(struct device *dev, \ struct device_attribute *da, const char *buf, size_t count) \ { \ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \ struct w83781d_data *data = dev_get_drvdata(dev); \ int nr = attr->index; \ long val; \ - \ - val = simple_strtol(buf, NULL, 10); \ - \ + int err = kstrtol(buf, 10, &val); \ + if (err) \ + return err; \ mutex_lock(&data->update_lock); \ \ if (nr >= 2) { /* TEMP2 and TEMP3 */ \ @@ -423,13 +432,17 @@ show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +store_vrm_reg(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct w83781d_data *data = dev_get_drvdata(dev); - u32 val; + unsigned long val; + int err; - val = simple_strtoul(buf, NULL, 10); - data->vrm = val; + err = kstrtoul(buf, 10, &val); + if (err) + return err; + data->vrm = clamp_val(val, 0, 255); return count; } @@ -478,7 +491,8 @@ static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4); static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5); static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_temp3_alarm, NULL, 0); -static ssize_t show_beep_mask (struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_beep_mask(struct device *dev, + struct device_attribute *attr, char *buf) { struct w83781d_data *data = w83781d_update_device(dev); return sprintf(buf, "%ld\n", @@ -490,9 +504,12 @@ store_beep_mask(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct w83781d_data *data = dev_get_drvdata(dev); - u32 val; + unsigned long val; + int err; - val = simple_strtoul(buf, NULL, 10); + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->beep_mask &= 0x8000; /* preserve beep enable */ @@ -527,10 +544,14 @@ store_beep(struct device *dev, struct device_attribute *attr, { struct w83781d_data *data = dev_get_drvdata(dev); int bitnr = to_sensor_dev_attr(attr)->index; - unsigned long bit; u8 reg; + unsigned long bit; + int err; + + err = kstrtoul(buf, 10, &bit); + if (err) + return err; - bit = simple_strtoul(buf, NULL, 10); if (bit & ~1) return -EINVAL; @@ -618,10 +639,12 @@ show_fan_div(struct device *dev, struct device_attribute *da, char *buf) (long) DIV_FROM_REG(data->fan_div[attr->index])); } -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan divisor. This follows the principle of - least surprise; the user doesn't expect the fan minimum to change just - because the divisor changed. */ +/* + * Note: we save and restore the fan minimum here, because its value is + * determined in part by the fan divisor. This follows the principle of + * least surprise; the user doesn't expect the fan minimum to change just + * because the divisor changed. + */ static ssize_t store_fan_div(struct device *dev, struct device_attribute *da, const char *buf, size_t count) @@ -631,7 +654,12 @@ store_fan_div(struct device *dev, struct device_attribute *da, unsigned long min; int nr = attr->index; u8 reg; - unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); @@ -641,10 +669,12 @@ store_fan_div(struct device *dev, struct device_attribute *da, data->fan_div[nr] = DIV_TO_REG(val, data->type); - reg = (w83781d_read_value(data, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV) - & (nr==0 ? 0xcf : 0x3f)) - | ((data->fan_div[nr] & 0x03) << (nr==0 ? 4 : 6)); - w83781d_write_value(data, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV, reg); + reg = (w83781d_read_value(data, nr == 2 ? + W83781D_REG_PIN : W83781D_REG_VID_FANDIV) + & (nr == 0 ? 0xcf : 0x3f)) + | ((data->fan_div[nr] & 0x03) << (nr == 0 ? 4 : 6)); + w83781d_write_value(data, nr == 2 ? + W83781D_REG_PIN : W83781D_REG_VID_FANDIV, reg); /* w83781d and as99127f don't have extended divisor bits */ if (data->type != w83781d && data->type != as99127f) { @@ -691,12 +721,15 @@ store_pwm(struct device *dev, struct device_attribute *da, const char *buf, struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct w83781d_data *data = dev_get_drvdata(dev); int nr = attr->index; - u32 val; + unsigned long val; + int err; - val = simple_strtoul(buf, NULL, 10); + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - data->pwm[nr] = SENSORS_LIMIT(val, 0, 255); + data->pwm[nr] = clamp_val(val, 0, 255); w83781d_write_value(data, W83781D_REG_PWM[nr], data->pwm[nr]); mutex_unlock(&data->update_lock); return count; @@ -707,9 +740,13 @@ store_pwm2_enable(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct w83781d_data *data = dev_get_drvdata(dev); - u32 val, reg; + unsigned long val; + u32 reg; + int err; - val = simple_strtoul(buf, NULL, 10); + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); @@ -759,9 +796,13 @@ store_sensor(struct device *dev, struct device_attribute *da, struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct w83781d_data *data = dev_get_drvdata(dev); int nr = attr->index; - u32 val, tmp; + unsigned long val; + u32 tmp; + int err; - val = simple_strtoul(buf, NULL, 10); + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); @@ -785,8 +826,9 @@ store_sensor(struct device *dev, struct device_attribute *da, data->sens[nr] = val; break; case W83781D_DEFAULT_BETA: - dev_warn(dev, "Sensor type %d is deprecated, please use 4 " - "instead\n", W83781D_DEFAULT_BETA); + dev_warn(dev, + "Sensor type %d is deprecated, please use 4 instead\n", + W83781D_DEFAULT_BETA); /* fall through */ case 4: /* thermistor */ tmp = w83781d_read_value(data, W83781D_REG_SCFG1); @@ -811,7 +853,8 @@ static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO | S_IWUSR, show_sensor, store_sensor, 2); -/* Assumes that adapter is of I2C, not ISA variety. +/* + * Assumes that adapter is of I2C, not ISA variety. * OTHERWISE DON'T CALL THIS */ static int @@ -824,6 +867,7 @@ w83781d_detect_subclients(struct i2c_client *new_client) struct i2c_adapter *adapter = new_client->adapter; struct w83781d_data *data = i2c_get_clientdata(new_client); enum chips kind = data->type; + int num_sc = 1; id = i2c_adapter_id(adapter); @@ -831,8 +875,8 @@ w83781d_detect_subclients(struct i2c_client *new_client) for (i = 2; i <= 3; i++) { if (force_subclients[i] < 0x48 || force_subclients[i] > 0x4f) { - dev_err(&new_client->dev, "Invalid subclient " - "address %d; must be 0x48-0x4f\n", + dev_err(&new_client->dev, + "Invalid subclient address %d; must be 0x48-0x4f\n", force_subclients[i]); err = -EINVAL; goto ERROR_SC_1; @@ -848,6 +892,7 @@ w83781d_detect_subclients(struct i2c_client *new_client) } if (kind != w83783s) { + num_sc = 2; if (force_subclients[0] == id && force_subclients[1] == address) { sc_addr[1] = force_subclients[3]; @@ -863,19 +908,17 @@ w83781d_detect_subclients(struct i2c_client *new_client) } } - for (i = 0; i <= 1; i++) { + for (i = 0; i < num_sc; i++) { data->lm75[i] = i2c_new_dummy(adapter, sc_addr[i]); if (!data->lm75[i]) { - dev_err(&new_client->dev, "Subclient %d " - "registration at address 0x%x " - "failed.\n", i, sc_addr[i]); + dev_err(&new_client->dev, + "Subclient %d registration at address 0x%x failed.\n", + i, sc_addr[i]); err = -ENOMEM; if (i == 1) goto ERROR_SC_3; goto ERROR_SC_2; } - if (kind == w83783s) - break; } return 0; @@ -909,7 +952,7 @@ ERROR_SC_1: &sensor_dev_attr_temp##X##_alarm.dev_attr.attr, \ &sensor_dev_attr_temp##X##_beep.dev_attr.attr -static struct attribute* w83781d_attributes[] = { +static struct attribute *w83781d_attributes[] = { IN_UNIT_ATTRS(0), IN_UNIT_ATTRS(2), IN_UNIT_ATTRS(3), @@ -932,23 +975,58 @@ static const struct attribute_group w83781d_group = { .attrs = w83781d_attributes, }; -static struct attribute *w83781d_attributes_opt[] = { +static struct attribute *w83781d_attributes_in1[] = { IN_UNIT_ATTRS(1), + NULL +}; +static const struct attribute_group w83781d_group_in1 = { + .attrs = w83781d_attributes_in1, +}; + +static struct attribute *w83781d_attributes_in78[] = { IN_UNIT_ATTRS(7), IN_UNIT_ATTRS(8), + NULL +}; +static const struct attribute_group w83781d_group_in78 = { + .attrs = w83781d_attributes_in78, +}; + +static struct attribute *w83781d_attributes_temp3[] = { TEMP_UNIT_ATTRS(3), + NULL +}; +static const struct attribute_group w83781d_group_temp3 = { + .attrs = w83781d_attributes_temp3, +}; + +static struct attribute *w83781d_attributes_pwm12[] = { &sensor_dev_attr_pwm1.dev_attr.attr, &sensor_dev_attr_pwm2.dev_attr.attr, + &dev_attr_pwm2_enable.attr, + NULL +}; +static const struct attribute_group w83781d_group_pwm12 = { + .attrs = w83781d_attributes_pwm12, +}; + +static struct attribute *w83781d_attributes_pwm34[] = { &sensor_dev_attr_pwm3.dev_attr.attr, &sensor_dev_attr_pwm4.dev_attr.attr, - &dev_attr_pwm2_enable.attr, + NULL +}; +static const struct attribute_group w83781d_group_pwm34 = { + .attrs = w83781d_attributes_pwm34, +}; + +static struct attribute *w83781d_attributes_other[] = { &sensor_dev_attr_temp1_type.dev_attr.attr, &sensor_dev_attr_temp2_type.dev_attr.attr, &sensor_dev_attr_temp3_type.dev_attr.attr, NULL }; -static const struct attribute_group w83781d_group_opt = { - .attrs = w83781d_attributes_opt, +static const struct attribute_group w83781d_group_other = { + .attrs = w83781d_attributes_other, }; /* No clean up is done on error, it's up to the caller */ @@ -957,56 +1035,23 @@ w83781d_create_files(struct device *dev, int kind, int is_isa) { int err; - if ((err = sysfs_create_group(&dev->kobj, &w83781d_group))) + err = sysfs_create_group(&dev->kobj, &w83781d_group); + if (err) return err; if (kind != w83783s) { - if ((err = device_create_file(dev, - &sensor_dev_attr_in1_input.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_in1_min.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_in1_max.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_in1_alarm.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_in1_beep.dev_attr))) + err = sysfs_create_group(&dev->kobj, &w83781d_group_in1); + if (err) return err; } if (kind != as99127f && kind != w83781d && kind != w83783s) { - if ((err = device_create_file(dev, - &sensor_dev_attr_in7_input.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_in7_min.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_in7_max.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_in7_alarm.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_in7_beep.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_in8_input.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_in8_min.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_in8_max.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_in8_alarm.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_in8_beep.dev_attr))) + err = sysfs_create_group(&dev->kobj, &w83781d_group_in78); + if (err) return err; } if (kind != w83783s) { - if ((err = device_create_file(dev, - &sensor_dev_attr_temp3_input.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_temp3_max.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_temp3_max_hyst.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_temp3_alarm.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_temp3_beep.dev_attr))) + err = sysfs_create_group(&dev->kobj, &w83781d_group_temp3); + if (err) return err; if (kind != w83781d) { @@ -1019,30 +1064,29 @@ w83781d_create_files(struct device *dev, int kind, int is_isa) } if (kind != w83781d && kind != as99127f) { - if ((err = device_create_file(dev, - &sensor_dev_attr_pwm1.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_pwm2.dev_attr)) - || (err = device_create_file(dev, &dev_attr_pwm2_enable))) + err = sysfs_create_group(&dev->kobj, &w83781d_group_pwm12); + if (err) return err; } if (kind == w83782d && !is_isa) { - if ((err = device_create_file(dev, - &sensor_dev_attr_pwm3.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_pwm4.dev_attr))) + err = sysfs_create_group(&dev->kobj, &w83781d_group_pwm34); + if (err) return err; } if (kind != as99127f && kind != w83781d) { - if ((err = device_create_file(dev, - &sensor_dev_attr_temp1_type.dev_attr)) - || (err = device_create_file(dev, - &sensor_dev_attr_temp2_type.dev_attr))) + err = device_create_file(dev, + &sensor_dev_attr_temp1_type.dev_attr); + if (err) + return err; + err = device_create_file(dev, + &sensor_dev_attr_temp2_type.dev_attr); + if (err) return err; if (kind != w83783s) { - if ((err = device_create_file(dev, - &sensor_dev_attr_temp3_type.dev_attr))) + err = device_create_file(dev, + &sensor_dev_attr_temp3_type.dev_attr); + if (err) return err; } } @@ -1064,9 +1108,11 @@ w83781d_detect(struct i2c_client *client, struct i2c_board_info *info) if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* We block updates of the ISA device to minimize the risk of - concurrent access to the same W83781D chip through different - interfaces. */ + /* + * We block updates of the ISA device to minimize the risk of + * concurrent access to the same W83781D chip through different + * interfaces. + */ if (isa) mutex_lock(&isa->update_lock); @@ -1081,15 +1127,17 @@ w83781d_detect(struct i2c_client *client, struct i2c_board_info *info) /* Check for Winbond or Asus ID if in bank 0 */ if (!(val1 & 0x07) && ((!(val1 & 0x80) && val2 != 0xa3 && val2 != 0xc3) || - ( (val1 & 0x80) && val2 != 0x5c && val2 != 0x12))) { + ((val1 & 0x80) && val2 != 0x5c && val2 != 0x12))) { dev_dbg(&adapter->dev, "Detection of w83781d chip failed at step 4\n"); goto err_nodev; } - /* If Winbond SMBus, check address at 0x48. - Asus doesn't support, except for as99127f rev.2 */ + /* + * If Winbond SMBus, check address at 0x48. + * Asus doesn't support, except for as99127f rev.2 + */ if ((!(val1 & 0x80) && val2 == 0xa3) || - ( (val1 & 0x80) && val2 == 0x5c)) { + ((val1 & 0x80) && val2 == 0x5c)) { if (i2c_smbus_read_byte_data(client, W83781D_REG_I2C_ADDR) != address) { dev_dbg(&adapter->dev, @@ -1129,8 +1177,9 @@ w83781d_detect(struct i2c_client *client, struct i2c_board_info *info) goto err_nodev; if (val1 <= 0x30 && w83781d_alias_detect(client, val1)) { - dev_dbg(&adapter->dev, "Device at 0x%02x appears to " - "be the same as ISA device\n", address); + dev_dbg(&adapter->dev, + "Device at 0x%02x appears to be the same as ISA device\n", + address); goto err_nodev; } @@ -1147,6 +1196,17 @@ w83781d_detect(struct i2c_client *client, struct i2c_board_info *info) return -ENODEV; } +static void w83781d_remove_files(struct device *dev) +{ + sysfs_remove_group(&dev->kobj, &w83781d_group); + sysfs_remove_group(&dev->kobj, &w83781d_group_in1); + sysfs_remove_group(&dev->kobj, &w83781d_group_in78); + sysfs_remove_group(&dev->kobj, &w83781d_group_temp3); + sysfs_remove_group(&dev->kobj, &w83781d_group_pwm12); + sysfs_remove_group(&dev->kobj, &w83781d_group_pwm34); + sysfs_remove_group(&dev->kobj, &w83781d_group_other); +} + static int w83781d_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1154,11 +1214,9 @@ w83781d_probe(struct i2c_client *client, const struct i2c_device_id *id) struct w83781d_data *data; int err; - data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto ERROR1; - } + data = devm_kzalloc(dev, sizeof(struct w83781d_data), GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); mutex_init(&data->lock); @@ -1170,7 +1228,7 @@ w83781d_probe(struct i2c_client *client, const struct i2c_device_id *id) /* attach secondary i2c lm75-like clients */ err = w83781d_detect_subclients(client); if (err) - goto ERROR3; + return err; /* Initialize the chip */ w83781d_init_device(dev); @@ -1178,28 +1236,22 @@ w83781d_probe(struct i2c_client *client, const struct i2c_device_id *id) /* Register sysfs hooks */ err = w83781d_create_files(dev, data->type, 0); if (err) - goto ERROR4; + goto exit_remove_files; data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); - goto ERROR4; + goto exit_remove_files; } return 0; -ERROR4: - sysfs_remove_group(&dev->kobj, &w83781d_group); - sysfs_remove_group(&dev->kobj, &w83781d_group_opt); - + exit_remove_files: + w83781d_remove_files(dev); if (data->lm75[0]) i2c_unregister_device(data->lm75[0]); if (data->lm75[1]) i2c_unregister_device(data->lm75[1]); -ERROR3: - i2c_set_clientdata(client, NULL); - kfree(data); -ERROR1: return err; } @@ -1210,18 +1262,13 @@ w83781d_remove(struct i2c_client *client) struct device *dev = &client->dev; hwmon_device_unregister(data->hwmon_dev); - - sysfs_remove_group(&dev->kobj, &w83781d_group); - sysfs_remove_group(&dev->kobj, &w83781d_group_opt); + w83781d_remove_files(dev); if (data->lm75[0]) i2c_unregister_device(data->lm75[0]); if (data->lm75[1]) i2c_unregister_device(data->lm75[1]); - i2c_set_clientdata(client, NULL); - kfree(data); - return 0; } @@ -1245,17 +1292,17 @@ w83781d_read_value_i2c(struct w83781d_data *data, u16 reg) /* convert from ISA to LM75 I2C addresses */ switch (reg & 0xff) { case 0x50: /* TEMP */ - res = swab16(i2c_smbus_read_word_data(cl, 0)); + res = i2c_smbus_read_word_swapped(cl, 0); break; case 0x52: /* CONFIG */ res = i2c_smbus_read_byte_data(cl, 1); break; case 0x53: /* HYST */ - res = swab16(i2c_smbus_read_word_data(cl, 2)); + res = i2c_smbus_read_word_swapped(cl, 2); break; case 0x55: /* OVER */ default: - res = swab16(i2c_smbus_read_word_data(cl, 3)); + res = i2c_smbus_read_word_swapped(cl, 3); break; } } @@ -1289,10 +1336,10 @@ w83781d_write_value_i2c(struct w83781d_data *data, u16 reg, u16 value) i2c_smbus_write_byte_data(cl, 1, value & 0xff); break; case 0x53: /* HYST */ - i2c_smbus_write_word_data(cl, 2, swab16(value)); + i2c_smbus_write_word_swapped(cl, 2, value); break; case 0x55: /* OVER */ - i2c_smbus_write_word_data(cl, 3, swab16(value)); + i2c_smbus_write_word_swapped(cl, 3, value); break; } } @@ -1310,35 +1357,47 @@ w83781d_init_device(struct device *dev) int type = data->type; u8 tmp; - if (reset && type != as99127f) { /* this resets registers we don't have - documentation for on the as99127f */ - /* Resetting the chip has been the default for a long time, - but it causes the BIOS initializations (fan clock dividers, - thermal sensor types...) to be lost, so it is now optional. - It might even go away if nobody reports it as being useful, - as I see very little reason why this would be needed at - all. */ - dev_info(dev, "If reset=1 solved a problem you were " - "having, please report!\n"); + if (reset && type != as99127f) { /* + * this resets registers we don't have + * documentation for on the as99127f + */ + /* + * Resetting the chip has been the default for a long time, + * but it causes the BIOS initializations (fan clock dividers, + * thermal sensor types...) to be lost, so it is now optional. + * It might even go away if nobody reports it as being useful, + * as I see very little reason why this would be needed at + * all. + */ + dev_info(dev, + "If reset=1 solved a problem you were having, please report!\n"); /* save these registers */ i = w83781d_read_value(data, W83781D_REG_BEEP_CONFIG); p = w83781d_read_value(data, W83781D_REG_PWMCLK12); - /* Reset all except Watchdog values and last conversion values - This sets fan-divs to 2, among others */ + /* + * Reset all except Watchdog values and last conversion values + * This sets fan-divs to 2, among others + */ w83781d_write_value(data, W83781D_REG_CONFIG, 0x80); - /* Restore the registers and disable power-on abnormal beep. - This saves FAN 1/2/3 input/output values set by BIOS. */ + /* + * Restore the registers and disable power-on abnormal beep. + * This saves FAN 1/2/3 input/output values set by BIOS. + */ w83781d_write_value(data, W83781D_REG_BEEP_CONFIG, i | 0x80); w83781d_write_value(data, W83781D_REG_PWMCLK12, p); - /* Disable master beep-enable (reset turns it on). - Individual beep_mask should be reset to off but for some reason - disabling this bit helps some people not get beeped */ + /* + * Disable master beep-enable (reset turns it on). + * Individual beep_mask should be reset to off but for some + * reason disabling this bit helps some people not get beeped + */ w83781d_write_value(data, W83781D_REG_BEEP_INTS2, 0); } - /* Disable power-on abnormal beep, as advised by the datasheet. - Already done if reset=1. */ + /* + * Disable power-on abnormal beep, as advised by the datasheet. + * Already done if reset=1. + */ if (init && !reset && type != as99127f) { i = w83781d_read_value(data, W83781D_REG_BEEP_CONFIG); w83781d_write_value(data, W83781D_REG_BEEP_CONFIG, i | 0x80); @@ -1368,8 +1427,8 @@ w83781d_init_device(struct device *dev) /* Enable temp2 */ tmp = w83781d_read_value(data, W83781D_REG_TEMP2_CONFIG); if (tmp & 0x01) { - dev_warn(dev, "Enabling temp2, readings " - "might not make sense\n"); + dev_warn(dev, + "Enabling temp2, readings might not make sense\n"); w83781d_write_value(data, W83781D_REG_TEMP2_CONFIG, tmp & 0xfe); } @@ -1379,8 +1438,8 @@ w83781d_init_device(struct device *dev) tmp = w83781d_read_value(data, W83781D_REG_TEMP3_CONFIG); if (tmp & 0x01) { - dev_warn(dev, "Enabling temp3, " - "readings might not make sense\n"); + dev_warn(dev, + "Enabling temp3, readings might not make sense\n"); w83781d_write_value(data, W83781D_REG_TEMP3_CONFIG, tmp & 0xfe); } @@ -1444,7 +1503,7 @@ static struct w83781d_data *w83781d_update_device(struct device *dev) } /* Only PWM2 can be disabled */ data->pwm2_enable = (w83781d_read_value(data, - W83781D_REG_PWMCLK12) & 0x08) >> 3; + W83781D_REG_PWMCLK12) & 0x08) >> 3; } data->temp = w83781d_read_value(data, W83781D_REG_TEMP(1)); @@ -1495,8 +1554,10 @@ static struct w83781d_data *w83781d_update_device(struct device *dev) | (w83781d_read_value(data, W83782D_REG_ALARM2) << 8); } else { - /* No real-time status registers, fall back to - interrupt status registers */ + /* + * No real-time status registers, fall back to + * interrupt status registers + */ data->alarms = w83781d_read_value(data, W83781D_REG_ALARM1) | (w83781d_read_value(data, @@ -1550,8 +1611,10 @@ static struct platform_device *pdev; static unsigned short isa_address = 0x290; -/* I2C devices get this name attribute automatically, but for ISA devices - we must create it by ourselves. */ +/* + * I2C devices get this name attribute automatically, but for ISA devices + * we must create it by ourselves. + */ static ssize_t show_name(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -1581,8 +1644,10 @@ static int w83781d_alias_detect(struct i2c_client *client, u8 chipid) if (w83781d_read_value(isa, W83781D_REG_WCHIPID) != chipid) return 0; /* Chip type doesn't match */ - /* We compare all the limit registers, the config register and the - * interrupt mask registers */ + /* + * We compare all the limit registers, the config register and the + * interrupt mask registers + */ for (i = 0x2b; i <= 0x3d; i++) { if (w83781d_read_value(isa, i) != i2c_smbus_read_byte_data(client, i)) @@ -1663,12 +1728,14 @@ w83781d_write_value_isa(struct w83781d_data *data, u16 reg, u16 value) } } -/* The SMBus locks itself, usually, but nothing may access the Winbond between - bank switches. ISA access must always be locked explicitly! - We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, - would slow down the W83781D access and should not be necessary. - There are some ugly typecasts here, but the good news is - they should - nowhere else be necessary! */ +/* + * The SMBus locks itself, usually, but nothing may access the Winbond between + * bank switches. ISA access must always be locked explicitly! + * We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, + * would slow down the W83781D access and should not be necessary. + * There are some ugly typecasts here, but the good news is - they should + * nowhere else be necessary! + */ static int w83781d_read_value(struct w83781d_data *data, u16 reg) { @@ -1698,7 +1765,7 @@ w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value) return 0; } -static int __devinit +static int w83781d_isa_probe(struct platform_device *pdev) { int err, reg; @@ -1707,17 +1774,16 @@ w83781d_isa_probe(struct platform_device *pdev) /* Reserve the ISA region */ res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start + W83781D_ADDR_REG_OFFSET, 2, - "w83781d")) { - err = -EBUSY; - goto exit; - } + if (!devm_request_region(&pdev->dev, + res->start + W83781D_ADDR_REG_OFFSET, 2, + "w83781d")) + return -EBUSY; + + data = devm_kzalloc(&pdev->dev, sizeof(struct w83781d_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; - data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit_release_region; - } mutex_init(&data->lock); data->isa_addr = res->start; platform_set_drvdata(pdev, data); @@ -1754,27 +1820,19 @@ w83781d_isa_probe(struct platform_device *pdev) return 0; exit_remove_files: - sysfs_remove_group(&pdev->dev.kobj, &w83781d_group); - sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); + w83781d_remove_files(&pdev->dev); device_remove_file(&pdev->dev, &dev_attr_name); - kfree(data); - exit_release_region: - release_region(res->start + W83781D_ADDR_REG_OFFSET, 2); - exit: return err; } -static int __devexit +static int w83781d_isa_remove(struct platform_device *pdev) { struct w83781d_data *data = platform_get_drvdata(pdev); hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&pdev->dev.kobj, &w83781d_group); - sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); + w83781d_remove_files(&pdev->dev); device_remove_file(&pdev->dev, &dev_attr_name); - release_region(data->isa_addr + W83781D_ADDR_REG_OFFSET, 2); - kfree(data); return 0; } @@ -1785,7 +1843,7 @@ static struct platform_driver w83781d_isa_driver = { .name = "w83781d", }, .probe = w83781d_isa_probe, - .remove = __devexit_p(w83781d_isa_remove), + .remove = w83781d_isa_remove, }; /* return 1 if a supported chip is found, 0 otherwise */ @@ -1793,43 +1851,48 @@ static int __init w83781d_isa_found(unsigned short address) { int val, save, found = 0; - - /* We have to request the region in two parts because some - boards declare base+4 to base+7 as a PNP device */ - if (!request_region(address, 4, "w83781d")) { - pr_debug("w83781d: Failed to request low part of region\n"); - return 0; - } - if (!request_region(address + 4, 4, "w83781d")) { - pr_debug("w83781d: Failed to request high part of region\n"); - release_region(address, 4); - return 0; + int port; + + /* + * Some boards declare base+0 to base+7 as a PNP device, some base+4 + * to base+7 and some base+5 to base+6. So we better request each port + * individually for the probing phase. + */ + for (port = address; port < address + W83781D_EXTENT; port++) { + if (!request_region(port, 1, "w83781d")) { + pr_debug("Failed to request port 0x%x\n", port); + goto release; + } } #define REALLY_SLOW_IO - /* We need the timeouts for at least some W83781D-like - chips. But only if we read 'undefined' registers. */ + /* + * We need the timeouts for at least some W83781D-like + * chips. But only if we read 'undefined' registers. + */ val = inb_p(address + 1); if (inb_p(address + 2) != val || inb_p(address + 3) != val || inb_p(address + 7) != val) { - pr_debug("w83781d: Detection failed at step 1\n"); + pr_debug("Detection failed at step %d\n", 1); goto release; } #undef REALLY_SLOW_IO - /* We should be able to change the 7 LSB of the address port. The - MSB (busy flag) should be clear initially, set after the write. */ + /* + * We should be able to change the 7 LSB of the address port. The + * MSB (busy flag) should be clear initially, set after the write. + */ save = inb_p(address + W83781D_ADDR_REG_OFFSET); if (save & 0x80) { - pr_debug("w83781d: Detection failed at step 2\n"); + pr_debug("Detection failed at step %d\n", 2); goto release; } val = ~save & 0x7f; outb_p(val, address + W83781D_ADDR_REG_OFFSET); if (inb_p(address + W83781D_ADDR_REG_OFFSET) != (val | 0x80)) { outb_p(save, address + W83781D_ADDR_REG_OFFSET); - pr_debug("w83781d: Detection failed at step 3\n"); + pr_debug("Detection failed at step %d\n", 3); goto release; } @@ -1837,7 +1900,7 @@ w83781d_isa_found(unsigned short address) outb_p(W83781D_REG_CONFIG, address + W83781D_ADDR_REG_OFFSET); val = inb_p(address + W83781D_DATA_REG_OFFSET); if (val & 0x80) { - pr_debug("w83781d: Detection failed at step 4\n"); + pr_debug("Detection failed at step %d\n", 4); goto release; } outb_p(W83781D_REG_BANK, address + W83781D_ADDR_REG_OFFSET); @@ -1846,19 +1909,19 @@ w83781d_isa_found(unsigned short address) val = inb_p(address + W83781D_DATA_REG_OFFSET); if ((!(save & 0x80) && (val != 0xa3)) || ((save & 0x80) && (val != 0x5c))) { - pr_debug("w83781d: Detection failed at step 5\n"); + pr_debug("Detection failed at step %d\n", 5); goto release; } outb_p(W83781D_REG_I2C_ADDR, address + W83781D_ADDR_REG_OFFSET); val = inb_p(address + W83781D_DATA_REG_OFFSET); if (val < 0x03 || val > 0x77) { /* Not a valid I2C address */ - pr_debug("w83781d: Detection failed at step 6\n"); + pr_debug("Detection failed at step %d\n", 6); goto release; } /* The busy flag should be clear again */ if (inb_p(address + W83781D_ADDR_REG_OFFSET) & 0x80) { - pr_debug("w83781d: Detection failed at step 7\n"); + pr_debug("Detection failed at step %d\n", 7); goto release; } @@ -1873,12 +1936,12 @@ w83781d_isa_found(unsigned short address) found = 1; if (found) - pr_info("w83781d: Found a %s chip at %#x\n", + pr_info("Found a %s chip at %#x\n", val == 0x30 ? "W83782D" : "W83781D", (int)address); release: - release_region(address + 4, 4); - release_region(address, 4); + for (port--; port >= address; port--) + release_region(port, 1); return found; } @@ -1896,21 +1959,19 @@ w83781d_isa_device_add(unsigned short address) pdev = platform_device_alloc("w83781d", address); if (!pdev) { err = -ENOMEM; - printk(KERN_ERR "w83781d: Device allocation failed\n"); + pr_err("Device allocation failed\n"); goto exit; } err = platform_device_add_resources(pdev, &res, 1); if (err) { - printk(KERN_ERR "w83781d: Device resource addition failed " - "(%d)\n", err); + pr_err("Device resource addition failed (%d)\n", err); goto exit_device_put; } err = platform_device_add(pdev); if (err) { - printk(KERN_ERR "w83781d: Device addition failed (%d)\n", - err); + pr_err("Device addition failed (%d)\n", err); goto exit_device_put; } @@ -2007,8 +2068,10 @@ sensors_w83781d_init(void) { int res; - /* We register the ISA device first, so that we can skip the - * registration of an I2C interface to the same device. */ + /* + * We register the ISA device first, so that we can skip the + * registration of an I2C interface to the same device. + */ res = w83781d_isa_register(); if (res) goto exit; diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index 400a88bde27..bdcf2dce5ec 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -1,36 +1,36 @@ /* - w83791d.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - - Copyright (C) 2006-2007 Charles Spirakis <bezaur@gmail.com> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - 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. -*/ + * w83791d.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * + * Copyright (C) 2006-2007 Charles Spirakis <bezaur@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ /* - Supports following chips: - - Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA - w83791d 10 5 5 3 0x71 0x5ca3 yes no - - The w83791d chip appears to be part way between the 83781d and the - 83792d. Thus, this file is derived from both the w83792d.c and - w83781d.c files. - - The w83791g chip is the same as the w83791d but lead-free. -*/ + * Supports following chips: + * + * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA + * w83791d 10 5 5 3 0x71 0x5ca3 yes no + * + * The w83791d chip appears to be part way between the 83781d and the + * 83792d. Thus, this file is derived from both the w83792d.c and + * w83781d.c files. + * + * The w83791g chip is the same as the w83791d but lead-free. + */ #include <linux/module.h> #include <linux/init.h> @@ -41,6 +41,7 @@ #include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/jiffies.h> #define NUMBER_OF_VIN 10 #define NUMBER_OF_FANIN 5 @@ -55,14 +56,14 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, static unsigned short force_subclients[4]; module_param_array(force_subclients, short, NULL, 0); -MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " - "{bus, clientaddr, subclientaddr1, subclientaddr2}"); +MODULE_PARM_DESC(force_subclients, + "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}"); -static int reset; +static bool reset; module_param(reset, bool, 0); MODULE_PARM_DESC(reset, "Set to one to force a hardware chip reset"); -static int init; +static bool init; module_param(init, bool, 0); MODULE_PARM_DESC(init, "Set to one to force extra software initialization"); @@ -198,10 +199,12 @@ static const u8 W83791D_REG_BEEP_CTRL[3] = { #define W83791D_REG_VBAT 0x5D #define W83791D_REG_I2C_ADDR 0x48 -/* The SMBus locks itself. The Winbond W83791D has a bank select register - (index 0x4e), but the driver only accesses registers in bank 0. Since - we don't switch banks, we don't need any special code to handle - locking access between bank switches */ +/* + * The SMBus locks itself. The Winbond W83791D has a bank select register + * (index 0x4e), but the driver only accesses registers in bank 0. Since + * we don't switch banks, we don't need any special code to handle + * locking access between bank switches + */ static inline int w83791d_read(struct i2c_client *client, u8 reg) { return i2c_smbus_read_byte_data(client, reg); @@ -212,21 +215,23 @@ static inline int w83791d_write(struct i2c_client *client, u8 reg, u8 value) return i2c_smbus_write_byte_data(client, reg, value); } -/* The analog voltage inputs have 16mV LSB. Since the sysfs output is - in mV as would be measured on the chip input pin, need to just - multiply/divide by 16 to translate from/to register values. */ -#define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8) / 16), 0, 255)) +/* + * The analog voltage inputs have 16mV LSB. Since the sysfs output is + * in mV as would be measured on the chip input pin, need to just + * multiply/divide by 16 to translate from/to register values. + */ +#define IN_TO_REG(val) (clamp_val((((val) + 8) / 16), 0, 255)) #define IN_FROM_REG(val) ((val) * 16) static 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); + rpm = clamp_val(rpm, 1, 1000000); + return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254); } -#define FAN_FROM_REG(val,div) ((val) == 0 ? -1 : \ +#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : \ ((val) == 255 ? 0 : \ 1350000 / ((val) * (div)))) @@ -237,10 +242,12 @@ static u8 fan_to_reg(long rpm, int div) (val) < 0 ? ((val) - 500) / 1000 : \ ((val) + 500) / 1000) -/* for temp2 and temp3 which are 9-bit resolution, LSB = 0.5 degree Celsius - Assumes the top 8 bits are the integral amount and the bottom 8 bits - are the fractional amount. Since we only have 0.5 degree resolution, - the bottom 7 bits will always be zero */ +/* + * for temp2 and temp3 which are 9-bit resolution, LSB = 0.5 degree Celsius + * Assumes the top 8 bits are the integral amount and the bottom 8 bits + * are the fractional amount. Since we only have 0.5 degree resolution, + * the bottom 7 bits will always be zero + */ #define TEMP23_FROM_REG(val) ((val) / 128 * 500) #define TEMP23_TO_REG(val) ((val) <= -128000 ? 0x8000 : \ (val) >= 127500 ? 0x7F80 : \ @@ -253,8 +260,7 @@ static u8 fan_to_reg(long rpm, int div) ((val) + 500) / 1000) /* for thermal cruise temp tolerance, 4-bits, LSB = 1 degree Celsius */ -#define TOL_TEMP_TO_REG(val) ((val) < 0 ? 0 : \ - (val) >= 15000 ? 15 : \ +#define TOL_TEMP_TO_REG(val) ((val) >= 15000 ? 15 : \ ((val) + 500) / 1000) #define BEEP_MASK_TO_REG(val) ((val) & 0xffffff) @@ -267,7 +273,7 @@ static u8 div_to_reg(int nr, long val) int i; /* fan divisors max out at 128 */ - val = SENSORS_LIMIT(val, 1, 128) >> 1; + val = clamp_val(val, 1, 128) >> 1; for (i = 0; i < 7; i++) { if (val == 0) break; @@ -300,17 +306,19 @@ struct w83791d_data { s8 temp1[3]; /* current, over, thyst */ s16 temp_add[2][3]; /* fixed point value. Top 8 bits are the - integral part, bottom 8 bits are the - fractional part. We only use the top - 9 bits as the resolution is only - to the 0.5 degree C... - two sensors with three values - (cur, over, hyst) */ + * integral part, bottom 8 bits are the + * fractional part. We only use the top + * 9 bits as the resolution is only + * to the 0.5 degree C... + * two sensors with three values + * (cur, over, hyst) + */ /* PWMs */ u8 pwm[5]; /* pwm duty cycle */ u8 pwm_enable[3]; /* pwm enable status for fan 1-3 - (fan 4-5 only support manual mode) */ + * (fan 4-5 only support manual mode) + */ u8 temp_target[3]; /* pwm 1-3 target temperature */ u8 temp_tolerance[3]; /* pwm 1-3 temperature tolerance */ @@ -329,8 +337,8 @@ static int w83791d_detect(struct i2c_client *client, struct i2c_board_info *info); static int w83791d_remove(struct i2c_client *client); -static int w83791d_read(struct i2c_client *client, u8 register); -static int w83791d_write(struct i2c_client *client, u8 register, u8 value); +static int w83791d_read(struct i2c_client *client, u8 reg); +static int w83791d_write(struct i2c_client *client, u8 reg, u8 value); static struct w83791d_data *w83791d_update_device(struct device *dev); #ifdef DEBUG @@ -366,7 +374,7 @@ static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ to_sensor_dev_attr(attr); \ struct w83791d_data *data = w83791d_update_device(dev); \ int nr = sensor_attr->index; \ - return sprintf(buf,"%d\n", IN_FROM_REG(data->reg[nr])); \ + return sprintf(buf, "%d\n", IN_FROM_REG(data->reg[nr])); \ } show_in_reg(in); @@ -382,9 +390,11 @@ static ssize_t store_in_##reg(struct device *dev, \ to_sensor_dev_attr(attr); \ struct i2c_client *client = to_i2c_client(dev); \ struct w83791d_data *data = i2c_get_clientdata(client); \ - unsigned long val = simple_strtoul(buf, NULL, 10); \ int nr = sensor_attr->index; \ - \ + unsigned long val; \ + int err = kstrtoul(buf, 10, &val); \ + if (err) \ + return err; \ mutex_lock(&data->update_lock); \ data->in_##reg[nr] = IN_TO_REG(val); \ w83791d_write(client, W83791D_REG_IN_##REG[nr], data->in_##reg[nr]); \ @@ -455,7 +465,14 @@ static ssize_t store_beep(struct device *dev, struct device_attribute *attr, struct w83791d_data *data = i2c_get_clientdata(client); int bitnr = sensor_attr->index; int bytenr = bitnr / 8; - long val = simple_strtol(buf, NULL, 10) ? 1 : 0; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + val = val ? 1 : 0; mutex_lock(&data->update_lock); @@ -485,8 +502,10 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); } -/* Note: The bitmask for the beep enable/disable is different than - the bitmask for the alarm. */ +/* + * Note: The bitmask for the beep enable/disable is different than + * the bitmask for the alarm. + */ static struct sensor_device_attribute sda_in_beep[] = { SENSOR_ATTR(in0_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 0), SENSOR_ATTR(in1_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 13), @@ -521,7 +540,7 @@ static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ to_sensor_dev_attr(attr); \ struct w83791d_data *data = w83791d_update_device(dev); \ int nr = sensor_attr->index; \ - return sprintf(buf,"%d\n", \ + return sprintf(buf, "%d\n", \ FAN_FROM_REG(data->reg[nr], DIV_FROM_REG(data->fan_div[nr]))); \ } @@ -534,8 +553,13 @@ static ssize_t store_fan_min(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); struct i2c_client *client = to_i2c_client(dev); struct w83791d_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); int nr = sensor_attr->index; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->fan_min[nr] = fan_to_reg(val, DIV_FROM_REG(data->fan_div[nr])); @@ -554,10 +578,12 @@ static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%u\n", DIV_FROM_REG(data->fan_div[nr])); } -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan divisor. This follows the principle of - least suprise; the user doesn't expect the fan minimum to change just - because the divisor changed. */ +/* + * Note: we save and restore the fan minimum here, because its value is + * determined in part by the fan divisor. This follows the principle of + * least surprise; the user doesn't expect the fan minimum to change just + * because the divisor changed. + */ static ssize_t store_fan_div(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -572,12 +598,18 @@ static ssize_t store_fan_div(struct device *dev, struct device_attribute *attr, int indx = 0; u8 keep_mask = 0; u8 new_shift = 0; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; /* Save fan_min */ min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])); mutex_lock(&data->update_lock); - data->fan_div[nr] = div_to_reg(nr, simple_strtoul(buf, NULL, 10)); + data->fan_div[nr] = div_to_reg(nr, val); switch (nr) { case 0: @@ -711,11 +743,11 @@ static ssize_t store_pwm(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; unsigned long val; - if (strict_strtoul(buf, 10, &val)) + if (kstrtoul(buf, 10, &val)) return -EINVAL; mutex_lock(&data->update_lock); - data->pwm[nr] = SENSORS_LIMIT(val, 0, 255); + data->pwm[nr] = clamp_val(val, 0, 255); w83791d_write(client, W83791D_REG_PWM[nr], data->pwm[nr]); mutex_unlock(&data->update_lock); return count; @@ -756,7 +788,7 @@ static ssize_t store_pwmenable(struct device *dev, u8 val_shift = 0; u8 keep_mask = 0; - int ret = strict_strtoul(buf, 10, &val); + int ret = kstrtoul(buf, 10, &val); if (ret || val < 1 || val > 3) return -EINVAL; @@ -816,10 +848,10 @@ static ssize_t store_temp_target(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct w83791d_data *data = i2c_get_clientdata(client); int nr = sensor_attr->index; - unsigned long val; + long val; u8 target_mask; - if (strict_strtoul(buf, 10, &val)) + if (kstrtol(buf, 10, &val)) return -EINVAL; mutex_lock(&data->update_lock); @@ -863,7 +895,7 @@ static ssize_t store_temp_tolerance(struct device *dev, u8 val_shift = 0; u8 keep_mask = 0; - if (strict_strtoul(buf, 10, &val)) + if (kstrtoul(buf, 10, &val)) return -EINVAL; switch (nr) { @@ -918,8 +950,13 @@ static ssize_t store_temp1(struct device *dev, struct device_attribute *devattr, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct w83791d_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); int nr = attr->index; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); data->temp1[nr] = TEMP1_TO_REG(val); @@ -946,10 +983,15 @@ static ssize_t store_temp23(struct device *dev, struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); struct i2c_client *client = to_i2c_client(dev); struct w83791d_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; int nr = attr->nr; int index = attr->index; + err = kstrtol(buf, 10, &val); + if (err) + return err; + mutex_lock(&data->update_lock); data->temp_add[nr][index] = TEMP23_TO_REG(val); w83791d_write(client, W83791D_REG_TEMP_ADD[nr][index * 2], @@ -985,8 +1027,10 @@ static struct sensor_device_attribute_2 sda_temp_max_hyst[] = { show_temp23, store_temp23, 1, 2), }; -/* Note: The bitmask for the beep enable/disable is different than - the bitmask for the alarm. */ +/* + * Note: The bitmask for the beep enable/disable is different than + * the bitmask for the alarm. + */ static struct sensor_device_attribute sda_temp_beep[] = { SENSOR_ATTR(temp1_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 4), SENSOR_ATTR(temp2_beep, S_IWUSR | S_IRUGO, show_beep, store_beep, 5), @@ -999,7 +1043,7 @@ static struct sensor_device_attribute sda_temp_alarm[] = { SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13), }; -/* get reatime status of all sensors items: voltage, temp, fan */ +/* get realtime status of all sensors items: voltage, temp, fan */ static ssize_t show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1035,13 +1079,20 @@ static ssize_t store_beep_mask(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct w83791d_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); int i; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - /* The beep_enable state overrides any enabling request from - the masks */ + /* + * The beep_enable state overrides any enabling request from + * the masks + */ data->beep_mask = BEEP_MASK_TO_REG(val) & ~GLOBAL_BEEP_ENABLE_MASK; data->beep_mask |= (data->beep_enable << GLOBAL_BEEP_ENABLE_SHIFT); @@ -1063,7 +1114,12 @@ static ssize_t store_beep_enable(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct w83791d_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); @@ -1073,8 +1129,10 @@ static ssize_t store_beep_enable(struct device *dev, data->beep_mask &= ~GLOBAL_BEEP_ENABLE_MASK; data->beep_mask |= (data->beep_enable << GLOBAL_BEEP_ENABLE_SHIFT); - /* The global control is in the second beep control register - so only need to update that register */ + /* + * The global control is in the second beep control register + * so only need to update that register + */ val = (data->beep_mask >> 8) & 0xff; w83791d_write(client, W83791D_REG_BEEP_CTRL[1], val); @@ -1113,36 +1171,44 @@ static ssize_t store_vrm_reg(struct device *dev, const char *buf, size_t count) { struct w83791d_data *data = dev_get_drvdata(dev); + unsigned long val; + int err; + + /* + * No lock needed as vrm is internal to the driver + * (not read from a chip register) and so is not + * updated in w83791d_update_device() + */ - /* No lock needed as vrm is internal to the driver - (not read from a chip register) and so is not - updated in w83791d_update_device() */ - data->vrm = simple_strtoul(buf, NULL, 10); + err = kstrtoul(buf, 10, &val); + if (err) + return err; + data->vrm = val; return count; } static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); #define IN_UNIT_ATTRS(X) \ - &sda_in_input[X].dev_attr.attr, \ - &sda_in_min[X].dev_attr.attr, \ - &sda_in_max[X].dev_attr.attr, \ - &sda_in_beep[X].dev_attr.attr, \ + &sda_in_input[X].dev_attr.attr, \ + &sda_in_min[X].dev_attr.attr, \ + &sda_in_max[X].dev_attr.attr, \ + &sda_in_beep[X].dev_attr.attr, \ &sda_in_alarm[X].dev_attr.attr #define FAN_UNIT_ATTRS(X) \ - &sda_fan_input[X].dev_attr.attr, \ - &sda_fan_min[X].dev_attr.attr, \ - &sda_fan_div[X].dev_attr.attr, \ - &sda_fan_beep[X].dev_attr.attr, \ + &sda_fan_input[X].dev_attr.attr, \ + &sda_fan_min[X].dev_attr.attr, \ + &sda_fan_div[X].dev_attr.attr, \ + &sda_fan_beep[X].dev_attr.attr, \ &sda_fan_alarm[X].dev_attr.attr #define TEMP_UNIT_ATTRS(X) \ - &sda_temp_input[X].dev_attr.attr, \ - &sda_temp_max[X].dev_attr.attr, \ - &sda_temp_max_hyst[X].dev_attr.attr, \ - &sda_temp_beep[X].dev_attr.attr, \ + &sda_temp_input[X].dev_attr.attr, \ + &sda_temp_max[X].dev_attr.attr, \ + &sda_temp_max_hyst[X].dev_attr.attr, \ + &sda_temp_beep[X].dev_attr.attr, \ &sda_temp_alarm[X].dev_attr.attr static struct attribute *w83791d_attributes[] = { @@ -1186,9 +1252,11 @@ static const struct attribute_group w83791d_group = { .attrs = w83791d_attributes, }; -/* Separate group of attributes for fan/pwm 4-5. Their pins can also be - in use for GPIO in which case their sysfs-interface should not be made - available */ +/* + * Separate group of attributes for fan/pwm 4-5. Their pins can also be + * in use for GPIO in which case their sysfs-interface should not be made + * available + */ static struct attribute *w83791d_attributes_fanpwm45[] = { FAN_UNIT_ATTRS(3), FAN_UNIT_ATTRS(4), @@ -1228,9 +1296,8 @@ static int w83791d_detect_subclients(struct i2c_client *client) } val = w83791d_read(client, W83791D_REG_I2C_SUBADDR); - if (!(val & 0x08)) { + if (!(val & 0x08)) data->lm75[0] = i2c_new_dummy(adapter, 0x48 + (val & 0x7)); - } if (!(val & 0x80)) { if ((data->lm75[0] != NULL) && ((val & 0x7) == ((val >> 4) & 0x7))) { @@ -1265,9 +1332,8 @@ static int w83791d_detect(struct i2c_client *client, int val1, val2; unsigned short address = client->addr; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - } if (w83791d_read(client, W83791D_REG_CONFIG) & 0x80) return -ENODEV; @@ -1277,12 +1343,14 @@ static int w83791d_detect(struct i2c_client *client, /* Check for Winbond ID if in bank 0 */ if (!(val1 & 0x07)) { if ((!(val1 & 0x80) && val2 != 0xa3) || - ( (val1 & 0x80) && val2 != 0x5c)) { + ((val1 & 0x80) && val2 != 0x5c)) { return -ENODEV; } } - /* If Winbond chip, address of chip and W83791D_REG_I2C_ADDR - should match */ + /* + * If Winbond chip, address of chip and W83791D_REG_I2C_ADDR + * should match + */ if (w83791d_read(client, W83791D_REG_I2C_ADDR) != address) return -ENODEV; @@ -1316,30 +1384,31 @@ static int w83791d_probe(struct i2c_client *client, (val1 >> 5) & 0x07, (val1 >> 1) & 0x0f, val1); #endif - data = kzalloc(sizeof(struct w83791d_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto error0; - } + data = devm_kzalloc(&client->dev, sizeof(struct w83791d_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); mutex_init(&data->update_lock); err = w83791d_detect_subclients(client); if (err) - goto error1; + return err; /* Initialize the chip */ w83791d_init_client(client); - /* If the fan_div is changed, make sure there is a rational - fan_min in place */ - for (i = 0; i < NUMBER_OF_FANIN; i++) { + /* + * If the fan_div is changed, make sure there is a rational + * fan_min in place + */ + for (i = 0; i < NUMBER_OF_FANIN; i++) data->fan_min[i] = w83791d_read(client, W83791D_REG_FAN_MIN[i]); - } /* Register sysfs hooks */ - if ((err = sysfs_create_group(&client->dev.kobj, &w83791d_group))) + err = sysfs_create_group(&client->dev.kobj, &w83791d_group); + if (err) goto error3; /* Check if pins of fan/pwm 4-5 are in use as GPIO */ @@ -1370,9 +1439,6 @@ error3: i2c_unregister_device(data->lm75[0]); if (data->lm75[1] != NULL) i2c_unregister_device(data->lm75[1]); -error1: - kfree(data); -error0: return err; } @@ -1388,7 +1454,6 @@ static int w83791d_remove(struct i2c_client *client) if (data->lm75[1] != NULL) i2c_unregister_device(data->lm75[1]); - kfree(data); return 0; } @@ -1398,19 +1463,20 @@ static void w83791d_init_client(struct i2c_client *client) u8 tmp; u8 old_beep; - /* The difference between reset and init is that reset - does a hard reset of the chip via index 0x40, bit 7, - but init simply forces certain registers to have "sane" - values. The hope is that the BIOS has done the right - thing (which is why the default is reset=0, init=0), - but if not, reset is the hard hammer and init - is the soft mallet both of which are trying to whack - things into place... - NOTE: The data sheet makes a distinction between - "power on defaults" and "reset by MR". As far as I can tell, - the hard reset puts everything into a power-on state so I'm - not sure what "reset by MR" means or how it can happen. - */ + /* + * The difference between reset and init is that reset + * does a hard reset of the chip via index 0x40, bit 7, + * but init simply forces certain registers to have "sane" + * values. The hope is that the BIOS has done the right + * thing (which is why the default is reset=0, init=0), + * but if not, reset is the hard hammer and init + * is the soft mallet both of which are trying to whack + * things into place... + * NOTE: The data sheet makes a distinction between + * "power on defaults" and "reset by MR". As far as I can tell, + * the hard reset puts everything into a power-on state so I'm + * not sure what "reset by MR" means or how it can happen. + */ if (reset || init) { /* keep some BIOS settings when we... */ old_beep = w83791d_read(client, W83791D_REG_BEEP_CONFIG); @@ -1494,8 +1560,10 @@ static struct w83791d_data *w83791d_update_device(struct device *dev) data->fan_div[3] = reg_array_tmp[2] & 0x07; data->fan_div[4] = (reg_array_tmp[2] >> 4) & 0x07; - /* The fan divisor for fans 0-2 get bit 2 from - bits 5-7 respectively of vbat register */ + /* + * The fan divisor for fans 0-2 get bit 2 from + * bits 5-7 respectively of vbat register + */ vbat_reg = w83791d_read(client, W83791D_REG_VBAT); for (i = 0; i < 3; i++) data->fan_div[i] |= (vbat_reg >> (3 + i)) & 0x04; @@ -1601,12 +1669,13 @@ static void w83791d_print_debug(struct w83791d_data *data, struct device *dev) dev_dbg(dev, "fan_div[%d] is: 0x%02x\n", i, data->fan_div[i]); } - /* temperature math is signed, but only print out the - bits that matter */ + /* + * temperature math is signed, but only print out the + * bits that matter + */ dev_dbg(dev, "%d set of Temperatures: ===>\n", NUMBER_OF_TEMPIN); - for (i = 0; i < 3; i++) { + for (i = 0; i < 3; i++) dev_dbg(dev, "temp1[%d] is: 0x%02x\n", i, (u8) data->temp1[i]); - } for (i = 0; i < 2; i++) { for (j = 0; j < 3; j++) { dev_dbg(dev, "temp_add[%d][%d] is: 0x%04x\n", i, j, @@ -1625,19 +1694,8 @@ static void w83791d_print_debug(struct w83791d_data *data, struct device *dev) } #endif -static int __init sensors_w83791d_init(void) -{ - return i2c_add_driver(&w83791d_driver); -} - -static void __exit sensors_w83791d_exit(void) -{ - i2c_del_driver(&w83791d_driver); -} +module_i2c_driver(w83791d_driver); MODULE_AUTHOR("Charles Spirakis <bezaur@gmail.com>"); MODULE_DESCRIPTION("W83791D driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_w83791d_init); -module_exit(sensors_w83791d_exit); diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index 679718e6b01..4068db4d958 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -1,39 +1,39 @@ /* - w83792d.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (C) 2004, 2005 Winbond Electronics Corp. - Chunhao Huang <DZShen@Winbond.com.tw>, - Rudolf Marek <r.marek@assembler.cz> - - 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. - - Note: - 1. This driver is only for 2.6 kernel, 2.4 kernel need a different driver. - 2. This driver is only for Winbond W83792D C version device, there - are also some motherboards with B version W83792D device. The - calculation method to in6-in7(measured value, limits) is a little - different between C and B version. C or B version can be identified - by CR[0x49h]. -*/ + * w83792d.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (C) 2004, 2005 Winbond Electronics Corp. + * Shane Huang, + * Rudolf Marek <r.marek@assembler.cz> + * + * 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. + * + * Note: + * 1. This driver is only for 2.6 kernel, 2.4 kernel need a different driver. + * 2. This driver is only for Winbond W83792D C version device, there + * are also some motherboards with B version W83792D device. The + * calculation method to in6-in7(measured value, limits) is a little + * different between C and B version. C or B version can be identified + * by CR[0x49h]. + */ /* - Supports following chips: - - Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA - w83792d 9 7 7 3 0x7a 0x5ca3 yes no -*/ + * Supports following chips: + * + * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA + * w83792d 9 7 7 3 0x7a 0x5ca3 yes no + */ #include <linux/module.h> #include <linux/init.h> @@ -44,6 +44,7 @@ #include <linux/err.h> #include <linux/mutex.h> #include <linux/sysfs.h> +#include <linux/jiffies.h> /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, @@ -53,10 +54,10 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, static unsigned short force_subclients[4]; module_param_array(force_subclients, short, NULL, 0); -MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " - "{bus, clientaddr, subclientaddr1, subclientaddr2}"); +MODULE_PARM_DESC(force_subclients, + "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}"); -static int init; +static bool init; module_param(init, bool, 0); MODULE_PARM_DESC(init, "Set to one to force chip initialization"); @@ -218,39 +219,40 @@ static const u8 W83792D_REG_LEVELS[3][4] = { #define W83792D_REG_VBAT 0x5D #define W83792D_REG_I2C_ADDR 0x48 -/* Conversions. Rounding and limit checking is only done on the TO_REG - variants. Note that you should be a bit careful with which arguments - these macros are called: arguments may be evaluated more than once. - Fixing this is just not worth it. */ -#define IN_FROM_REG(nr,val) (((nr)<=1)?(val*2): \ - ((((nr)==6)||((nr)==7))?(val*6):(val*4))) -#define IN_TO_REG(nr,val) (((nr)<=1)?(val/2): \ - ((((nr)==6)||((nr)==7))?(val/6):(val/4))) +/* + * Conversions. Rounding and limit checking is only done on the TO_REG + * variants. Note that you should be a bit careful with which arguments + * these macros are called: arguments may be evaluated more than once. + * Fixing this is just not worth it. + */ +#define IN_FROM_REG(nr, val) (((nr) <= 1) ? ((val) * 2) : \ + ((((nr) == 6) || ((nr) == 7)) ? ((val) * 6) : ((val) * 4))) +#define IN_TO_REG(nr, val) (((nr) <= 1) ? ((val) / 2) : \ + ((((nr) == 6) || ((nr) == 7)) ? ((val) / 6) : ((val) / 4))) 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); + rpm = clamp_val(rpm, 1, 1000000); + return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254); } -#define FAN_FROM_REG(val,div) ((val) == 0 ? -1 : \ +#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : \ ((val) == 255 ? 0 : \ 1350000 / ((val) * (div)))) /* for temp1 */ -#define TEMP1_TO_REG(val) (SENSORS_LIMIT(((val) < 0 ? (val)+0x100*1000 \ - : (val)) / 1000, 0, 0xff)) +#define TEMP1_TO_REG(val) (clamp_val(((val) < 0 ? (val) + 0x100 * 1000 \ + : (val)) / 1000, 0, 0xff)) #define TEMP1_FROM_REG(val) (((val) & 0x80 ? (val)-0x100 : (val)) * 1000) -/* for temp2 and temp3, because they need addtional resolution */ +/* for temp2 and temp3, because they need additional resolution */ #define TEMP_ADD_FROM_REG(val1, val2) \ ((((val1) & 0x80 ? (val1)-0x100 \ : (val1)) * 1000) + ((val2 & 0x80) ? 500 : 0)) #define TEMP_ADD_TO_REG_HIGH(val) \ - (SENSORS_LIMIT(((val) < 0 ? (val)+0x100*1000 \ - : (val)) / 1000, 0, 0xff)) + (clamp_val(((val) < 0 ? (val) + 0x100 * 1000 : (val)) / 1000, 0, 0xff)) #define TEMP_ADD_TO_REG_LOW(val) ((val%1000) ? 0x80 : 0x00) #define DIV_FROM_REG(val) (1 << (val)) @@ -259,13 +261,13 @@ static inline u8 DIV_TO_REG(long val) { int i; - val = SENSORS_LIMIT(val, 1, 128) >> 1; + val = clamp_val(val, 1, 128) >> 1; for (i = 0; i < 7; i++) { if (val == 0) break; val >>= 1; } - return ((u8) i); + return (u8)i; } struct w83792d_data { @@ -287,12 +289,13 @@ struct w83792d_data { u8 temp1[3]; /* current, over, thyst */ u8 temp_add[2][6]; /* Register value */ u8 fan_div[7]; /* Register encoding, shifted right */ - u8 pwm[7]; /* We only consider the first 3 set of pwm, - although 792 chip has 7 set of pwm. */ + u8 pwm[7]; /* + * We only consider the first 3 set of pwm, + * although 792 chip has 7 set of pwm. + */ u8 pwmenable[3]; u32 alarms; /* realtime status register encoding,combined */ u8 chassis; /* Chassis status */ - u8 chassis_clear; /* CLR_CHS, clear chassis intrusion detection */ u8 thermal_cruise[3]; /* Smart FanI: Fan1,2,3 target value */ u8 tolerance[3]; /* Fan1,2,3 tolerance(Smart Fan I/II) */ u8 sf2_points[3][4]; /* Smart FanII: Fan1,2,3 temperature points */ @@ -333,12 +336,14 @@ static struct i2c_driver w83792d_driver = { static inline long in_count_from_reg(int nr, struct w83792d_data *data) { /* in7 and in8 do not have low bits, but the formula still works */ - return ((data->in[nr] << 2) | ((data->low_bits >> (2 * nr)) & 0x03)); + return (data->in[nr] << 2) | ((data->low_bits >> (2 * nr)) & 0x03); } -/* The SMBus locks itself. The Winbond W83792D chip has a bank register, - but the driver only accesses registers in bank 0, so we don't have - to switch banks and lock access between switches. */ +/* + * The SMBus locks itself. The Winbond W83792D chip has a bank register, + * but the driver only accesses registers in bank 0, so we don't have + * to switch banks and lock access between switches. + */ static inline int w83792d_read_value(struct i2c_client *client, u8 reg) { return i2c_smbus_read_byte_data(client, reg); @@ -357,37 +362,43 @@ static ssize_t show_in(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct w83792d_data *data = w83792d_update_device(dev); - return sprintf(buf,"%ld\n", IN_FROM_REG(nr,(in_count_from_reg(nr, data)))); + return sprintf(buf, "%ld\n", + IN_FROM_REG(nr, in_count_from_reg(nr, data))); } #define show_in_reg(reg) \ static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + struct sensor_device_attribute *sensor_attr \ + = to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ struct w83792d_data *data = w83792d_update_device(dev); \ - return sprintf(buf,"%ld\n", (long)(IN_FROM_REG(nr, (data->reg[nr])*4))); \ + return sprintf(buf, "%ld\n", \ + (long)(IN_FROM_REG(nr, data->reg[nr]) * 4)); \ } show_in_reg(in_min); show_in_reg(in_max); #define store_in_reg(REG, reg) \ -static ssize_t store_in_##reg (struct device *dev, \ +static ssize_t store_in_##reg(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t count) \ { \ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + struct sensor_device_attribute *sensor_attr \ + = to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ struct i2c_client *client = to_i2c_client(dev); \ struct w83792d_data *data = i2c_get_clientdata(client); \ - u32 val; \ - \ - val = simple_strtoul(buf, NULL, 10); \ + unsigned long val; \ + int err = kstrtoul(buf, 10, &val); \ + if (err) \ + return err; \ mutex_lock(&data->update_lock); \ - data->in_##reg[nr] = SENSORS_LIMIT(IN_TO_REG(nr, val)/4, 0, 255); \ - w83792d_write_value(client, W83792D_REG_IN_##REG[nr], data->in_##reg[nr]); \ + data->in_##reg[nr] = clamp_val(IN_TO_REG(nr, val) / 4, 0, 255); \ + w83792d_write_value(client, W83792D_REG_IN_##REG[nr], \ + data->in_##reg[nr]); \ mutex_unlock(&data->update_lock); \ \ return count; \ @@ -396,13 +407,14 @@ store_in_reg(MIN, min); store_in_reg(MAX, max); #define show_fan_reg(reg) \ -static ssize_t show_##reg (struct device *dev, struct device_attribute *attr, \ +static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + struct sensor_device_attribute *sensor_attr \ + = to_sensor_dev_attr(attr); \ int nr = sensor_attr->index - 1; \ struct w83792d_data *data = w83792d_update_device(dev); \ - return sprintf(buf,"%d\n", \ + return sprintf(buf, "%d\n", \ FAN_FROM_REG(data->reg[nr], DIV_FROM_REG(data->fan_div[nr]))); \ } @@ -417,9 +429,13 @@ store_fan_min(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index - 1; struct i2c_client *client = to_i2c_client(dev); struct w83792d_data *data = i2c_get_clientdata(client); - u32 val; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; - val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); w83792d_write_value(client, W83792D_REG_FAN_MIN[nr], @@ -439,10 +455,12 @@ show_fan_div(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%u\n", DIV_FROM_REG(data->fan_div[nr - 1])); } -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan divisor. This follows the principle of - least surprise; the user doesn't expect the fan minimum to change just - because the divisor changed. */ +/* + * Note: we save and restore the fan minimum here, because its value is + * determined in part by the fan divisor. This follows the principle of + * least surprise; the user doesn't expect the fan minimum to change just + * because the divisor changed. + */ static ssize_t store_fan_div(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -455,13 +473,19 @@ store_fan_div(struct device *dev, struct device_attribute *attr, /*u8 reg;*/ u8 fan_div_reg = 0; u8 tmp_fan_div; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; /* Save fan_min */ mutex_lock(&data->update_lock); min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])); - data->fan_div[nr] = DIV_TO_REG(simple_strtoul(buf, NULL, 10)); + data->fan_div[nr] = DIV_TO_REG(val); fan_div_reg = w83792d_read_value(client, W83792D_REG_FAN_DIV[nr >> 1]); fan_div_reg &= (nr & 0x01) ? 0x8f : 0xf8; @@ -496,9 +520,13 @@ static ssize_t store_temp1(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct w83792d_data *data = i2c_get_clientdata(client); - s32 val; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; - val = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); data->temp1[nr] = TEMP1_TO_REG(val); w83792d_write_value(client, W83792D_REG_TEMP1[nr], @@ -513,11 +541,12 @@ static ssize_t store_temp1(struct device *dev, struct device_attribute *attr, static ssize_t show_temp23(struct device *dev, struct device_attribute *attr, char *buf) { - struct sensor_device_attribute_2 *sensor_attr = to_sensor_dev_attr_2(attr); + struct sensor_device_attribute_2 *sensor_attr + = to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr; int index = sensor_attr->index; struct w83792d_data *data = w83792d_update_device(dev); - return sprintf(buf,"%ld\n", + return sprintf(buf, "%ld\n", (long)TEMP_ADD_FROM_REG(data->temp_add[nr][index], data->temp_add[nr][index+1])); } @@ -525,14 +554,19 @@ static ssize_t show_temp23(struct device *dev, struct device_attribute *attr, static ssize_t store_temp23(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); + 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 w83792d_data *data = i2c_get_clientdata(client); - s32 val; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; - val = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); data->temp_add[nr][index] = TEMP_ADD_TO_REG_HIGH(val); data->temp_add[nr][index+1] = TEMP_ADD_TO_REG_LOW(val); @@ -545,7 +579,7 @@ static ssize_t store_temp23(struct device *dev, struct device_attribute *attr, return count; } -/* get reatime status of all sensors items: voltage, temp, fan */ +/* get realtime status of all sensors items: voltage, temp, fan */ static ssize_t show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) { @@ -604,7 +638,13 @@ store_pwm(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct w83792d_data *data = i2c_get_clientdata(client); - u8 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 255) >> 4; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + val = clamp_val(val, 0, 255) >> 4; mutex_lock(&data->update_lock); val |= w83792d_read_value(client, W83792D_REG_PWM[nr]) & 0xf0; @@ -623,10 +663,14 @@ store_pwmenable(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index - 1; struct i2c_client *client = to_i2c_client(dev); struct w83792d_data *data = i2c_get_clientdata(client); - u32 val; u8 fan_cfg_tmp, cfg1_tmp, cfg2_tmp, cfg3_tmp, cfg4_tmp; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; - val = simple_strtoul(buf, NULL, 10); if (val < 1 || val > 3) return -EINVAL; @@ -645,7 +689,7 @@ store_pwmenable(struct device *dev, struct device_attribute *attr, cfg1_tmp = data->pwmenable[0]; cfg2_tmp = (data->pwmenable[1]) << 2; cfg3_tmp = (data->pwmenable[2]) << 4; - cfg4_tmp = w83792d_read_value(client,W83792D_REG_FAN_CFG) & 0xc0; + cfg4_tmp = w83792d_read_value(client, W83792D_REG_FAN_CFG) & 0xc0; fan_cfg_tmp = ((cfg4_tmp | cfg3_tmp) | cfg2_tmp) | cfg1_tmp; w83792d_write_value(client, W83792D_REG_FAN_CFG, fan_cfg_tmp); mutex_unlock(&data->update_lock); @@ -671,10 +715,13 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct w83792d_data *data = i2c_get_clientdata(client); - u32 val; + unsigned long val; + int err; - val = simple_strtoul(buf, NULL, 10); - if (val != 0 && val != 1) + err = kstrtoul(buf, 10, &val); + if (err) + return err; + if (val > 1) return -EINVAL; mutex_lock(&data->update_lock); @@ -691,7 +738,7 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr, } static ssize_t -show_regs_chassis(struct device *dev, struct device_attribute *attr, +show_chassis_clear(struct device *dev, struct device_attribute *attr, char *buf) { struct w83792d_data *data = w83792d_update_device(dev); @@ -699,28 +746,21 @@ show_regs_chassis(struct device *dev, struct device_attribute *attr, } static ssize_t -show_chassis_clear(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83792d_data *data = w83792d_update_device(dev); - return sprintf(buf, "%d\n", data->chassis_clear); -} - -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 w83792d_data *data = i2c_get_clientdata(client); - u32 val; - u8 temp1 = 0, temp2 = 0; + unsigned long val; + u8 reg; + + if (kstrtoul(buf, 10, &val) || val != 0) + return -EINVAL; - val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); - data->chassis_clear = SENSORS_LIMIT(val, 0 ,1); - temp1 = ((data->chassis_clear) << 7) & 0x80; - temp2 = w83792d_read_value(client, - W83792D_REG_CHASSIS_CLR) & 0x7f; - w83792d_write_value(client, W83792D_REG_CHASSIS_CLR, temp1 | temp2); + reg = w83792d_read_value(client, W83792D_REG_CHASSIS_CLR); + w83792d_write_value(client, W83792D_REG_CHASSIS_CLR, reg | 0x80); + data->valid = 0; /* Force cache refresh */ mutex_unlock(&data->update_lock); return count; @@ -745,15 +785,20 @@ store_thermal_cruise(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index - 1; struct i2c_client *client = to_i2c_client(dev); struct w83792d_data *data = i2c_get_clientdata(client); - u32 val; - u8 target_tmp=0, target_mask=0; + u8 target_tmp = 0, target_mask = 0; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; - val = simple_strtoul(buf, NULL, 10); target_tmp = val; target_tmp = target_tmp & 0x7f; mutex_lock(&data->update_lock); - target_mask = w83792d_read_value(client, W83792D_REG_THERMAL[nr]) & 0x80; - data->thermal_cruise[nr] = SENSORS_LIMIT(target_tmp, 0, 255); + target_mask = w83792d_read_value(client, + W83792D_REG_THERMAL[nr]) & 0x80; + data->thermal_cruise[nr] = clamp_val(target_tmp, 0, 255); w83792d_write_value(client, W83792D_REG_THERMAL[nr], (data->thermal_cruise[nr]) | target_mask); mutex_unlock(&data->update_lock); @@ -780,19 +825,22 @@ store_tolerance(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index - 1; struct i2c_client *client = to_i2c_client(dev); struct w83792d_data *data = i2c_get_clientdata(client); - u32 val; u8 tol_tmp, tol_mask; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; - val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); tol_mask = w83792d_read_value(client, W83792D_REG_TOLERANCE[nr]) & ((nr == 1) ? 0x0f : 0xf0); - tol_tmp = SENSORS_LIMIT(val, 0, 15); + tol_tmp = clamp_val(val, 0, 15); tol_tmp &= 0x0f; data->tolerance[nr] = tol_tmp; - if (nr == 1) { + if (nr == 1) tol_tmp <<= 4; - } w83792d_write_value(client, W83792D_REG_TOLERANCE[nr], tol_mask | tol_tmp); mutex_unlock(&data->update_lock); @@ -805,7 +853,8 @@ static ssize_t show_sf2_point(struct device *dev, struct device_attribute *attr, char *buf) { - struct sensor_device_attribute_2 *sensor_attr = to_sensor_dev_attr_2(attr); + struct sensor_device_attribute_2 *sensor_attr + = to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr; int index = sensor_attr->index; struct w83792d_data *data = w83792d_update_device(dev); @@ -816,17 +865,22 @@ static ssize_t store_sf2_point(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); + struct sensor_device_attribute_2 *sensor_attr + = to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr - 1; int index = sensor_attr->index - 1; struct i2c_client *client = to_i2c_client(dev); struct w83792d_data *data = i2c_get_clientdata(client); - u32 val; u8 mask_tmp = 0; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; - val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); - data->sf2_points[index][nr] = SENSORS_LIMIT(val, 0, 127); + data->sf2_points[index][nr] = clamp_val(val, 0, 127); mask_tmp = w83792d_read_value(client, W83792D_REG_POINTS[index][nr]) & 0x80; w83792d_write_value(client, W83792D_REG_POINTS[index][nr], @@ -840,7 +894,8 @@ static ssize_t show_sf2_level(struct device *dev, struct device_attribute *attr, char *buf) { - struct sensor_device_attribute_2 *sensor_attr = to_sensor_dev_attr_2(attr); + struct sensor_device_attribute_2 *sensor_attr + = to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr; int index = sensor_attr->index; struct w83792d_data *data = w83792d_update_device(dev); @@ -852,25 +907,30 @@ static ssize_t store_sf2_level(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); + struct sensor_device_attribute_2 *sensor_attr + = to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr; int index = sensor_attr->index - 1; struct i2c_client *client = to_i2c_client(dev); struct w83792d_data *data = i2c_get_clientdata(client); - u32 val; - u8 mask_tmp=0, level_tmp=0; + u8 mask_tmp = 0, level_tmp = 0; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; - val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); - data->sf2_levels[index][nr] = SENSORS_LIMIT((val * 15) / 100, 0, 15); + data->sf2_levels[index][nr] = clamp_val((val * 15) / 100, 0, 15); mask_tmp = w83792d_read_value(client, W83792D_REG_LEVELS[index][nr]) - & ((nr==3) ? 0xf0 : 0x0f); - if (nr==3) { + & ((nr == 3) ? 0xf0 : 0x0f); + if (nr == 3) level_tmp = data->sf2_levels[index][nr]; - } else { + else level_tmp = data->sf2_levels[index][nr] << 4; - } - w83792d_write_value(client, W83792D_REG_LEVELS[index][nr], level_tmp | mask_tmp); + w83792d_write_value(client, W83792D_REG_LEVELS[index][nr], + level_tmp | mask_tmp); mutex_unlock(&data->update_lock); return count; @@ -891,8 +951,8 @@ w83792d_detect_subclients(struct i2c_client *new_client) for (i = 2; i <= 3; i++) { if (force_subclients[i] < 0x48 || force_subclients[i] > 0x4f) { - dev_err(&new_client->dev, "invalid subclient " - "address %d; must be 0x48-0x4f\n", + dev_err(&new_client->dev, + "invalid subclient address %d; must be 0x48-0x4f\n", force_subclients[i]); err = -ENODEV; goto ERROR_SC_0; @@ -904,14 +964,14 @@ w83792d_detect_subclients(struct i2c_client *new_client) } val = w83792d_read_value(new_client, W83792D_REG_I2C_SUBADDR); - if (!(val & 0x08)) { + if (!(val & 0x08)) data->lm75[0] = i2c_new_dummy(adapter, 0x48 + (val & 0x7)); - } if (!(val & 0x80)) { if ((data->lm75[0] != NULL) && ((val & 0x7) == ((val >> 4) & 0x7))) { - dev_err(&new_client->dev, "duplicate addresses 0x%x, " - "use force_subclient\n", data->lm75[0]->addr); + dev_err(&new_client->dev, + "duplicate addresses 0x%x, use force_subclient\n", + data->lm75[0]->addr); err = -ENODEV; goto ERROR_SC_1; } @@ -1010,8 +1070,7 @@ static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 20); static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, 21); static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 22); static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO, show_alarm, NULL, 23); -static DEVICE_ATTR(chassis, S_IRUGO, show_regs_chassis, NULL); -static DEVICE_ATTR(chassis_clear, S_IRUGO | S_IWUSR, +static DEVICE_ATTR(intrusion0_alarm, S_IRUGO | S_IWUSR, show_chassis_clear, store_chassis_clear); static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0); static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1); @@ -1212,8 +1271,7 @@ static struct attribute *w83792d_attributes[] = { &sensor_dev_attr_pwm3_mode.dev_attr.attr, &sensor_dev_attr_pwm3_enable.dev_attr.attr, &dev_attr_alarms.attr, - &dev_attr_chassis.attr, - &dev_attr_chassis_clear.attr, + &dev_attr_intrusion0_alarm.attr, &sensor_dev_attr_tolerance1.dev_attr.attr, &sensor_dev_attr_thermal_cruise1.dev_attr.attr, &sensor_dev_attr_tolerance2.dev_attr.attr, @@ -1268,9 +1326,8 @@ w83792d_detect(struct i2c_client *client, struct i2c_board_info *info) int val1, val2; unsigned short address = client->addr; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - } if (w83792d_read_value(client, W83792D_REG_CONFIG) & 0x80) return -ENODEV; @@ -1280,11 +1337,13 @@ w83792d_detect(struct i2c_client *client, struct i2c_board_info *info) /* Check for Winbond ID if in bank 0 */ if (!(val1 & 0x07)) { /* is Bank0 */ if ((!(val1 & 0x80) && val2 != 0xa3) || - ( (val1 & 0x80) && val2 != 0x5c)) + ((val1 & 0x80) && val2 != 0x5c)) return -ENODEV; } - /* If Winbond chip, address of chip and W83792D_REG_I2C_ADDR - should match */ + /* + * If Winbond chip, address of chip and W83792D_REG_I2C_ADDR + * should match + */ if (w83792d_read_value(client, W83792D_REG_I2C_ADDR) != address) return -ENODEV; @@ -1312,19 +1371,16 @@ w83792d_probe(struct i2c_client *client, const struct i2c_device_id *id) struct device *dev = &client->dev; int i, val1, err; - data = kzalloc(sizeof(struct w83792d_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto ERROR0; - } + data = devm_kzalloc(dev, sizeof(struct w83792d_data), GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); - data->valid = 0; mutex_init(&data->update_lock); err = w83792d_detect_subclients(client); if (err) - goto ERROR1; + return err; /* Initialize the chip */ w83792d_init_client(client); @@ -1336,33 +1392,40 @@ w83792d_probe(struct i2c_client *client, const struct i2c_device_id *id) } /* Register sysfs hooks */ - if ((err = sysfs_create_group(&dev->kobj, &w83792d_group))) - goto ERROR3; + err = sysfs_create_group(&dev->kobj, &w83792d_group); + if (err) + goto exit_i2c_unregister; - /* Read GPIO enable register to check if pins for fan 4,5 are used as - GPIO */ + /* + * Read GPIO enable register to check if pins for fan 4,5 are used as + * GPIO + */ val1 = w83792d_read_value(client, W83792D_REG_GPIO_EN); - if (!(val1 & 0x40)) - if ((err = sysfs_create_group(&dev->kobj, - &w83792d_group_fan[0]))) + if (!(val1 & 0x40)) { + err = sysfs_create_group(&dev->kobj, &w83792d_group_fan[0]); + if (err) goto exit_remove_files; + } - if (!(val1 & 0x20)) - if ((err = sysfs_create_group(&dev->kobj, - &w83792d_group_fan[1]))) + if (!(val1 & 0x20)) { + err = sysfs_create_group(&dev->kobj, &w83792d_group_fan[1]); + if (err) goto exit_remove_files; + } val1 = w83792d_read_value(client, W83792D_REG_PIN); - if (val1 & 0x40) - if ((err = sysfs_create_group(&dev->kobj, - &w83792d_group_fan[2]))) + if (val1 & 0x40) { + err = sysfs_create_group(&dev->kobj, &w83792d_group_fan[2]); + if (err) goto exit_remove_files; + } - if (val1 & 0x04) - if ((err = sysfs_create_group(&dev->kobj, - &w83792d_group_fan[3]))) + if (val1 & 0x04) { + err = sysfs_create_group(&dev->kobj, &w83792d_group_fan[3]); + if (err) goto exit_remove_files; + } data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { @@ -1376,14 +1439,11 @@ exit_remove_files: sysfs_remove_group(&dev->kobj, &w83792d_group); for (i = 0; i < ARRAY_SIZE(w83792d_group_fan); i++) sysfs_remove_group(&dev->kobj, &w83792d_group_fan[i]); -ERROR3: +exit_i2c_unregister: if (data->lm75[0] != NULL) i2c_unregister_device(data->lm75[0]); if (data->lm75[1] != NULL) i2c_unregister_device(data->lm75[1]); -ERROR1: - kfree(data); -ERROR0: return err; } @@ -1404,7 +1464,6 @@ w83792d_remove(struct i2c_client *client) if (data->lm75[1] != NULL) i2c_unregister_device(data->lm75[1]); - kfree(data); return 0; } @@ -1413,14 +1472,16 @@ w83792d_init_client(struct i2c_client *client) { u8 temp2_cfg, temp3_cfg, vid_in_b; - if (init) { + if (init) w83792d_write_value(client, W83792D_REG_CONFIG, 0x80); - } - /* Clear the bit6 of W83792D_REG_VID_IN_B(set it into 0): - W83792D_REG_VID_IN_B bit6 = 0: the high/low limit of - vin0/vin1 can be modified by user; - W83792D_REG_VID_IN_B bit6 = 1: the high/low limit of - vin0/vin1 auto-updated, can NOT be modified by user. */ + + /* + * Clear the bit6 of W83792D_REG_VID_IN_B(set it into 0): + * W83792D_REG_VID_IN_B bit6 = 0: the high/low limit of + * vin0/vin1 can be modified by user; + * W83792D_REG_VID_IN_B bit6 = 1: the high/low limit of + * vin0/vin1 auto-updated, can NOT be modified by user. + */ vid_in_b = w83792d_read_value(client, W83792D_REG_VID_IN_B); w83792d_write_value(client, W83792D_REG_VID_IN_B, vid_in_b & 0xbf); @@ -1489,7 +1550,7 @@ static struct w83792d_data *w83792d_update_device(struct device *dev) for (i = 0; i < 2; i++) { for (j = 0; j < 6; j++) { data->temp_add[i][j] = w83792d_read_value( - client,W83792D_REG_TEMP_ADD[i][j]); + client, W83792D_REG_TEMP_ADD[i][j]); } } @@ -1514,8 +1575,6 @@ static struct w83792d_data *w83792d_update_device(struct device *dev) /* Update CaseOpen status and it's CLR_CHS. */ data->chassis = (w83792d_read_value(client, W83792D_REG_CHASSIS) >> 5) & 0x01; - data->chassis_clear = (w83792d_read_value(client, - W83792D_REG_CHASSIS_CLR) >> 7) & 0x01; /* Update Thermal Cruise/Smart Fan I target value */ for (i = 0; i < 3; i++) { @@ -1534,8 +1593,9 @@ static struct w83792d_data *w83792d_update_device(struct device *dev) /* Update Smart Fan II temperature points */ for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { - data->sf2_points[i][j] = w83792d_read_value( - client,W83792D_REG_POINTS[i][j]) & 0x7f; + data->sf2_points[i][j] + = w83792d_read_value(client, + W83792D_REG_POINTS[i][j]) & 0x7f; } } @@ -1567,10 +1627,10 @@ static struct w83792d_data *w83792d_update_device(struct device *dev) #ifdef DEBUG static void w83792d_print_debug(struct w83792d_data *data, struct device *dev) { - int i=0, j=0; + int i = 0, j = 0; dev_dbg(dev, "==========The following is the debug message...========\n"); dev_dbg(dev, "9 set of Voltages: =====>\n"); - for (i=0; i<9; i++) { + for (i = 0; i < 9; i++) { dev_dbg(dev, "vin[%d] is: 0x%x\n", i, data->in[i]); dev_dbg(dev, "vin[%d] max is: 0x%x\n", i, data->in_max[i]); dev_dbg(dev, "vin[%d] min is: 0x%x\n", i, data->in_min[i]); @@ -1578,47 +1638,32 @@ static void w83792d_print_debug(struct w83792d_data *data, struct device *dev) dev_dbg(dev, "Low Bit1 is: 0x%x\n", data->low_bits & 0xff); dev_dbg(dev, "Low Bit2 is: 0x%x\n", data->low_bits >> 8); dev_dbg(dev, "7 set of Fan Counts and Duty Cycles: =====>\n"); - for (i=0; i<7; i++) { + for (i = 0; i < 7; i++) { dev_dbg(dev, "fan[%d] is: 0x%x\n", i, data->fan[i]); dev_dbg(dev, "fan[%d] min is: 0x%x\n", i, data->fan_min[i]); dev_dbg(dev, "pwm[%d] is: 0x%x\n", i, data->pwm[i]); } dev_dbg(dev, "3 set of Temperatures: =====>\n"); - for (i=0; i<3; i++) { + for (i = 0; i < 3; i++) dev_dbg(dev, "temp1[%d] is: 0x%x\n", i, data->temp1[i]); - } - for (i=0; i<2; i++) { - for (j=0; j<6; j++) { + for (i = 0; i < 2; i++) { + for (j = 0; j < 6; j++) { dev_dbg(dev, "temp_add[%d][%d] is: 0x%x\n", i, j, data->temp_add[i][j]); } } - for (i=0; i<7; i++) { + for (i = 0; i < 7; i++) dev_dbg(dev, "fan_div[%d] is: 0x%x\n", i, data->fan_div[i]); - } - dev_dbg(dev, "==========End of the debug message...==================\n"); + + dev_dbg(dev, "==========End of the debug message...================\n"); dev_dbg(dev, "\n"); } #endif -static int __init -sensors_w83792d_init(void) -{ - return i2c_add_driver(&w83792d_driver); -} +module_i2c_driver(w83792d_driver); -static void __exit -sensors_w83792d_exit(void) -{ - i2c_del_driver(&w83792d_driver); -} - -MODULE_AUTHOR("Chunhao Huang @ Winbond <DZShen@Winbond.com.tw>"); +MODULE_AUTHOR("Shane Huang (Winbond)"); MODULE_DESCRIPTION("W83792AD/D driver for linux-2.6"); MODULE_LICENSE("GPL"); - -module_init(sensors_w83792d_init); -module_exit(sensors_w83792d_exit); - diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index 9a2022b6749..9d63d71214c 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -1,30 +1,34 @@ /* - w83793.c - Linux kernel driver for hardware monitoring - Copyright (C) 2006 Winbond Electronics Corp. - Yuan Mu - Rudolf Marek <r.marek@assembler.cz> - - 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. -*/ + * w83793.c - Linux kernel driver for hardware monitoring + * Copyright (C) 2006 Winbond Electronics Corp. + * Yuan Mu + * Rudolf Marek <r.marek@assembler.cz> + * Copyright (C) 2009-2010 Sven Anders <anders@anduras.de>, ANDURAS AG. + * Watchdog driver part + * (Based partially on fschmd driver, + * Copyright 2007-2008 by Hans de Goede) + * + * 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 wchipid vendid i2c ISA - w83793 10 12 8 6 0x7b 0x5ca3 yes no -*/ + * Supports following chips: + * + * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA + * w83793 10 12 8 6 0x7b 0x5ca3 yes no + */ #include <linux/module.h> #include <linux/init.h> @@ -35,6 +39,17 @@ #include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/fs.h> +#include <linux/watchdog.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/kref.h> +#include <linux/notifier.h> +#include <linux/reboot.h> +#include <linux/jiffies.h> + +/* Default values */ +#define WATCHDOG_TIMEOUT 2 /* 2 minute default timeout */ /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, @@ -44,17 +59,29 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, static unsigned short force_subclients[4]; module_param_array(force_subclients, short, NULL, 0); -MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " - "{bus, clientaddr, subclientaddr1, subclientaddr2}"); +MODULE_PARM_DESC(force_subclients, + "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}"); -static int reset; +static bool reset; module_param(reset, bool, 0); MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended"); +static int timeout = WATCHDOG_TIMEOUT; /* default timeout in minutes */ +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in minutes. 2<= timeout <=255 (default=" + __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + /* - Address 0x00, 0x0d, 0x0e, 0x0f in all three banks are reserved - as ID, Bank Select registers -*/ + * Address 0x00, 0x0d, 0x0e, 0x0f in all three banks are reserved + * as ID, Bank Select registers + */ #define W83793_REG_BANKSEL 0x00 #define W83793_REG_VENDORID 0x0d #define W83793_REG_CHIPID 0x0e @@ -72,6 +99,11 @@ MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended"); #define W83793_REG_VID_LATCHB 0x08 #define W83793_REG_VID_CTRL 0x59 +#define W83793_REG_WDT_LOCK 0x01 +#define W83793_REG_WDT_ENABLE 0x02 +#define W83793_REG_WDT_STATUS 0x03 +#define W83793_REG_WDT_TIMEOUT 0x04 + static u16 W83793_REG_TEMP_MODE[2] = { 0x5e, 0x5f }; #define TEMP_READ 0 @@ -79,8 +111,10 @@ static u16 W83793_REG_TEMP_MODE[2] = { 0x5e, 0x5f }; #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 */ +/* + * only crit and crit_hyst affect real-time alarm status + * current crit crit_hyst warn warn_hyst + */ static u16 W83793_REG_TEMP[][5] = { {0x1c, 0x78, 0x79, 0x7a, 0x7b}, {0x1d, 0x7c, 0x7d, 0x7e, 0x7f}, @@ -150,34 +184,34 @@ static inline unsigned long FAN_FROM_REG(u16 val) { if ((val >= 0xfff) || (val == 0)) return 0; - return (1350000UL / val); + 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); + return clamp_val((1350000 + (rpm >> 1)) / rpm, 1, 0xffe); } static inline unsigned long TIME_FROM_REG(u8 reg) { - return (reg * 100); + return reg * 100; } static inline u8 TIME_TO_REG(unsigned long val) { - return SENSORS_LIMIT((val + 50) / 100, 0, 0xff); + return clamp_val((val + 50) / 100, 0, 0xff); } static inline long TEMP_FROM_REG(s8 reg) { - return (reg * 1000); + return reg * 1000; } static inline s8 TEMP_TO_REG(long val, s8 min, s8 max) { - return SENSORS_LIMIT((val + (val < 0 ? -500 : 500)) / 1000, min, max); + return clamp_val((val + (val < 0 ? -500 : 500)) / 1000, min, max); } struct w83793_data { @@ -187,7 +221,8 @@ struct w83793_data { char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ unsigned long last_nonvolatile; /* In jiffies, last time we update the - nonvolatile registers */ + * nonvolatile registers + */ u8 bank; u8 vrm; @@ -202,7 +237,8 @@ struct w83793_data { s8 temp[6][5]; /* current, crit, crit_hyst,warn, warn_hyst */ u8 temp_low_bits; /* Additional resolution TD1-TD4 */ u8 temp_mode[2]; /* byte 0: Temp D1-D4 mode each has 2 bits - byte 1: Temp R1,R2 mode, each has 1 bit */ + * byte 1: Temp R1,R2 mode, each has 1 bit + */ u8 temp_critical; /* If reached all fan will be at full speed */ u8 temp_fan_map[6]; /* Temp controls which pwm fan, bit field */ @@ -223,8 +259,41 @@ struct w83793_data { u8 tolerance[3]; /* Temp tolerance(Smart Fan I/II) */ u8 sf2_pwm[6][7]; /* Smart FanII: Fan duty cycle */ u8 sf2_temp[6][7]; /* Smart FanII: Temp level point */ + + /* watchdog */ + struct i2c_client *client; + struct mutex watchdog_lock; + struct list_head list; /* member of the watchdog_data_list */ + struct kref kref; + struct miscdevice watchdog_miscdev; + unsigned long watchdog_is_open; + char watchdog_expect_close; + char watchdog_name[10]; /* must be unique to avoid sysfs conflict */ + unsigned int watchdog_caused_reboot; + int watchdog_timeout; /* watchdog timeout in minutes */ }; +/* + * Somewhat ugly :( global data pointer list with all devices, so that + * we can find our device data as when using misc_register. There is no + * other method to get to one's device data from the open file-op and + * for usage in the reboot notifier callback. + */ +static LIST_HEAD(watchdog_data_list); + +/* Note this lock not only protect list access, but also data.kref access */ +static DEFINE_MUTEX(watchdog_data_mutex); + +/* + * Release our data struct when we're detached from the i2c client *and* all + * references to our watchdog device are released + */ +static void w83793_release_resources(struct kref *ref) +{ + struct w83793_data *data = container_of(ref, struct w83793_data, kref); + kfree(data); +} + static u8 w83793_read_value(struct i2c_client *client, u16 reg); static int w83793_write_value(struct i2c_client *client, u16 reg, u8 value); static int w83793_probe(struct i2c_client *client, @@ -277,7 +346,14 @@ store_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct w83793_data *data = dev_get_drvdata(dev); - data->vrm = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + data->vrm = val; return count; } @@ -294,7 +370,7 @@ show_alarm_beep(struct device *dev, struct device_attribute *attr, char *buf) int bit = sensor_attr->index & 0x07; u8 val; - if (ALARM_STATUS == nr) { + if (nr == ALARM_STATUS) { val = (data->alarms[index] >> (bit)) & 1; } else { /* BEEP_ENABLE */ val = (data->beeps[index] >> (bit)) & 1; @@ -314,10 +390,14 @@ store_beep(struct device *dev, struct device_attribute *attr, int index = sensor_attr->index >> 3; int shift = sensor_attr->index & 0x07; u8 beep_bit = 1 << shift; - u8 val; + unsigned long val; + int err; - val = simple_strtoul(buf, NULL, 10); - if (val != 0 && val != 1) + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + if (val > 1) return -EINVAL; mutex_lock(&data->update_lock); @@ -343,9 +423,14 @@ store_beep_enable(struct device *dev, struct device_attribute *attr, { struct i2c_client *client = to_i2c_client(dev); struct w83793_data *data = i2c_get_clientdata(client); - u8 val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; - if (val != 0 && val != 1) + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + if (val > 1) return -EINVAL; mutex_lock(&data->update_lock); @@ -358,7 +443,7 @@ store_beep_enable(struct device *dev, struct device_attribute *attr, return count; } -/* Write any value to clear chassis alarm */ +/* Write 0 to clear chassis alarm */ static ssize_t store_chassis_clear(struct device *dev, struct device_attribute *attr, const char *buf, @@ -366,12 +451,20 @@ store_chassis_clear(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct w83793_data *data = i2c_get_clientdata(client); - u8 val; + unsigned long val; + u8 reg; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + if (val) + return -EINVAL; mutex_lock(&data->update_lock); - val = w83793_read_value(client, W83793_REG_CLR_CHASSIS); - val |= 0x80; - w83793_write_value(client, W83793_REG_CLR_CHASSIS, val); + reg = w83793_read_value(client, W83793_REG_CLR_CHASSIS); + w83793_write_value(client, W83793_REG_CLR_CHASSIS, reg | 0x80); + data->valid = 0; /* Force cache refresh */ mutex_unlock(&data->update_lock); return count; } @@ -388,11 +481,10 @@ show_fan(struct device *dev, struct device_attribute *attr, char *buf) struct w83793_data *data = w83793_update_device(dev); u16 val; - if (FAN_INPUT == nr) { + if (nr == FAN_INPUT) val = data->fan[index] & 0x0fff; - } else { + else val = data->fan_min[index] & 0x0fff; - } return sprintf(buf, "%lu\n", FAN_FROM_REG(val)); } @@ -406,7 +498,13 @@ store_fan_min(struct device *dev, struct device_attribute *attr, int index = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct w83793_data *data = i2c_get_clientdata(client); - u16 val = FAN_TO_REG(simple_strtoul(buf, NULL, 10)); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + val = FAN_TO_REG(val); mutex_lock(&data->update_lock); data->fan_min[index] = val; @@ -428,7 +526,7 @@ show_pwm(struct device *dev, struct device_attribute *attr, char *buf) int nr = sensor_attr->nr; int index = sensor_attr->index; - if (PWM_STOP_TIME == nr) + if (nr == PWM_STOP_TIME) val = TIME_FROM_REG(data->pwm_stop_time[index]); else val = (data->pwm[index][nr] & 0x3f) << 2; @@ -446,17 +544,21 @@ store_pwm(struct device *dev, struct device_attribute *attr, to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr; int index = sensor_attr->index; - u8 val; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - if (PWM_STOP_TIME == nr) { - val = TIME_TO_REG(simple_strtoul(buf, NULL, 10)); + if (nr == PWM_STOP_TIME) { + val = TIME_TO_REG(val); data->pwm_stop_time[index] = val; w83793_write_value(client, W83793_REG_PWM_STOP_TIME(index), val); } else { - val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 0xff) - >> 2; + val = clamp_val(val, 0, 0xff) >> 2; data->pwm[index][nr] = w83793_read_value(client, W83793_REG_PWM(index, nr)) & 0xc0; data->pwm[index][nr] |= val; @@ -478,7 +580,7 @@ show_temp(struct device *dev, struct device_attribute *attr, char *buf) struct w83793_data *data = w83793_update_device(dev); long temp = TEMP_FROM_REG(data->temp[index][nr]); - if (TEMP_READ == nr && index < 4) { /* Only TD1-TD4 have low bits */ + if (nr == TEMP_READ && index < 4) { /* Only TD1-TD4 have low bits */ int low = ((data->temp_low_bits >> (index * 2)) & 0x03) * 250; temp += temp > 0 ? low : -low; } @@ -495,7 +597,12 @@ store_temp(struct device *dev, struct device_attribute *attr, int index = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct w83793_data *data = i2c_get_clientdata(client); - long tmp = simple_strtol(buf, NULL, 10); + long tmp; + int err; + + err = kstrtol(buf, 10, &tmp); + if (err) + return err; mutex_lock(&data->update_lock); data->temp[index][nr] = TEMP_TO_REG(tmp, -128, 127); @@ -506,18 +613,18 @@ store_temp(struct device *dev, struct device_attribute *attr, } /* - TD1-TD4 - each has 4 mode:(2 bits) - 0: Stop monitor - 1: Use internal temp sensor(default) - 2: Reserved - 3: Use sensor in Intel CPU and get result by PECI - - TR1-TR2 - each has 2 mode:(1 bit) - 0: Disable temp sensor monitor - 1: To enable temp sensors monitor -*/ + * TD1-TD4 + * each has 4 mode:(2 bits) + * 0: Stop monitor + * 1: Use internal temp sensor(default) + * 2: Reserved + * 3: Use sensor in Intel CPU and get result by PECI + * + * TR1-TR2 + * each has 2 mode:(1 bit) + * 0: Disable temp sensor monitor + * 1: To enable temp sensors monitor + */ /* 0 disable, 6 PECI */ static u8 TO_TEMP_MODE[] = { 0, 0, 0, 6 }; @@ -537,11 +644,10 @@ show_temp_mode(struct device *dev, struct device_attribute *attr, char *buf) tmp = (data->temp_mode[index] >> shift) & mask; /* for the internal sensor, found out if diode or thermistor */ - if (tmp == 1) { + if (tmp == 1) tmp = index == 0 ? 3 : 4; - } else { + else tmp = TO_TEMP_MODE[tmp]; - } return sprintf(buf, "%d\n", tmp); } @@ -557,7 +663,12 @@ store_temp_mode(struct device *dev, struct device_attribute *attr, int index = sensor_attr->index; u8 mask = (index < 4) ? 0x03 : 0x01; u8 shift = (index < 4) ? (2 * index) : (index - 4); - u8 val = simple_strtoul(buf, NULL, 10); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; /* transform the sysfs interface values into table above */ if ((val == 6) && (index < 4)) { @@ -596,15 +707,14 @@ show_sf_setup(struct device *dev, struct device_attribute *attr, char *buf) struct w83793_data *data = w83793_update_device(dev); u32 val = 0; - if (SETUP_PWM_DEFAULT == nr) { + if (nr == SETUP_PWM_DEFAULT) val = (data->pwm_default & 0x3f) << 2; - } else if (SETUP_PWM_UPTIME == nr) { + else if (nr == SETUP_PWM_UPTIME) val = TIME_FROM_REG(data->pwm_uptime); - } else if (SETUP_PWM_DOWNTIME == nr) { + else if (nr == SETUP_PWM_DOWNTIME) val = TIME_FROM_REG(data->pwm_downtime); - } else if (SETUP_TEMP_CRITICAL == nr) { + else if (nr == SETUP_TEMP_CRITICAL) val = TEMP_FROM_REG(data->temp_critical & 0x7f); - } return sprintf(buf, "%d\n", val); } @@ -618,31 +728,34 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->nr; struct i2c_client *client = to_i2c_client(dev); struct w83793_data *data = i2c_get_clientdata(client); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - if (SETUP_PWM_DEFAULT == nr) { + if (nr == SETUP_PWM_DEFAULT) { data->pwm_default = w83793_read_value(client, W83793_REG_PWM_DEFAULT) & 0xc0; - data->pwm_default |= SENSORS_LIMIT(simple_strtoul(buf, NULL, - 10), - 0, 0xff) >> 2; + data->pwm_default |= clamp_val(val, 0, 0xff) >> 2; w83793_write_value(client, W83793_REG_PWM_DEFAULT, data->pwm_default); - } else if (SETUP_PWM_UPTIME == nr) { - data->pwm_uptime = TIME_TO_REG(simple_strtoul(buf, NULL, 10)); + } else if (nr == SETUP_PWM_UPTIME) { + data->pwm_uptime = TIME_TO_REG(val); data->pwm_uptime += data->pwm_uptime == 0 ? 1 : 0; w83793_write_value(client, W83793_REG_PWM_UPTIME, data->pwm_uptime); - } else if (SETUP_PWM_DOWNTIME == nr) { - data->pwm_downtime = TIME_TO_REG(simple_strtoul(buf, NULL, 10)); + } else if (nr == SETUP_PWM_DOWNTIME) { + data->pwm_downtime = TIME_TO_REG(val); data->pwm_downtime += data->pwm_downtime == 0 ? 1 : 0; w83793_write_value(client, W83793_REG_PWM_DOWNTIME, data->pwm_downtime); } else { /* SETUP_TEMP_CRITICAL */ data->temp_critical = w83793_read_value(client, W83793_REG_TEMP_CRITICAL) & 0x80; - data->temp_critical |= TEMP_TO_REG(simple_strtol(buf, NULL, 10), - 0, 0x7f); + data->temp_critical |= TEMP_TO_REG(val, 0, 0x7f); w83793_write_value(client, W83793_REG_TEMP_CRITICAL, data->temp_critical); } @@ -652,31 +765,31 @@ store_sf_setup(struct device *dev, struct device_attribute *attr, } /* - Temp SmartFan control - TEMP_FAN_MAP - Temp channel control which pwm fan, bitfield, bit 0 indicate pwm1... - It's possible two or more temp channels control the same fan, w83793 - always prefers to pick the most critical request and applies it to - the related Fan. - It's possible one fan is not in any mapping of 6 temp channels, this - means the fan is manual mode - - TEMP_PWM_ENABLE - Each temp channel has its own SmartFan mode, and temp channel - control fans that are set by TEMP_FAN_MAP - 0: SmartFanII mode - 1: Thermal Cruise Mode - - TEMP_CRUISE - Target temperature in thermal cruise mode, w83793 will try to turn - fan speed to keep the temperature of target device around this - temperature. - - TEMP_TOLERANCE - If Temp higher or lower than target with this tolerance, w83793 - will take actions to speed up or slow down the fan to keep the - temperature within the tolerance range. -*/ + * Temp SmartFan control + * TEMP_FAN_MAP + * Temp channel control which pwm fan, bitfield, bit 0 indicate pwm1... + * It's possible two or more temp channels control the same fan, w83793 + * always prefers to pick the most critical request and applies it to + * the related Fan. + * It's possible one fan is not in any mapping of 6 temp channels, this + * means the fan is manual mode + * + * TEMP_PWM_ENABLE + * Each temp channel has its own SmartFan mode, and temp channel + * control fans that are set by TEMP_FAN_MAP + * 0: SmartFanII mode + * 1: Thermal Cruise Mode + * + * TEMP_CRUISE + * Target temperature in thermal cruise mode, w83793 will try to turn + * fan speed to keep the temperature of target device around this + * temperature. + * + * TEMP_TOLERANCE + * If Temp higher or lower than target with this tolerance, w83793 + * will take actions to speed up or slow down the fan to keep the + * temperature within the tolerance range. + */ #define TEMP_FAN_MAP 0 #define TEMP_PWM_ENABLE 1 @@ -692,12 +805,12 @@ show_sf_ctrl(struct device *dev, struct device_attribute *attr, char *buf) struct w83793_data *data = w83793_update_device(dev); u32 val; - if (TEMP_FAN_MAP == nr) { + if (nr == TEMP_FAN_MAP) { val = data->temp_fan_map[index]; - } else if (TEMP_PWM_ENABLE == nr) { - /* +2 to transfrom into 2 and 3 to conform with sysfs intf */ + } else if (nr == TEMP_PWM_ENABLE) { + /* +2 to transform into 2 and 3 to conform with sysfs intf */ val = ((data->pwm_enable >> index) & 0x01) + 2; - } else if (TEMP_CRUISE == nr) { + } else if (nr == TEMP_CRUISE) { val = TEMP_FROM_REG(data->temp_cruise[index] & 0x7f); } else { /* TEMP_TOLERANCE */ val = data->tolerance[index >> 1] >> ((index & 0x01) ? 4 : 0); @@ -716,16 +829,20 @@ store_sf_ctrl(struct device *dev, struct device_attribute *attr, int index = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct w83793_data *data = i2c_get_clientdata(client); - u32 val; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - if (TEMP_FAN_MAP == nr) { - val = simple_strtoul(buf, NULL, 10) & 0xff; + if (nr == TEMP_FAN_MAP) { + val = clamp_val(val, 0, 255); w83793_write_value(client, W83793_REG_TEMP_FAN_MAP(index), val); data->temp_fan_map[index] = val; - } else if (TEMP_PWM_ENABLE == nr) { - val = simple_strtoul(buf, NULL, 10); - if (2 == val || 3 == val) { + } else if (nr == TEMP_PWM_ENABLE) { + if (val == 2 || val == 3) { data->pwm_enable = w83793_read_value(client, W83793_REG_PWM_ENABLE); if (val - 2) @@ -738,12 +855,11 @@ store_sf_ctrl(struct device *dev, struct device_attribute *attr, mutex_unlock(&data->update_lock); return -EINVAL; } - } else if (TEMP_CRUISE == nr) { + } else if (nr == TEMP_CRUISE) { data->temp_cruise[index] = w83793_read_value(client, W83793_REG_TEMP_CRUISE(index)); - val = TEMP_TO_REG(simple_strtol(buf, NULL, 10), 0, 0x7f); data->temp_cruise[index] &= 0x80; - data->temp_cruise[index] |= val; + data->temp_cruise[index] |= TEMP_TO_REG(val, 0, 0x7f); w83793_write_value(client, W83793_REG_TEMP_CRUISE(index), data->temp_cruise[index]); @@ -753,9 +869,8 @@ store_sf_ctrl(struct device *dev, struct device_attribute *attr, data->tolerance[i] = w83793_read_value(client, W83793_REG_TEMP_TOL(i)); - val = TEMP_TO_REG(simple_strtol(buf, NULL, 10), 0, 0x0f); data->tolerance[i] &= ~(0x0f << shift); - data->tolerance[i] |= val << shift; + data->tolerance[i] |= TEMP_TO_REG(val, 0, 0x0f) << shift; w83793_write_value(client, W83793_REG_TEMP_TOL(i), data->tolerance[i]); } @@ -786,7 +901,13 @@ store_sf2_pwm(struct device *dev, struct device_attribute *attr, to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr; int index = sensor_attr->index; - u8 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 0xff) >> 2; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + val = clamp_val(val, 0, 0xff) >> 2; mutex_lock(&data->update_lock); data->sf2_pwm[index][nr] = @@ -821,7 +942,13 @@ store_sf2_temp(struct device *dev, struct device_attribute *attr, to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr; int index = sensor_attr->index; - u8 val = TEMP_TO_REG(simple_strtol(buf, NULL, 10), 0, 0x7f); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + val = TEMP_TO_REG(val, 0, 0x7f); mutex_lock(&data->update_lock); data->sf2_temp[index][nr] = @@ -863,20 +990,22 @@ store_in(struct device *dev, struct device_attribute *attr, int index = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct w83793_data *data = i2c_get_clientdata(client); - u32 val; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + val = (val + scale_in[index] / 2) / scale_in[index]; - val = - (simple_strtoul(buf, NULL, 10) + - scale_in[index] / 2) / scale_in[index]; mutex_lock(&data->update_lock); if (index > 2) { /* fix the limit values of 5VDD and 5VSB to ALARM mechanism */ - if (1 == nr || 2 == nr) { + if (nr == 1 || nr == 2) val -= scale_in_add[index] / scale_in[index]; - } - val = SENSORS_LIMIT(val, 0, 255); + val = clamp_val(val, 0, 255); } else { - val = SENSORS_LIMIT(val, 0, 0x3FF); + val = clamp_val(val, 0, 0x3FF); data->in_low_bits[nr] = w83793_read_value(client, W83793_REG_IN_LOW_BITS[nr]); data->in_low_bits[nr] &= ~(0x03 << (2 * index)); @@ -1040,7 +1169,7 @@ static struct sensor_device_attribute_2 w83793_vid[] = { static DEVICE_ATTR(vrm, S_IWUSR | S_IRUGO, show_vrm, store_vrm); static struct sensor_device_attribute_2 sda_single_files[] = { - SENSOR_ATTR_2(chassis, S_IWUSR | S_IRUGO, show_alarm_beep, + SENSOR_ATTR_2(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm_beep, store_chassis_clear, ALARM_STATUS, 30), SENSOR_ATTR_2(beep_enable, S_IWUSR | S_IRUGO, show_beep_enable, store_beep_enable, NOT_USED, NOT_USED), @@ -1056,21 +1185,359 @@ static struct sensor_device_attribute_2 sda_single_files[] = { static void w83793_init_client(struct i2c_client *client) { - if (reset) { + if (reset) w83793_write_value(client, W83793_REG_CONFIG, 0x80); - } /* Start monitoring */ w83793_write_value(client, W83793_REG_CONFIG, w83793_read_value(client, W83793_REG_CONFIG) | 0x01); +} + +/* + * Watchdog routines + */ + +static int watchdog_set_timeout(struct w83793_data *data, int timeout) +{ + unsigned int mtimeout; + int ret; + + mtimeout = DIV_ROUND_UP(timeout, 60); + + if (mtimeout > 255) + return -EINVAL; + + mutex_lock(&data->watchdog_lock); + if (!data->client) { + ret = -ENODEV; + goto leave; + } + + data->watchdog_timeout = mtimeout; + /* Set Timeout value (in Minutes) */ + w83793_write_value(data->client, W83793_REG_WDT_TIMEOUT, + data->watchdog_timeout); + + ret = mtimeout * 60; + +leave: + mutex_unlock(&data->watchdog_lock); + return ret; +} + +static int watchdog_get_timeout(struct w83793_data *data) +{ + int timeout; + + mutex_lock(&data->watchdog_lock); + timeout = data->watchdog_timeout * 60; + mutex_unlock(&data->watchdog_lock); + + return timeout; } +static int watchdog_trigger(struct w83793_data *data) +{ + int ret = 0; + + mutex_lock(&data->watchdog_lock); + if (!data->client) { + ret = -ENODEV; + goto leave; + } + + /* Set Timeout value (in Minutes) */ + w83793_write_value(data->client, W83793_REG_WDT_TIMEOUT, + data->watchdog_timeout); + +leave: + mutex_unlock(&data->watchdog_lock); + return ret; +} + +static int watchdog_enable(struct w83793_data *data) +{ + int ret = 0; + + mutex_lock(&data->watchdog_lock); + if (!data->client) { + ret = -ENODEV; + goto leave; + } + + /* Set initial timeout */ + w83793_write_value(data->client, W83793_REG_WDT_TIMEOUT, + data->watchdog_timeout); + + /* Enable Soft Watchdog */ + w83793_write_value(data->client, W83793_REG_WDT_LOCK, 0x55); + +leave: + mutex_unlock(&data->watchdog_lock); + return ret; +} + +static int watchdog_disable(struct w83793_data *data) +{ + int ret = 0; + + mutex_lock(&data->watchdog_lock); + if (!data->client) { + ret = -ENODEV; + goto leave; + } + + /* Disable Soft Watchdog */ + w83793_write_value(data->client, W83793_REG_WDT_LOCK, 0xAA); + +leave: + mutex_unlock(&data->watchdog_lock); + return ret; +} + +static int watchdog_open(struct inode *inode, struct file *filp) +{ + struct w83793_data *pos, *data = NULL; + int watchdog_is_open; + + /* + * We get called from drivers/char/misc.c with misc_mtx hold, and we + * call misc_register() from w83793_probe() with watchdog_data_mutex + * hold, as misc_register() takes the misc_mtx lock, this is a possible + * deadlock, so we use mutex_trylock here. + */ + if (!mutex_trylock(&watchdog_data_mutex)) + return -ERESTARTSYS; + list_for_each_entry(pos, &watchdog_data_list, list) { + if (pos->watchdog_miscdev.minor == iminor(inode)) { + data = pos; + break; + } + } + + /* Check, if device is already open */ + watchdog_is_open = test_and_set_bit(0, &data->watchdog_is_open); + + /* + * Increase data reference counter (if not already done). + * Note we can never not have found data, so we don't check for this + */ + if (!watchdog_is_open) + kref_get(&data->kref); + + mutex_unlock(&watchdog_data_mutex); + + /* Check, if device is already open and possibly issue error */ + if (watchdog_is_open) + return -EBUSY; + + /* Enable Soft Watchdog */ + watchdog_enable(data); + + /* Store pointer to data into filp's private data */ + filp->private_data = data; + + return nonseekable_open(inode, filp); +} + +static int watchdog_close(struct inode *inode, struct file *filp) +{ + struct w83793_data *data = filp->private_data; + + if (data->watchdog_expect_close) { + watchdog_disable(data); + data->watchdog_expect_close = 0; + } else { + watchdog_trigger(data); + dev_crit(&data->client->dev, + "unexpected close, not stopping watchdog!\n"); + } + + clear_bit(0, &data->watchdog_is_open); + + /* Decrease data reference counter */ + mutex_lock(&watchdog_data_mutex); + kref_put(&data->kref, w83793_release_resources); + mutex_unlock(&watchdog_data_mutex); + + return 0; +} + +static ssize_t watchdog_write(struct file *filp, const char __user *buf, + size_t count, loff_t *offset) +{ + ssize_t ret; + struct w83793_data *data = filp->private_data; + + if (count) { + if (!nowayout) { + size_t i; + + /* Clear it in case it was set with a previous write */ + data->watchdog_expect_close = 0; + + for (i = 0; i != count; i++) { + char c; + if (get_user(c, buf + i)) + return -EFAULT; + if (c == 'V') + data->watchdog_expect_close = 1; + } + } + ret = watchdog_trigger(data); + if (ret < 0) + return ret; + } + return count; +} + +static long watchdog_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | + WDIOF_SETTIMEOUT | + WDIOF_CARDRESET, + .identity = "w83793 watchdog" + }; + + int val, ret = 0; + struct w83793_data *data = filp->private_data; + + switch (cmd) { + case WDIOC_GETSUPPORT: + if (!nowayout) + ident.options |= WDIOF_MAGICCLOSE; + if (copy_to_user((void __user *)arg, &ident, sizeof(ident))) + ret = -EFAULT; + break; + + case WDIOC_GETSTATUS: + val = data->watchdog_caused_reboot ? WDIOF_CARDRESET : 0; + ret = put_user(val, (int __user *)arg); + break; + + case WDIOC_GETBOOTSTATUS: + ret = put_user(0, (int __user *)arg); + break; + + case WDIOC_KEEPALIVE: + ret = watchdog_trigger(data); + break; + + case WDIOC_GETTIMEOUT: + val = watchdog_get_timeout(data); + ret = put_user(val, (int __user *)arg); + break; + + case WDIOC_SETTIMEOUT: + if (get_user(val, (int __user *)arg)) { + ret = -EFAULT; + break; + } + ret = watchdog_set_timeout(data, val); + if (ret > 0) + ret = put_user(ret, (int __user *)arg); + break; + + case WDIOC_SETOPTIONS: + if (get_user(val, (int __user *)arg)) { + ret = -EFAULT; + break; + } + + if (val & WDIOS_DISABLECARD) + ret = watchdog_disable(data); + else if (val & WDIOS_ENABLECARD) + ret = watchdog_enable(data); + else + ret = -EINVAL; + + break; + default: + ret = -ENOTTY; + } + return ret; +} + +static const struct file_operations watchdog_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = watchdog_open, + .release = watchdog_close, + .write = watchdog_write, + .unlocked_ioctl = watchdog_ioctl, +}; + +/* + * Notifier for system down + */ + +static int watchdog_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + struct w83793_data *data = NULL; + + if (code == SYS_DOWN || code == SYS_HALT) { + + /* Disable each registered watchdog */ + mutex_lock(&watchdog_data_mutex); + list_for_each_entry(data, &watchdog_data_list, list) { + if (data->watchdog_miscdev.minor) + watchdog_disable(data); + } + mutex_unlock(&watchdog_data_mutex); + } + + return NOTIFY_DONE; +} + +/* + * The WDT needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block watchdog_notifier = { + .notifier_call = watchdog_notify_sys, +}; + +/* + * Init / remove routines + */ + static int w83793_remove(struct i2c_client *client) { struct w83793_data *data = i2c_get_clientdata(client); struct device *dev = &client->dev; - int i; + int i, tmp; + + /* Unregister the watchdog (if registered) */ + if (data->watchdog_miscdev.minor) { + misc_deregister(&data->watchdog_miscdev); + + if (data->watchdog_is_open) { + dev_warn(&client->dev, + "i2c client detached with watchdog open! " + "Stopping watchdog.\n"); + watchdog_disable(data); + } + + mutex_lock(&watchdog_data_mutex); + list_del(&data->list); + mutex_unlock(&watchdog_data_mutex); + + /* Tell the watchdog code the client is gone */ + mutex_lock(&data->watchdog_lock); + data->client = NULL; + mutex_unlock(&data->watchdog_lock); + } + + /* Reset Configuration Register to Disable Watch Dog Registers */ + tmp = w83793_read_value(client, W83793_REG_CONFIG); + w83793_write_value(client, W83793_REG_CONFIG, tmp & ~0x04); + + unregister_reboot_notifier(&watchdog_notifier); hwmon_device_unregister(data->hwmon_dev); @@ -1099,7 +1566,10 @@ static int w83793_remove(struct i2c_client *client) if (data->lm75[1] != NULL) i2c_unregister_device(data->lm75[1]); - kfree(data); + /* Decrease data reference counter */ + mutex_lock(&watchdog_data_mutex); + kref_put(&data->kref, w83793_release_resources); + mutex_unlock(&watchdog_data_mutex); return 0; } @@ -1132,9 +1602,8 @@ w83793_detect_subclients(struct i2c_client *client) } tmp = w83793_read_value(client, W83793_REG_I2C_SUBADDR); - if (!(tmp & 0x08)) { + if (!(tmp & 0x08)) data->lm75[0] = i2c_new_dummy(adapter, 0x48 + (tmp & 0x7)); - } if (!(tmp & 0x80)) { if ((data->lm75[0] != NULL) && ((tmp & 0x7) == ((tmp >> 4) & 0x7))) { @@ -1167,9 +1636,8 @@ static int w83793_detect(struct i2c_client *client, struct i2c_adapter *adapter = client->adapter; unsigned short address = client->addr; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - } bank = i2c_smbus_read_byte_data(client, W83793_REG_BANKSEL); @@ -1180,8 +1648,10 @@ static int w83793_detect(struct i2c_client *client, return -ENODEV; } - /* If Winbond chip, address of chip and W83793_REG_I2C_ADDR - should match */ + /* + * If Winbond chip, address of chip and W83793_REG_I2C_ADDR + * should match + */ if ((bank & 0x07) == 0 && i2c_smbus_read_byte_data(client, W83793_REG_I2C_ADDR) != (address << 1)) { @@ -1203,6 +1673,7 @@ static int w83793_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; + const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 }; struct w83793_data *data; int i, tmp, val, err; int files_fan = ARRAY_SIZE(w83793_left_fan) / 7; @@ -1218,6 +1689,16 @@ static int w83793_probe(struct i2c_client *client, i2c_set_clientdata(client, data); data->bank = i2c_smbus_read_byte_data(client, W83793_REG_BANKSEL); mutex_init(&data->update_lock); + mutex_init(&data->watchdog_lock); + INIT_LIST_HEAD(&data->list); + kref_init(&data->kref); + + /* + * Store client pointer in our data struct for watchdog usage + * (where the client is found through a data ptr instead of the + * otherway around) + */ + data->client = client; err = w83793_detect_subclients(client); if (err) @@ -1227,8 +1708,8 @@ static int w83793_probe(struct i2c_client *client, w83793_init_client(client); /* - Only fan 1-5 has their own input pins, - Pwm 1-3 has their own pins + * Only fan 1-5 has their own input pins, + * Pwm 1-3 has their own pins */ data->has_fan = 0x1f; data->has_pwm = 0x07; @@ -1290,7 +1771,7 @@ static int w83793_probe(struct i2c_client *client, } /* check the temp1-6 mode, ignore former AMDSI selected inputs */ - tmp = w83793_read_value(client,W83793_REG_TEMP_MODE[0]); + tmp = w83793_read_value(client, W83793_REG_TEMP_MODE[0]); if (tmp & 0x01) data->has_temp |= 0x01; if (tmp & 0x04) @@ -1300,7 +1781,7 @@ static int w83793_probe(struct i2c_client *client, if (tmp & 0x40) data->has_temp |= 0x08; - tmp = w83793_read_value(client,W83793_REG_TEMP_MODE[1]); + tmp = w83793_read_value(client, W83793_REG_TEMP_MODE[1]); if (tmp & 0x01) data->has_temp |= 0x10; if (tmp & 0x02) @@ -1380,8 +1861,81 @@ static int w83793_probe(struct i2c_client *client, goto exit_remove; } + /* Watchdog initialization */ + + /* Register boot notifier */ + err = register_reboot_notifier(&watchdog_notifier); + if (err != 0) { + dev_err(&client->dev, + "cannot register reboot notifier (err=%d)\n", err); + goto exit_devunreg; + } + + /* + * Enable Watchdog registers. + * Set Configuration Register to Enable Watch Dog Registers + * (Bit 2) = XXXX, X1XX. + */ + tmp = w83793_read_value(client, W83793_REG_CONFIG); + w83793_write_value(client, W83793_REG_CONFIG, tmp | 0x04); + + /* Set the default watchdog timeout */ + data->watchdog_timeout = timeout; + + /* Check, if last reboot was caused by watchdog */ + data->watchdog_caused_reboot = + w83793_read_value(data->client, W83793_REG_WDT_STATUS) & 0x01; + + /* Disable Soft Watchdog during initialiation */ + watchdog_disable(data); + + /* + * We take the data_mutex lock early so that watchdog_open() cannot + * run when misc_register() has completed, but we've not yet added + * our data to the watchdog_data_list (and set the default timeout) + */ + mutex_lock(&watchdog_data_mutex); + for (i = 0; i < ARRAY_SIZE(watchdog_minors); i++) { + /* Register our watchdog part */ + snprintf(data->watchdog_name, sizeof(data->watchdog_name), + "watchdog%c", (i == 0) ? '\0' : ('0' + i)); + data->watchdog_miscdev.name = data->watchdog_name; + data->watchdog_miscdev.fops = &watchdog_fops; + data->watchdog_miscdev.minor = watchdog_minors[i]; + + err = misc_register(&data->watchdog_miscdev); + if (err == -EBUSY) + continue; + if (err) { + data->watchdog_miscdev.minor = 0; + dev_err(&client->dev, + "Registering watchdog chardev: %d\n", err); + break; + } + + list_add(&data->list, &watchdog_data_list); + + dev_info(&client->dev, + "Registered watchdog chardev major 10, minor: %d\n", + watchdog_minors[i]); + break; + } + if (i == ARRAY_SIZE(watchdog_minors)) { + data->watchdog_miscdev.minor = 0; + dev_warn(&client->dev, + "Couldn't register watchdog chardev (due to no free minor)\n"); + } + + mutex_unlock(&watchdog_data_mutex); + return 0; + /* Unregister hwmon device */ + +exit_devunreg: + + hwmon_device_unregister(data->hwmon_dev); + /* Unregister sysfs hooks */ exit_remove: @@ -1419,9 +1973,9 @@ static void w83793_update_nonvolatile(struct device *dev) struct w83793_data *data = i2c_get_clientdata(client); int i, j; /* - They are somewhat "stable" registers, and to update them everytime - takes so much time, it's just not worthy. Update them in a long - interval to avoid exception. + * They are somewhat "stable" registers, and to update them every time + * takes so much time, it's just not worthy. Update them in a long + * interval to avoid exception. */ if (!(time_after(jiffies, data->last_nonvolatile + HZ * 300) || !data->valid)) @@ -1438,9 +1992,8 @@ static void w83793_update_nonvolatile(struct device *dev) for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) { /* Update the Fan measured value and limits */ - if (!(data->has_fan & (1 << i))) { + if (!(data->has_fan & (1 << i))) continue; - } data->fan_min[i] = w83793_read_value(client, W83793_REG_FAN_MIN(i)) << 8; data->fan_min[i] |= @@ -1495,9 +2048,8 @@ static void w83793_update_nonvolatile(struct device *dev) w83793_read_value(client, W83793_REG_TEMP_CRITICAL); data->beep_enable = w83793_read_value(client, W83793_REG_OVT_BEEP); - for (i = 0; i < ARRAY_SIZE(data->beeps); i++) { + for (i = 0; i < ARRAY_SIZE(data->beeps); i++) data->beeps[i] = w83793_read_value(client, W83793_REG_BEEP(i)); - } data->last_nonvolatile = jiffies; } @@ -1523,9 +2075,8 @@ static struct w83793_data *w83793_update_device(struct device *dev) w83793_read_value(client, W83793_REG_IN_LOW_BITS[IN_READ]); for (i = 0; i < ARRAY_SIZE(data->fan); i++) { - if (!(data->has_fan & (1 << i))) { + if (!(data->has_fan & (1 << i))) continue; - } data->fan[i] = w83793_read_value(client, W83793_REG_FAN(i)) << 8; data->fan[i] |= @@ -1565,8 +2116,10 @@ END: return data; } -/* Ignore the possibility that somebody change bank outside the driver - Must be called with data->update_lock held, except during initialization */ +/* + * Ignore the possibility that somebody change bank outside the driver + * Must be called with data->update_lock held, except during initialization + */ static u8 w83793_read_value(struct i2c_client *client, u16 reg) { struct w83793_data *data = i2c_get_clientdata(client); @@ -1601,16 +2154,16 @@ static int w83793_write_value(struct i2c_client *client, u16 reg, u8 value) new_bank |= data->bank & 0xfc; if (data->bank != new_bank) { - if ((res = i2c_smbus_write_byte_data - (client, W83793_REG_BANKSEL, new_bank)) >= 0) - data->bank = new_bank; - else { + res = i2c_smbus_write_byte_data(client, W83793_REG_BANKSEL, + new_bank); + if (res < 0) { dev_err(&client->dev, "set bank to %d failed, fall back " "to bank %d, write reg 0x%x error\n", new_bank, data->bank, reg); goto END; } + data->bank = new_bank; } res = i2c_smbus_write_byte_data(client, reg & 0xff, value); @@ -1618,19 +2171,8 @@ END: return res; } -static int __init sensors_w83793_init(void) -{ - return i2c_add_driver(&w83793_driver); -} - -static void __exit sensors_w83793_exit(void) -{ - i2c_del_driver(&w83793_driver); -} +module_i2c_driver(w83793_driver); -MODULE_AUTHOR("Yuan Mu"); +MODULE_AUTHOR("Yuan Mu, Sven Anders"); MODULE_DESCRIPTION("w83793 driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_w83793_init); -module_exit(sensors_w83793_exit); diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c new file mode 100644 index 00000000000..21894131190 --- /dev/null +++ b/drivers/hwmon/w83795.c @@ -0,0 +1,2287 @@ +/* + * w83795.c - Linux kernel driver for hardware monitoring + * Copyright (C) 2008 Nuvoton Technology Corp. + * Wei Song + * Copyright (C) 2010 Jean Delvare <jdelvare@suse.de> + * + * 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/jiffies.h> + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { + 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END +}; + + +static bool 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_CTRL 0x40 +#define ALARM_CTRL_RTSACS (1 << 7) +#define W83795_REG_ALARM(index) (0x41 + (index)) +#define W83795_REG_CLR_CHASSIS 0x4D +#define W83795_REG_BEEP(index) (0x50 + (index)) + +#define W83795_REG_OVT_CFG 0x58 +#define OVT_CFG_SEL (1 << 7) + + +#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 TSS_MAP_RESERVED 0xff +static const u8 tss_map[4][6] = { + { 0, 1, 2, 3, 4, 5}, + { 6, 7, 8, 9, 0, 1}, + {10, 11, 12, 13, 2, 3}, + { 4, 5, 4, 5, TSS_MAP_RESERVED, TSS_MAP_RESERVED}, +}; + +#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 clamp_val((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 clamp_val((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 clamp_val(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 = clamp_val(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 enable_beep; + 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; + u8 lsb; + + /* 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 */ + lsb = 0; /* Silent false gcc warning */ + for (i = 0; i < ARRAY_SIZE(data->fan); i++) { + /* + * 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 */ + if (data->enable_beep) { + 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; + u8 intrusion; + 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 intrusion and alarms + * It is important to read intrusion first, because reading from + * register SMI STS6 clears the interrupt status temporarily. + */ + tmp = w83795_read(client, W83795_REG_ALARM_CTRL); + /* Switch to interrupt status for intrusion if needed */ + if (tmp & ALARM_CTRL_RTSACS) + w83795_write(client, W83795_REG_ALARM_CTRL, + tmp & ~ALARM_CTRL_RTSACS); + intrusion = w83795_read(client, W83795_REG_ALARM(5)) & (1 << 6); + /* Switch to real-time alarms */ + w83795_write(client, W83795_REG_ALARM_CTRL, tmp | ALARM_CTRL_RTSACS); + for (i = 0; i < ARRAY_SIZE(data->alarms); i++) + data->alarms[i] = w83795_read(client, W83795_REG_ALARM(i)); + data->alarms[5] |= intrusion; + /* Restore original configuration if needed */ + if (!(tmp & ALARM_CTRL_RTSACS)) + w83795_write(client, W83795_REG_ALARM_CTRL, + tmp & ~ALARM_CTRL_RTSACS); + + 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 (kstrtoul(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 (kstrtoul(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); + + /* Clear status and force cache refresh */ + w83795_read(client, W83795_REG_ALARM(5)); + data->valid = 0; + 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 (kstrtoul(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 (kstrtoul(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 = clamp_val(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; + + /* Speed cruise mode */ + if (data->pwm_fcms[0] & (1 << index)) { + tmp = 2; + goto out; + } + /* Thermal cruise or SmartFan IV mode */ + for (tmp = 0; tmp < 6; tmp++) { + if (data->pwm_tfmr[tmp] & (1 << index)) { + tmp = 3; + goto out; + } + } + /* Manual mode */ + 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 (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + if (val < 1 || val > 2) + return -EINVAL; + +#ifndef CONFIG_SENSORS_W83795_FANCTRL + if (val > 1) { + dev_warn(dev, "Automatic fan speed control support disabled\n"); + dev_warn(dev, "Build with CONFIG_SENSORS_W83795_FANCTRL=y if you want it\n"); + return -EOPNOTSUPP; + } +#endif + + mutex_lock(&data->update_lock); + switch (val) { + case 1: + /* Clear speed cruise mode bits */ + data->pwm_fcms[0] &= ~(1 << index); + w83795_write(client, W83795_REG_FCMS1, data->pwm_fcms[0]); + /* Clear thermal cruise mode bits */ + for (i = 0; i < 6; i++) { + data->pwm_tfmr[i] &= ~(1 << index); + w83795_write(client, W83795_REG_TFMR(i), + data->pwm_tfmr[i]); + } + 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_pwm_mode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83795_data *data = w83795_update_pwm_config(dev); + int index = to_sensor_dev_attr_2(attr)->index; + unsigned int mode; + + if (data->pwm_fomc & (1 << index)) + mode = 0; /* DC */ + else + mode = 1; /* PWM */ + + return sprintf(buf, "%u\n", mode); +} + +/* + * Check whether a given temperature source can ever be useful. + * Returns the number of selectable temperature channels which are + * enabled. + */ +static int w83795_tss_useful(const struct w83795_data *data, int tsrc) +{ + int useful = 0, i; + + for (i = 0; i < 4; i++) { + if (tss_map[i][tsrc] == TSS_MAP_RESERVED) + continue; + if (tss_map[i][tsrc] < 6) /* Analog */ + useful += (data->has_temp >> tss_map[i][tsrc]) & 1; + else /* Digital */ + useful += (data->has_dts >> (tss_map[i][tsrc] - 6)) & 1; + } + + return useful; +} + +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 tmp = data->temp_src[index / 2]; + + if (index & 1) + tmp >>= 4; /* Pick high nibble */ + else + tmp &= 0x0f; /* Pick low nibble */ + + /* Look-up the actual temperature channel number */ + if (tmp >= 4 || tss_map[tmp][index] == TSS_MAP_RESERVED) + return -EINVAL; /* Shouldn't happen */ + + return sprintf(buf, "%u\n", (unsigned int)tss_map[tmp][index] + 1); +} + +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; + int tmp; + unsigned long channel; + u8 val = index / 2; + + if (kstrtoul(buf, 10, &channel) < 0 || + channel < 1 || channel > 14) + return -EINVAL; + + /* Check if request can be fulfilled */ + for (tmp = 0; tmp < 4; tmp++) { + if (tss_map[tmp][index] == channel - 1) + break; + } + if (tmp == 4) /* No match */ + return -EINVAL; + + 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 (kstrtoul(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 = clamp_val(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 (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + switch (nr) { + case FANIN_TARGET: + val = fan_to_reg(clamp_val(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 = clamp_val(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 (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + val /= 1000; + + mutex_lock(&data->update_lock); + switch (nr) { + case TEMP_PWM_TTTI: + val = clamp_val(val, 0, 0x7f); + w83795_write(client, W83795_REG_TTTI(index), val); + break; + case TEMP_PWM_CTFS: + val = clamp_val(val, 0, 0x7f); + w83795_write(client, W83795_REG_CTFS(index), val); + break; + case TEMP_PWM_HCT: + val = clamp_val(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 = clamp_val(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 (kstrtoul(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 (kstrtoul(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 (kstrtol(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 (kstrtol(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 (kstrtoul(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 (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + val = in_to_reg(index, val); + + if ((index >= 17) && + !((data->has_gain >> (index - 17)) & 1)) + val /= 8; + val = clamp_val(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 (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + + switch (nr) { + case SETUP_PWM_DEFAULT: + val = clamp_val(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, _min and _beep 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)) } + +/* + * Don't change the attribute order, _beep is accessed by index + * somewhere else in the code + */ +#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##_enable, S_IWUSR | S_IRUGO, \ + show_pwm_enable, store_pwm_enable, NOT_USED, index - 1), \ + SENSOR_ATTR_2(pwm##index##_mode, S_IRUGO, \ + show_pwm_mode, NULL, NOT_USED, 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##_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(fan##index##_target, S_IWUSR | S_IRUGO, \ + show_fanin, store_fanin, FANIN_TARGET, index - 1) } + +/* + * Don't change the attribute order, _beep is accessed by index + * somewhere else in the code + */ +#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) } + +/* + * Don't change the attribute order, _beep is accessed by index + * somewhere else in the code + */ +#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##_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[][28] = { + 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[][8] = { + 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 w83795_tss[6] = { + SENSOR_ATTR_2(temp1_source_sel, S_IWUSR | S_IRUGO, + show_temp_src, store_temp_src, NOT_USED, 0), + SENSOR_ATTR_2(temp2_source_sel, S_IWUSR | S_IRUGO, + show_temp_src, store_temp_src, NOT_USED, 1), + SENSOR_ATTR_2(temp3_source_sel, S_IWUSR | S_IRUGO, + show_temp_src, store_temp_src, NOT_USED, 2), + SENSOR_ATTR_2(temp4_source_sel, S_IWUSR | S_IRUGO, + show_temp_src, store_temp_src, NOT_USED, 3), + SENSOR_ATTR_2(temp5_source_sel, S_IWUSR | S_IRUGO, + show_temp_src, store_temp_src, NOT_USED, 4), + SENSOR_ATTR_2(temp6_source_sel, S_IWUSR | S_IRUGO, + show_temp_src, store_temp_src, NOT_USED, 5), +}; + +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), +#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 +}; + +static const struct sensor_device_attribute_2 sda_beep_files[] = { + 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), +}; + +/* + * 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; +} + +#ifdef CONFIG_SENSORS_W83795_FANCTRL +#define NUM_PWM_ATTRIBUTES ARRAY_SIZE(w83795_pwm[0]) +#define NUM_TEMP_ATTRIBUTES ARRAY_SIZE(w83795_temp[0]) +#else +#define NUM_PWM_ATTRIBUTES 4 +#define NUM_TEMP_ATTRIBUTES 8 +#endif + +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++) { + if (j == 4 && !data->enable_beep) + continue; + 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++) { + if (j == 3 && !data->enable_beep) + continue; + err = fn(dev, &w83795_fan[i][j].dev_attr); + if (err) + return err; + } + } + + for (i = 0; i < ARRAY_SIZE(w83795_tss); i++) { + j = w83795_tss_useful(data, i); + if (!j) + continue; + err = fn(dev, &w83795_tss[i].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; + } + + if (data->enable_beep) { + for (i = 0; i < ARRAY_SIZE(sda_beep_files); i++) { + err = fn(dev, &sda_beep_files[i].dev_attr); + if (err) + return err; + } + } + + for (i = 0; i < data->has_pwm; i++) { + for (j = 0; j < NUM_PWM_ATTRIBUTES; j++) { + err = fn(dev, &w83795_pwm[i][j].dev_attr); + if (err) + return err; + } + } + + for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) { + if (!(data->has_temp & (1 << i))) + continue; + for (j = 0; j < NUM_TEMP_ATTRIBUTES; j++) { + if (j == 7 && !data->enable_beep) + continue; + 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++) { + if (j == 7 && !data->enable_beep) + continue; + 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 = devm_kzalloc(dev, sizeof(struct w83795_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + 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; + + /* Check if BEEP pin is available */ + if (data->chip_type == w83795g) { + /* The W83795G has a dedicated BEEP pin */ + data->enable_beep = 1; + } else { + /* + * The W83795ADG has a shared pin for OVT# and BEEP, so you + * can't have both + */ + tmp = w83795_read(client, W83795_REG_OVT_CFG); + if ((tmp & OVT_CFG_SEL) == 0) + data->enable_beep = 1; + } + + 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); + 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); + + 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, +}; + +module_i2c_driver(w83795_driver); + +MODULE_AUTHOR("Wei Song, Jean Delvare <jdelvare@suse.de>"); +MODULE_DESCRIPTION("W83795G/ADG hardware monitoring driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/w83l785ts.c b/drivers/hwmon/w83l785ts.c index 20781def65e..ac304312201 100644 --- a/drivers/hwmon/w83l785ts.c +++ b/drivers/hwmon/w83l785ts.c @@ -1,7 +1,7 @@ /* * w83l785ts.c - Part of lm_sensors, Linux kernel modules for hardware * monitoring - * Copyright (C) 2003-2009 Jean Delvare <khali@linux-fr.org> + * Copyright (C) 2003-2009 Jean Delvare <jdelvare@suse.de> * * Inspired from the lm83 driver. The W83L785TS-S is a sensor chip made * by Winbond. It reports a single external temperature with a 1 deg @@ -10,7 +10,7 @@ * http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83L785TS-S.pdf * * Ported to Linux 2.6 by Wolfgang Ziegler <nuppla@gmx.at> and Jean Delvare - * <khali@linux-fr.org>. + * <jdelvare@suse.de>. * * Thanks to James Bolt <james@evilpenguin.com> for benchmarking the read * error handling mechanism. @@ -86,7 +86,7 @@ static struct w83l785ts_data *w83l785ts_update_device(struct device *dev); /* * Driver data (common to all clients) */ - + static const struct i2c_device_id w83l785ts_id[] = { { "w83l785ts", 0 }, { } @@ -116,8 +116,7 @@ struct w83l785ts_data { unsigned long last_updated; /* in jiffies */ /* registers values */ - s8 temp[2]; /* 0: input - 1: critical limit */ + s8 temp[2]; /* 0: input, 1: critical limit */ }; /* @@ -177,42 +176,35 @@ static int w83l785ts_detect(struct i2c_client *client, return 0; } -static int w83l785ts_probe(struct i2c_client *new_client, +static int w83l785ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct w83l785ts_data *data; - int err = 0; + struct device *dev = &client->dev; + int err; - data = kzalloc(sizeof(struct w83l785ts_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(dev, sizeof(struct w83l785ts_data), GFP_KERNEL); + if (!data) + return -ENOMEM; - i2c_set_clientdata(new_client, data); - data->valid = 0; + i2c_set_clientdata(client, data); mutex_init(&data->update_lock); - /* Default values in case the first read fails (unlikely). */ - data->temp[1] = data->temp[0] = 0; - /* * Initialize the W83L785TS chip * Nothing yet, assume it is already started. */ - err = device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_input.dev_attr); + err = device_create_file(dev, &sensor_dev_attr_temp1_input.dev_attr); if (err) - goto exit_remove; + return err; - err = device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_max.dev_attr); + err = device_create_file(dev, &sensor_dev_attr_temp1_max.dev_attr); if (err) goto exit_remove; /* Register sysfs hooks */ - data->hwmon_dev = hwmon_device_register(&new_client->dev); + data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); goto exit_remove; @@ -221,12 +213,8 @@ static int w83l785ts_probe(struct i2c_client *new_client, return 0; exit_remove: - device_remove_file(&new_client->dev, - &sensor_dev_attr_temp1_input.dev_attr); - device_remove_file(&new_client->dev, - &sensor_dev_attr_temp1_max.dev_attr); - kfree(data); -exit: + device_remove_file(dev, &sensor_dev_attr_temp1_input.dev_attr); + device_remove_file(dev, &sensor_dev_attr_temp1_max.dev_attr); return err; } @@ -240,7 +228,6 @@ static int w83l785ts_remove(struct i2c_client *client) device_remove_file(&client->dev, &sensor_dev_attr_temp1_max.dev_attr); - kfree(data); return 0; } @@ -250,8 +237,10 @@ static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval) struct device *dev; const char *prefix; - /* We might be called during detection, at which point the client - isn't yet fully initialized, so we can't use dev_dbg on it */ + /* + * We might be called during detection, at which point the client + * isn't yet fully initialized, so we can't use dev_dbg on it + */ if (i2c_get_clientdata(client)) { dev = &client->dev; prefix = ""; @@ -260,9 +249,11 @@ static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval) prefix = "w83l785ts: "; } - /* Frequent read errors have been reported on Asus boards, so we + /* + * Frequent read errors have been reported on Asus boards, so we * retry on read errors. If it still fails (unlikely), return the - * default value requested by the caller. */ + * default value requested by the caller. + */ for (i = 1; i <= MAX_RETRIES; i++) { value = i2c_smbus_read_byte_data(client, reg); if (value >= 0) { @@ -302,19 +293,8 @@ static struct w83l785ts_data *w83l785ts_update_device(struct device *dev) return data; } -static int __init sensors_w83l785ts_init(void) -{ - return i2c_add_driver(&w83l785ts_driver); -} - -static void __exit sensors_w83l785ts_exit(void) -{ - i2c_del_driver(&w83l785ts_driver); -} +module_i2c_driver(w83l785ts_driver); -MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); +MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); MODULE_DESCRIPTION("W83L785TS-S driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_w83l785ts_init); -module_exit(sensors_w83l785ts_exit); diff --git a/drivers/hwmon/w83l786ng.c b/drivers/hwmon/w83l786ng.c index 0254e181893..32487c19cbf 100644 --- a/drivers/hwmon/w83l786ng.c +++ b/drivers/hwmon/w83l786ng.c @@ -1,28 +1,28 @@ /* - w83l786ng.c - Linux kernel driver for hardware monitoring - Copyright (c) 2007 Kevin Lo <kevlo@kevlo.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. -*/ + * w83l786ng.c - Linux kernel driver for hardware monitoring + * Copyright (c) 2007 Kevin Lo <kevlo@kevlo.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 wchipid vendid i2c ISA - w83l786ng 3 2 2 2 0x7b 0x5ca3 yes no -*/ + * Supports following chips: + * + * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA + * w83l786ng 3 2 2 2 0x7b 0x5ca3 yes no + */ #include <linux/module.h> #include <linux/init.h> @@ -33,13 +33,14 @@ #include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> +#include <linux/jiffies.h> /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x2e, 0x2f, I2C_CLIENT_END }; /* Insmod parameters */ -static int reset; +static bool reset; module_param(reset, bool, 0); MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended"); @@ -52,7 +53,7 @@ MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended"); #define W83L786NG_REG_CONFIG 0x40 #define W83L786NG_REG_ALARM1 0x41 -#define W83L786NG_REG_ALARM2 0x42 +#define W83L786NG_REG_ALARM2 0x42 #define W83L786NG_REG_GPIO_EN 0x47 #define W83L786NG_REG_MAN_ID2 0x4C #define W83L786NG_REG_MAN_ID1 0x4D @@ -85,23 +86,26 @@ 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); + rpm = clamp_val(rpm, 1, 1000000); + return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254); } -#define FAN_FROM_REG(val,div) ((val) == 0 ? -1 : \ +#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : \ ((val) == 255 ? 0 : \ 1350000 / ((val) * (div)))) /* for temp */ -#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val) < 0 ? (val)+0x100*1000 \ - : (val)) / 1000, 0, 0xff)) -#define TEMP_FROM_REG(val) (((val) & 0x80 ? (val)-0x100 : (val)) * 1000) - -/* The analog voltage inputs have 8mV LSB. Since the sysfs output is - in mV as would be measured on the chip input pin, need to just - multiply/divide by 8 to translate from/to register values. */ -#define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 4) / 8), 0, 255)) +#define TEMP_TO_REG(val) (clamp_val(((val) < 0 ? (val) + 0x100 * 1000 \ + : (val)) / 1000, 0, 0xff)) +#define TEMP_FROM_REG(val) (((val) & 0x80 ? \ + (val) - 0x100 : (val)) * 1000) + +/* + * The analog voltage inputs have 8mV LSB. Since the sysfs output is + * in mV as would be measured on the chip input pin, need to just + * multiply/divide by 8 to translate from/to register values. + */ +#define IN_TO_REG(val) (clamp_val((((val) + 4) / 8), 0, 255)) #define IN_FROM_REG(val) ((val) * 8) #define DIV_FROM_REG(val) (1 << (val)) @@ -110,13 +114,13 @@ static inline u8 DIV_TO_REG(long val) { int i; - val = SENSORS_LIMIT(val, 1, 128) >> 1; + val = clamp_val(val, 1, 128) >> 1; for (i = 0; i < 7; i++) { if (val == 0) break; val >>= 1; } - return ((u8) i); + return (u8)i; } struct w83l786ng_data { @@ -125,7 +129,7 @@ struct w83l786ng_data { char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ unsigned long last_nonvolatile; /* In jiffies, last time we update the - nonvolatile registers */ + * nonvolatile registers */ u8 in[3]; u8 in_max[3]; @@ -137,10 +141,10 @@ struct w83l786ng_data { u8 temp[2][3]; u8 pwm[2]; u8 pwm_mode[2]; /* 0->DC variable voltage - 1->PWM variable duty cycle */ + * 1->PWM variable duty cycle */ u8 pwm_enable[2]; /* 1->manual - 2->thermal cruise (also called SmartFan I) */ + * 2->thermal cruise (also called SmartFan I) */ u8 tolerance[2]; }; @@ -186,11 +190,11 @@ w83l786ng_write_value(struct i2c_client *client, u8 reg, u8 value) #define show_in_reg(reg) \ static ssize_t \ show_##reg(struct device *dev, struct device_attribute *attr, \ - char *buf) \ + char *buf) \ { \ int nr = to_sensor_dev_attr(attr)->index; \ struct w83l786ng_data *data = w83l786ng_update_device(dev); \ - return sprintf(buf,"%d\n", IN_FROM_REG(data->reg[nr])); \ + return sprintf(buf, "%d\n", IN_FROM_REG(data->reg[nr])); \ } show_in_reg(in) @@ -199,13 +203,16 @@ show_in_reg(in_max) #define store_in_reg(REG, reg) \ static ssize_t \ -store_in_##reg (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ +store_in_##reg(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ { \ int nr = to_sensor_dev_attr(attr)->index; \ struct i2c_client *client = to_i2c_client(dev); \ struct w83l786ng_data *data = i2c_get_clientdata(client); \ - unsigned long val = simple_strtoul(buf, NULL, 10); \ + unsigned long val; \ + int err = kstrtoul(buf, 10, &val); \ + if (err) \ + return err; \ mutex_lock(&data->update_lock); \ data->in_##reg[nr] = IN_TO_REG(val); \ w83l786ng_write_value(client, W83L786NG_REG_IN_##REG(nr), \ @@ -241,8 +248,8 @@ static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ { \ int nr = to_sensor_dev_attr(attr)->index; \ struct w83l786ng_data *data = w83l786ng_update_device(dev); \ - return sprintf(buf,"%d\n", \ - FAN_FROM_REG(data->fan[nr], DIV_FROM_REG(data->fan_div[nr]))); \ + return sprintf(buf, "%d\n", \ + FAN_FROM_REG(data->reg[nr], DIV_FROM_REG(data->fan_div[nr]))); \ } show_fan_reg(fan); @@ -255,9 +262,13 @@ store_fan_min(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct w83l786ng_data *data = i2c_get_clientdata(client); - u32 val; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; - val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); w83l786ng_write_value(client, W83L786NG_REG_FAN_MIN(nr), @@ -276,10 +287,12 @@ show_fan_div(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%u\n", DIV_FROM_REG(data->fan_div[nr])); } -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan divisor. This follows the principle of - least surprise; the user doesn't expect the fan minimum to change just - because the divisor changed. */ +/* + * Note: we save and restore the fan minimum here, because its value is + * determined in part by the fan divisor. This follows the principle of + * least surprise; the user doesn't expect the fan minimum to change just + * because the divisor changed. + */ static ssize_t store_fan_div(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -294,11 +307,18 @@ store_fan_div(struct device *dev, struct device_attribute *attr, u8 keep_mask = 0; u8 new_shift = 0; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + /* Save fan_min */ mutex_lock(&data->update_lock); min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])); - data->fan_div[nr] = DIV_TO_REG(simple_strtoul(buf, NULL, 10)); + data->fan_div[nr] = DIV_TO_REG(val); switch (nr) { case 0: @@ -371,16 +391,20 @@ store_temp(struct device *dev, struct device_attribute *attr, int index = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct w83l786ng_data *data = i2c_get_clientdata(client); - s32 val; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; - val = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); data->temp[nr][index] = TEMP_TO_REG(val); w83l786ng_write_value(client, W83L786NG_REG_TEMP[nr][index], data->temp[nr][index]); mutex_unlock(&data->update_lock); - return count; + return count; } static struct sensor_device_attribute_2 sda_temp_input[] = { @@ -403,8 +427,8 @@ static struct sensor_device_attribute_2 sda_temp_max_hyst[] = { }; #define show_pwm_reg(reg) \ -static ssize_t show_##reg (struct device *dev, struct device_attribute *attr, \ - char *buf) \ +static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ + char *buf) \ { \ struct w83l786ng_data *data = w83l786ng_update_device(dev); \ int nr = to_sensor_dev_attr(attr)->index; \ @@ -422,8 +446,13 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct w83l786ng_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); u8 reg; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; if (val > 1) return -EINVAL; @@ -445,10 +474,18 @@ store_pwm(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct w83l786ng_data *data = i2c_get_clientdata(client); - u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 255); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + val = clamp_val(val, 0, 255); + val = DIV_ROUND_CLOSEST(val, 0x11); mutex_lock(&data->update_lock); - data->pwm[nr] = val; + data->pwm[nr] = val * 0x11; + val |= w83l786ng_read_value(client, W83L786NG_REG_PWM[nr]) & 0xf0; w83l786ng_write_value(client, W83L786NG_REG_PWM[nr], val); mutex_unlock(&data->update_lock); return count; @@ -461,17 +498,21 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct w83l786ng_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); - u8 reg; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; - if (!val || (val > 2)) /* only modes 1 and 2 are supported */ + if (!val || val > 2) /* only modes 1 and 2 are supported */ return -EINVAL; mutex_lock(&data->update_lock); reg = w83l786ng_read_value(client, W83L786NG_REG_FAN_CFG); data->pwm_enable[nr] = val; - reg &= ~(0x02 << W83L786NG_PWM_ENABLE_SHIFT[nr]); + reg &= ~(0x03 << W83L786NG_PWM_ENABLE_SHIFT[nr]); reg |= (val - 1) << W83L786NG_PWM_ENABLE_SHIFT[nr]; w83l786ng_write_value(client, W83L786NG_REG_FAN_CFG, reg); mutex_unlock(&data->update_lock); @@ -513,20 +554,22 @@ store_tolerance(struct device *dev, struct device_attribute *attr, int nr = to_sensor_dev_attr(attr)->index; struct i2c_client *client = to_i2c_client(dev); struct w83l786ng_data *data = i2c_get_clientdata(client); - u32 val; u8 tol_tmp, tol_mask; + unsigned long val; + int err; - val = simple_strtoul(buf, NULL, 10); + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); tol_mask = w83l786ng_read_value(client, W83L786NG_REG_TOLERANCE) & ((nr == 1) ? 0x0f : 0xf0); - tol_tmp = SENSORS_LIMIT(val, 0, 15); + tol_tmp = clamp_val(val, 0, 15); tol_tmp &= 0x0f; data->tolerance[nr] = tol_tmp; - if (nr == 1) { + if (nr == 1) tol_tmp <<= 4; - } w83l786ng_write_value(client, W83L786NG_REG_TOLERANCE, tol_mask | tol_tmp); @@ -591,9 +634,8 @@ w83l786ng_detect(struct i2c_client *client, struct i2c_board_info *info) u16 man_id; u8 chip_id; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - } /* Detection */ if ((w83l786ng_read_value(client, W83L786NG_REG_CONFIG) & 0x80)) { @@ -628,11 +670,10 @@ w83l786ng_probe(struct i2c_client *client, const struct i2c_device_id *id) int i, err = 0; u8 reg_tmp; - data = kzalloc(sizeof(struct w83l786ng_data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&client->dev, sizeof(struct w83l786ng_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; i2c_set_clientdata(client, data); mutex_init(&data->update_lock); @@ -652,7 +693,8 @@ w83l786ng_probe(struct i2c_client *client, const struct i2c_device_id *id) data->fan_div[1] = (reg_tmp >> 4) & 0x07; /* Register sysfs hooks */ - if ((err = sysfs_create_group(&client->dev.kobj, &w83l786ng_group))) + err = sysfs_create_group(&client->dev.kobj, &w83l786ng_group); + if (err) goto exit_remove; data->hwmon_dev = hwmon_device_register(dev); @@ -667,8 +709,6 @@ w83l786ng_probe(struct i2c_client *client, const struct i2c_device_id *id) exit_remove: sysfs_remove_group(&client->dev.kobj, &w83l786ng_group); - kfree(data); -exit: return err; } @@ -680,8 +720,6 @@ w83l786ng_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &w83l786ng_group); - kfree(data); - return 0; } @@ -740,9 +778,10 @@ static struct w83l786ng_data *w83l786ng_update_device(struct device *dev) ((pwmcfg >> W83L786NG_PWM_MODE_SHIFT[i]) & 1) ? 0 : 1; data->pwm_enable[i] = - ((pwmcfg >> W83L786NG_PWM_ENABLE_SHIFT[i]) & 2) + 1; - data->pwm[i] = w83l786ng_read_value(client, - W83L786NG_REG_PWM[i]); + ((pwmcfg >> W83L786NG_PWM_ENABLE_SHIFT[i]) & 3) + 1; + data->pwm[i] = + (w83l786ng_read_value(client, W83L786NG_REG_PWM[i]) + & 0x0f) * 0x11; } @@ -769,21 +808,8 @@ static struct w83l786ng_data *w83l786ng_update_device(struct device *dev) return data; } -static int __init -sensors_w83l786ng_init(void) -{ - return i2c_add_driver(&w83l786ng_driver); -} - -static void __exit -sensors_w83l786ng_exit(void) -{ - i2c_del_driver(&w83l786ng_driver); -} +module_i2c_driver(w83l786ng_driver); MODULE_AUTHOR("Kevin Lo"); MODULE_DESCRIPTION("w83l786ng driver"); MODULE_LICENSE("GPL"); - -module_init(sensors_w83l786ng_init); -module_exit(sensors_w83l786ng_exit); diff --git a/drivers/hwmon/wm831x-hwmon.c b/drivers/hwmon/wm831x-hwmon.c index c16e9e74c35..df6ceaf8d58 100644 --- a/drivers/hwmon/wm831x-hwmon.c +++ b/drivers/hwmon/wm831x-hwmon.c @@ -24,6 +24,7 @@ #include <linux/err.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> +#include <linux/slab.h> #include <linux/mfd/wm831x/core.h> #include <linux/mfd/wm831x/auxadc.h> @@ -39,7 +40,7 @@ static ssize_t show_name(struct device *dev, return sprintf(buf, "wm831x\n"); } -static const char *input_names[] = { +static const char * const input_names[] = { [WM831X_AUX_SYSVDD] = "SYSVDD", [WM831X_AUX_USB] = "USB", [WM831X_AUX_BKUP_BATT] = "Backup battery", @@ -116,8 +117,10 @@ static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_chip_temp, NULL, WM831X_AUX_CHIP_TEMP); static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, WM831X_AUX_CHIP_TEMP); -/* Report as a voltage since conversion depends on external components - * and that's what the ABI wants. */ +/* + * Report as a voltage since conversion depends on external components + * and that's what the ABI wants. + */ static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_voltage, NULL, WM831X_AUX_BATT_TEMP); static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL, @@ -154,13 +157,14 @@ static const struct attribute_group wm831x_attr_group = { .attrs = wm831x_attributes, }; -static int __devinit wm831x_hwmon_probe(struct platform_device *pdev) +static int wm831x_hwmon_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_hwmon *hwmon; int ret; - hwmon = kzalloc(sizeof(struct wm831x_hwmon), GFP_KERNEL); + hwmon = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_hwmon), + GFP_KERNEL); if (!hwmon) return -ENOMEM; @@ -168,7 +172,7 @@ static int __devinit wm831x_hwmon_probe(struct platform_device *pdev) ret = sysfs_create_group(&pdev->dev.kobj, &wm831x_attr_group); if (ret) - goto err; + return ret; hwmon->classdev = hwmon_device_register(&pdev->dev); if (IS_ERR(hwmon->classdev)) { @@ -182,43 +186,29 @@ static int __devinit wm831x_hwmon_probe(struct platform_device *pdev) err_sysfs: sysfs_remove_group(&pdev->dev.kobj, &wm831x_attr_group); -err: - kfree(hwmon); return ret; } -static int __devexit wm831x_hwmon_remove(struct platform_device *pdev) +static int wm831x_hwmon_remove(struct platform_device *pdev) { struct wm831x_hwmon *hwmon = platform_get_drvdata(pdev); hwmon_device_unregister(hwmon->classdev); sysfs_remove_group(&pdev->dev.kobj, &wm831x_attr_group); - platform_set_drvdata(pdev, NULL); - kfree(hwmon); return 0; } static struct platform_driver wm831x_hwmon_driver = { .probe = wm831x_hwmon_probe, - .remove = __devexit_p(wm831x_hwmon_remove), + .remove = wm831x_hwmon_remove, .driver = { .name = "wm831x-hwmon", .owner = THIS_MODULE, }, }; -static int __init wm831x_hwmon_init(void) -{ - return platform_driver_register(&wm831x_hwmon_driver); -} -module_init(wm831x_hwmon_init); - -static void __exit wm831x_hwmon_exit(void) -{ - platform_driver_unregister(&wm831x_hwmon_driver); -} -module_exit(wm831x_hwmon_exit); +module_platform_driver(wm831x_hwmon_driver); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_DESCRIPTION("WM831x Hardware Monitoring"); diff --git a/drivers/hwmon/wm8350-hwmon.c b/drivers/hwmon/wm8350-hwmon.c index 13290595ca8..64bf75c9442 100644 --- a/drivers/hwmon/wm8350-hwmon.c +++ b/drivers/hwmon/wm8350-hwmon.c @@ -34,7 +34,7 @@ static ssize_t show_name(struct device *dev, return sprintf(buf, "wm8350\n"); } -static const char *input_names[] = { +static const char * const input_names[] = { [WM8350_AUXADC_USB] = "USB", [WM8350_AUXADC_LINE] = "Line", [WM8350_AUXADC_BATT] = "Battery", @@ -91,7 +91,7 @@ static const struct attribute_group wm8350_attr_group = { .attrs = wm8350_attributes, }; -static int __devinit wm8350_hwmon_probe(struct platform_device *pdev) +static int wm8350_hwmon_probe(struct platform_device *pdev) { struct wm8350 *wm8350 = platform_get_drvdata(pdev); int ret; @@ -114,7 +114,7 @@ err: return ret; } -static int __devexit wm8350_hwmon_remove(struct platform_device *pdev) +static int wm8350_hwmon_remove(struct platform_device *pdev) { struct wm8350 *wm8350 = platform_get_drvdata(pdev); @@ -126,24 +126,14 @@ static int __devexit wm8350_hwmon_remove(struct platform_device *pdev) static struct platform_driver wm8350_hwmon_driver = { .probe = wm8350_hwmon_probe, - .remove = __devexit_p(wm8350_hwmon_remove), + .remove = wm8350_hwmon_remove, .driver = { .name = "wm8350-hwmon", .owner = THIS_MODULE, }, }; -static int __init wm8350_hwmon_init(void) -{ - return platform_driver_register(&wm8350_hwmon_driver); -} -module_init(wm8350_hwmon_init); - -static void __exit wm8350_hwmon_exit(void) -{ - platform_driver_unregister(&wm8350_hwmon_driver); -} -module_exit(wm8350_hwmon_exit); +module_platform_driver(wm8350_hwmon_driver); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_DESCRIPTION("WM8350 Hardware Monitoring"); |
