aboutsummaryrefslogtreecommitdiff
path: root/drivers/hwmon
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/Kconfig1142
-rw-r--r--drivers/hwmon/Makefile67
-rw-r--r--drivers/hwmon/ab8500.c206
-rw-r--r--drivers/hwmon/abituguru.c688
-rw-r--r--drivers/hwmon/abituguru3.c367
-rw-r--r--drivers/hwmon/abx500.c491
-rw-r--r--drivers/hwmon/abx500.h69
-rw-r--r--drivers/hwmon/acpi_power_meter.c1015
-rw-r--r--drivers/hwmon/ad7314.c170
-rw-r--r--drivers/hwmon/ad7414.c55
-rw-r--r--drivers/hwmon/ad7418.c70
-rw-r--r--drivers/hwmon/adc128d818.c491
-rw-r--r--drivers/hwmon/adcxx.c58
-rw-r--r--drivers/hwmon/adm1021.c268
-rw-r--r--drivers/hwmon/adm1025.c95
-rw-r--r--drivers/hwmon/adm1026.c604
-rw-r--r--drivers/hwmon/adm1029.c86
-rw-r--r--drivers/hwmon/adm1031.c340
-rw-r--r--drivers/hwmon/adm9240.c134
-rw-r--r--drivers/hwmon/ads1015.c322
-rw-r--r--drivers/hwmon/ads7828.c307
-rw-r--r--drivers/hwmon/ads7871.c251
-rw-r--r--drivers/hwmon/adt7310.c118
-rw-r--r--drivers/hwmon/adt7410.c80
-rw-r--r--drivers/hwmon/adt7411.c354
-rw-r--r--drivers/hwmon/adt7462.c110
-rw-r--r--drivers/hwmon/adt7470.c97
-rw-r--r--drivers/hwmon/adt7473.c1180
-rw-r--r--drivers/hwmon/adt7475.c155
-rw-r--r--drivers/hwmon/adt7x10.c511
-rw-r--r--drivers/hwmon/adt7x10.h37
-rw-r--r--drivers/hwmon/amc6821.c145
-rw-r--r--drivers/hwmon/ams/Makefile8
-rw-r--r--drivers/hwmon/ams/ams-core.c247
-rw-r--r--drivers/hwmon/ams/ams-i2c.c275
-rw-r--r--drivers/hwmon/ams/ams-input.c157
-rw-r--r--drivers/hwmon/ams/ams-pmu.c199
-rw-r--r--drivers/hwmon/ams/ams.h69
-rw-r--r--drivers/hwmon/applesmc.c1610
-rw-r--r--drivers/hwmon/asb100.c267
-rw-r--r--drivers/hwmon/asc7621.c1247
-rw-r--r--drivers/hwmon/asus_atk0110.c73
-rw-r--r--drivers/hwmon/atxp1.c227
-rw-r--r--drivers/hwmon/coretemp.c863
-rw-r--r--drivers/hwmon/da9052-hwmon.c330
-rw-r--r--drivers/hwmon/da9055-hwmon.c332
-rw-r--r--drivers/hwmon/dme1737.c1017
-rw-r--r--drivers/hwmon/ds1621.c364
-rw-r--r--drivers/hwmon/ds620.c298
-rw-r--r--drivers/hwmon/emc1403.c499
-rw-r--r--drivers/hwmon/emc2103.c730
-rw-r--r--drivers/hwmon/emc6w201.c553
-rw-r--r--drivers/hwmon/f71805f.c281
-rw-r--r--drivers/hwmon/f71882fg.c1141
-rw-r--r--drivers/hwmon/f75375s.c425
-rw-r--r--drivers/hwmon/fam15h_power.c267
-rw-r--r--drivers/hwmon/fschmd.c267
-rw-r--r--drivers/hwmon/g760a.c66
-rw-r--r--drivers/hwmon/g762.c1149
-rw-r--r--drivers/hwmon/gl518sm.c145
-rw-r--r--drivers/hwmon/gl520sm.c263
-rw-r--r--drivers/hwmon/gpio-fan.c606
-rw-r--r--drivers/hwmon/hdaps.c636
-rw-r--r--drivers/hwmon/hih6130.c306
-rw-r--r--drivers/hwmon/hp_accel.c392
-rw-r--r--drivers/hwmon/htu21.c199
-rw-r--r--drivers/hwmon/hwmon-vid.c218
-rw-r--r--drivers/hwmon/hwmon.c263
-rw-r--r--drivers/hwmon/i5k_amb.c93
-rw-r--r--drivers/hwmon/ibmaem.c169
-rw-r--r--drivers/hwmon/ibmpex.c28
-rw-r--r--drivers/hwmon/iio_hwmon.c186
-rw-r--r--drivers/hwmon/ina209.c626
-rw-r--r--drivers/hwmon/ina2xx.c292
-rw-r--r--drivers/hwmon/it87.c2488
-rw-r--r--drivers/hwmon/jc42.c554
-rw-r--r--drivers/hwmon/jz4740-hwmon.c184
-rw-r--r--drivers/hwmon/k10temp.c50
-rw-r--r--drivers/hwmon/k8temp.c139
-rw-r--r--drivers/hwmon/lineage-pem.c575
-rw-r--r--drivers/hwmon/lis3lv02d.c610
-rw-r--r--drivers/hwmon/lis3lv02d.h243
-rw-r--r--drivers/hwmon/lis3lv02d_i2c.c183
-rw-r--r--drivers/hwmon/lis3lv02d_spi.c143
-rw-r--r--drivers/hwmon/lm63.c1022
-rw-r--r--drivers/hwmon/lm70.c124
-rw-r--r--drivers/hwmon/lm73.c219
-rw-r--r--drivers/hwmon/lm75.c404
-rw-r--r--drivers/hwmon/lm75.h10
-rw-r--r--drivers/hwmon/lm77.c514
-rw-r--r--drivers/hwmon/lm78.c552
-rw-r--r--drivers/hwmon/lm80.c756
-rw-r--r--drivers/hwmon/lm83.c204
-rw-r--r--drivers/hwmon/lm85.c585
-rw-r--r--drivers/hwmon/lm87.c978
-rw-r--r--drivers/hwmon/lm90.c1342
-rw-r--r--drivers/hwmon/lm92.c316
-rw-r--r--drivers/hwmon/lm93.c829
-rw-r--r--drivers/hwmon/lm95234.c729
-rw-r--r--drivers/hwmon/lm95241.c586
-rw-r--r--drivers/hwmon/lm95245.c507
-rw-r--r--drivers/hwmon/ltc2945.c519
-rw-r--r--drivers/hwmon/ltc4151.c215
-rw-r--r--drivers/hwmon/ltc4215.c139
-rw-r--r--drivers/hwmon/ltc4222.c237
-rw-r--r--drivers/hwmon/ltc4245.c324
-rw-r--r--drivers/hwmon/ltc4260.c200
-rw-r--r--drivers/hwmon/ltc4261.c266
-rw-r--r--drivers/hwmon/max1111.c152
-rw-r--r--drivers/hwmon/max16065.c676
-rw-r--r--drivers/hwmon/max1619.c329
-rw-r--r--drivers/hwmon/max1668.c460
-rw-r--r--drivers/hwmon/max197.c347
-rw-r--r--drivers/hwmon/max6639.c613
-rw-r--r--drivers/hwmon/max6642.c327
-rw-r--r--drivers/hwmon/max6650.c470
-rw-r--r--drivers/hwmon/max6697.c680
-rw-r--r--drivers/hwmon/mc13783-adc.c148
-rw-r--r--drivers/hwmon/mcp3021.c201
-rw-r--r--drivers/hwmon/nct6683.c1457
-rw-r--r--drivers/hwmon/nct6775.c4228
-rw-r--r--drivers/hwmon/ntc_thermistor.c552
-rw-r--r--drivers/hwmon/pc87360.c752
-rw-r--r--drivers/hwmon/pc87427.c934
-rw-r--r--drivers/hwmon/pcf8591.c195
-rw-r--r--drivers/hwmon/pmbus/Kconfig124
-rw-r--r--drivers/hwmon/pmbus/Makefile15
-rw-r--r--drivers/hwmon/pmbus/adm1275.c397
-rw-r--r--drivers/hwmon/pmbus/lm25066.c530
-rw-r--r--drivers/hwmon/pmbus/ltc2978.c507
-rw-r--r--drivers/hwmon/pmbus/max16064.c127
-rw-r--r--drivers/hwmon/pmbus/max34440.c435
-rw-r--r--drivers/hwmon/pmbus/max8688.c204
-rw-r--r--drivers/hwmon/pmbus/pmbus.c217
-rw-r--r--drivers/hwmon/pmbus/pmbus.h387
-rw-r--r--drivers/hwmon/pmbus/pmbus_core.c1803
-rw-r--r--drivers/hwmon/pmbus/ucd9000.c246
-rw-r--r--drivers/hwmon/pmbus/ucd9200.c180
-rw-r--r--drivers/hwmon/pmbus/zl6100.c419
-rw-r--r--drivers/hwmon/s3c-hwmon.c70
-rw-r--r--drivers/hwmon/sch5627.c605
-rw-r--r--drivers/hwmon/sch5636.c535
-rw-r--r--drivers/hwmon/sch56xx-common.c619
-rw-r--r--drivers/hwmon/sch56xx-common.h32
-rw-r--r--drivers/hwmon/sht15.c1007
-rw-r--r--drivers/hwmon/sht21.c264
-rw-r--r--drivers/hwmon/shtc1.c251
-rw-r--r--drivers/hwmon/sis5595.c368
-rw-r--r--drivers/hwmon/smm665.c721
-rw-r--r--drivers/hwmon/smsc47b397.c124
-rw-r--r--drivers/hwmon/smsc47m1.c386
-rw-r--r--drivers/hwmon/smsc47m192.c167
-rw-r--r--drivers/hwmon/thmc50.c115
-rw-r--r--drivers/hwmon/tmp102.c307
-rw-r--r--drivers/hwmon/tmp401.c833
-rw-r--r--drivers/hwmon/tmp421.c108
-rw-r--r--drivers/hwmon/twl4030-madc-hwmon.c145
-rw-r--r--drivers/hwmon/ultra45_env.c76
-rw-r--r--drivers/hwmon/vexpress.c260
-rw-r--r--drivers/hwmon/via-cputemp.c150
-rw-r--r--drivers/hwmon/via686a.c470
-rw-r--r--drivers/hwmon/vt1211.c468
-rw-r--r--drivers/hwmon/vt8231.c353
-rw-r--r--drivers/hwmon/w83627ehf.c2100
-rw-r--r--drivers/hwmon/w83627hf.c457
-rw-r--r--drivers/hwmon/w83781d.c645
-rw-r--r--drivers/hwmon/w83791d.c384
-rw-r--r--drivers/hwmon/w83792d.c483
-rw-r--r--drivers/hwmon/w83793.c918
-rw-r--r--drivers/hwmon/w83795.c2287
-rw-r--r--drivers/hwmon/w83l785ts.c74
-rw-r--r--drivers/hwmon/w83l786ng.c226
-rw-r--r--drivers/hwmon/wm831x-hwmon.c36
-rw-r--r--drivers/hwmon/wm8350-hwmon.c20
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, &reg))) {
+ 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, &degrees) < 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, &reg_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, &reg);
- 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);
- 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, &reg);
- 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, &reg);
- 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, &ltc2945_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, &ltc4215_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, &ltc4215_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, &ltc4215_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(&ltc4215_driver);
-}
-
-static void __exit ltc4215_exit(void)
-{
- i2c_del_driver(&ltc4215_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, &ltc4222_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] = &ltc4245_std_group;
+
+ /* if we're using the extra gpio support, register it's attributes */
+ if (data->use_extra_gpios)
+ data->groups[1] = &ltc4245_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, &ltc4245_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, &ltc4245_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, &ltc4245_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(&ltc4245_driver);
-}
-
-static void __exit ltc4245_exit(void)
-{
- i2c_del_driver(&ltc4245_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, &ltc4260_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");