]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge tag 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 29 Apr 2013 16:45:48 +0000 (09:45 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 29 Apr 2013 16:45:48 +0000 (09:45 -0700)
Pull hwmon update from Guenter Roeck:

 - New drivers for NCT6775, NCT6776, NCT6779, and LM95234.

 - Added support for LTC2974, LTC3883, LM25056, TMP431, TMP432, ADT7310,
   and ADT7320 to existing drivers.

 - Various code cleanups and minor improvements.

* tag 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (54 commits)
  hwmon: (nct6775) Fix coding style problems
  hwmon: (nct6775) Constify strings
  hwmon: (tmp401) Add support for TMP432
  hwmon: (tmp401) Add support for update_interval attribute
  hwmon: (tmp401) Reset valid flag when resetting temperature history
  hwmon: (tmp401) Simplification and cleanup
  hwmon: (tmp401) Use sysfs_create_group / sysfs_remove_group
  hwmon: (tmp401) Drop unused defines, use BIT for bit masks
  hwmon: (nct6775) Use ARRAY_SIZE for loops where possible
  documentation: hwmon: Fix typo in documentation/hwmon
  hwmon: (nct6775) Enable both AUXTIN and VIN3 on NCT6776
  hwmon: (ad7314) use spi_get_drvdata() and spi_set_drvdata()
  MAINTAINERS: Add myself as maintainer for the NCT6775 driver
  hwmon: (nct6775) Expand scope of supported chips
  hwmon: (gpio-fan) Use is_visible to determine if attributes should be created
  hwmon: (tmp401) Fix device detection for TMP411B and TMP411C
  hwmon: Add driver for LM95234
  hwmon: (tmp401) Add support for TMP431
  hwmon: (pmbus/lm25066) Add support for LM25056
  hwmon: (pmbus/lm25066) Refactor device specific coefficients
  ...

77 files changed:
Documentation/devicetree/bindings/hwmon/ntc_thermistor.txt [new file with mode: 0644]
Documentation/hwmon/adt7410
Documentation/hwmon/lm25066
Documentation/hwmon/lm95234 [new file with mode: 0644]
Documentation/hwmon/ltc2978
Documentation/hwmon/nct6775 [new file with mode: 0644]
Documentation/hwmon/sht15
Documentation/hwmon/tmp401
Documentation/hwmon/zl6100
MAINTAINERS
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/abituguru.c
drivers/hwmon/abituguru3.c
drivers/hwmon/ad7314.c
drivers/hwmon/adm1021.c
drivers/hwmon/adm1026.c
drivers/hwmon/adm1029.c
drivers/hwmon/adm9240.c
drivers/hwmon/ads7871.c
drivers/hwmon/adt7310.c [new file with mode: 0644]
drivers/hwmon/adt7410.c
drivers/hwmon/adt7411.c
drivers/hwmon/adt7x10.c [new file with mode: 0644]
drivers/hwmon/adt7x10.h [new file with mode: 0644]
drivers/hwmon/applesmc.c
drivers/hwmon/asb100.c
drivers/hwmon/asc7621.c
drivers/hwmon/coretemp.c
drivers/hwmon/da9052-hwmon.c
drivers/hwmon/da9055-hwmon.c
drivers/hwmon/dme1737.c
drivers/hwmon/f71805f.c
drivers/hwmon/fam15h_power.c
drivers/hwmon/fschmd.c
drivers/hwmon/gl518sm.c
drivers/hwmon/gpio-fan.c
drivers/hwmon/ibmaem.c
drivers/hwmon/ibmpex.c
drivers/hwmon/ina2xx.c
drivers/hwmon/it87.c
drivers/hwmon/k8temp.c
drivers/hwmon/lm78.c
drivers/hwmon/lm80.c
drivers/hwmon/lm85.c
drivers/hwmon/lm93.c
drivers/hwmon/lm95234.c [new file with mode: 0644]
drivers/hwmon/ltc4151.c
drivers/hwmon/ltc4215.c
drivers/hwmon/ltc4245.c
drivers/hwmon/ltc4261.c
drivers/hwmon/max6697.c
drivers/hwmon/mc13783-adc.c
drivers/hwmon/nct6775.c [new file with mode: 0644]
drivers/hwmon/ntc_thermistor.c
drivers/hwmon/pc87360.c
drivers/hwmon/pc87427.c
drivers/hwmon/pmbus/Kconfig
drivers/hwmon/pmbus/lm25066.c
drivers/hwmon/pmbus/ltc2978.c
drivers/hwmon/s3c-hwmon.c
drivers/hwmon/sch56xx-common.c
drivers/hwmon/sis5595.c
drivers/hwmon/thmc50.c
drivers/hwmon/tmp102.c
drivers/hwmon/tmp401.c
drivers/hwmon/tmp421.c
drivers/hwmon/via686a.c
drivers/hwmon/vt1211.c
drivers/hwmon/vt8231.c
drivers/hwmon/w83627ehf.c
drivers/hwmon/w83781d.c
drivers/hwmon/w83791d.c
drivers/hwmon/w83792d.c
drivers/hwmon/w83793.c
drivers/hwmon/w83795.c
include/linux/platform_data/ntc_thermistor.h

diff --git a/Documentation/devicetree/bindings/hwmon/ntc_thermistor.txt b/Documentation/devicetree/bindings/hwmon/ntc_thermistor.txt
new file mode 100644 (file)
index 0000000..c6f6667
--- /dev/null
@@ -0,0 +1,29 @@
+NTC Thermistor hwmon sensors
+-------------------------------
+
+Requires node properties:
+- "compatible" value : one of
+       "ntc,ncp15wb473"
+       "ntc,ncp18wb473"
+       "ntc,ncp21wb473"
+       "ntc,ncp03wb473"
+       "ntc,ncp15wl333"
+- "pullup-uv"  Pull up voltage in micro volts
+- "pullup-ohm" Pull up resistor value in ohms
+- "pulldown-ohm" Pull down resistor value in ohms
+- "connected-positive" Always ON, If not specified.
+               Status change is possible.
+- "io-channels"        Channel node of ADC to be used for
+               conversion.
+
+Read more about iio bindings at
+       Documentation/devicetree/bindings/iio/iio-bindings.txt
+
+Example:
+       ncp15wb473@0 {
+               compatible = "ntc,ncp15wb473";
+               pullup-uv = <1800000>;
+               pullup-ohm = <47000>;
+               pulldown-ohm = <0>;
+               io-channels = <&adc 3>;
+       };
index 58150c480e5694668c32f6348dab6ae1bcd168bf..9817941e5f1932a014ae1e597b6749dca61fe552 100644 (file)
@@ -12,29 +12,42 @@ Supported chips:
     Addresses scanned: None
     Datasheet: Publicly available at the Analog Devices website
                http://www.analog.com/static/imported-files/data_sheets/ADT7420.pdf
+  * Analog Devices ADT7310
+    Prefix: 'adt7310'
+    Addresses scanned: None
+    Datasheet: Publicly available at the Analog Devices website
+               http://www.analog.com/static/imported-files/data_sheets/ADT7310.pdf
+  * Analog Devices ADT7320
+    Prefix: 'adt7320'
+    Addresses scanned: None
+    Datasheet: Publicly available at the Analog Devices website
+               http://www.analog.com/static/imported-files/data_sheets/ADT7320.pdf
 
 Author: Hartmut Knaack <knaack.h@gmx.de>
 
 Description
 -----------
 
-The ADT7410 is a temperature sensor with rated temperature range of -55°C to
-+150°C. It has a high accuracy of +/-0.5°C and can be operated at a resolution
-of 13 bits (0.0625°C) or 16 bits (0.0078°C). The sensor provides an INT pin to
-indicate that a minimum or maximum temperature set point has been exceeded, as
-well as a critical temperature (CT) pin to indicate that the critical
-temperature set point has been exceeded. Both pins can be set up with a common
-hysteresis of 0°C - 15°C and a fault queue, ranging from 1 to 4 events. Both
-pins can individually set to be active-low or active-high, while the whole
-device can either run in comparator mode or interrupt mode. The ADT7410
-supports continous temperature sampling, as well as sampling one temperature
-value per second or even justget one sample on demand for power saving.
-Besides, it can completely power down its ADC, if power management is
-required.
-
-The ADT7420 is register compatible, the only differences being the package,
-a slightly narrower operating temperature range (-40°C to +150°C), and a
-better accuracy (0.25°C instead of 0.50°C.)
+The ADT7310/ADT7410 is a temperature sensor with rated temperature range of
+-55°C to +150°C. It has a high accuracy of +/-0.5°C and can be operated at a
+resolution of 13 bits (0.0625°C) or 16 bits (0.0078°C). The sensor provides an
+INT pin to indicate that a minimum or maximum temperature set point has been
+exceeded, as well as a critical temperature (CT) pin to indicate that the
+critical temperature set point has been exceeded. Both pins can be set up with a
+common hysteresis of 0°C - 15°C and a fault queue, ranging from 1 to 4 events.
+Both pins can individually set to be active-low or active-high, while the whole
+device can either run in comparator mode or interrupt mode. The ADT7410 supports
+continuous temperature sampling, as well as sampling one temperature value per
+second or even just get one sample on demand for power saving. Besides, it can
+completely power down its ADC, if power management is required.
+
+The ADT7320/ADT7420 is register compatible, the only differences being the
+package, a slightly narrower operating temperature range (-40°C to +150°C), and
+a better accuracy (0.25°C instead of 0.50°C.)
+
+The difference between the ADT7310/ADT7320 and ADT7410/ADT7420 is the control
+interface, the ADT7310 and ADT7320 use SPI while the ADT7410 and ADT7420 use
+I2C.
 
 Configuration Notes
 -------------------
index 26025e419d357c5e078e96a3f831be67f5b4e289..c1b57d72efc33c69c05b1856c7631930e5ac28bd 100644 (file)
@@ -1,7 +1,13 @@
-Kernel driver max8688
+Kernel driver lm25066
 =====================
 
 Supported chips:
+  * TI LM25056
+    Prefix: 'lm25056'
+    Addresses scanned: -
+    Datasheets:
+       http://www.ti.com/lit/gpn/lm25056
+       http://www.ti.com/lit/gpn/lm25056a
   * National Semiconductor LM25066
     Prefix: 'lm25066'
     Addresses scanned: -
@@ -25,8 +31,9 @@ Author: Guenter Roeck <linux@roeck-us.net>
 Description
 -----------
 
-This driver supports hardware montoring for National Semiconductor LM25066,
-LM5064, and LM5064 Power Management, Monitoring, Control, and Protection ICs.
+This driver supports hardware montoring for National Semiconductor / TI LM25056,
+LM25066, LM5064, and LM5064 Power Management, Monitoring, Control, and
+Protection ICs.
 
 The driver is a client driver to the core PMBus driver. Please see
 Documentation/hwmon/pmbus for details on PMBus client drivers.
@@ -60,14 +67,19 @@ in1_max                     Maximum input voltage.
 in1_min_alarm          Input voltage low alarm.
 in1_max_alarm          Input voltage high alarm.
 
-in2_label              "vout1"
-in2_input              Measured output voltage.
-in2_average            Average measured output voltage.
-in2_min                        Minimum output voltage.
-in2_min_alarm          Output voltage low alarm.
-
-in3_label              "vout2"
-in3_input              Measured voltage on vaux pin
+in2_label              "vmon"
+in2_input              Measured voltage on VAUX pin
+in2_min                        Minimum VAUX voltage (LM25056 only).
+in2_max                        Maximum VAUX voltage (LM25056 only).
+in2_min_alarm          VAUX voltage low alarm (LM25056 only).
+in2_max_alarm          VAUX voltage high alarm (LM25056 only).
+
+in3_label              "vout1"
+                       Not supported on LM25056.
+in3_input              Measured output voltage.
+in3_average            Average measured output voltage.
+in3_min                        Minimum output voltage.
+in3_min_alarm          Output voltage low alarm.
 
 curr1_label            "iin"
 curr1_input            Measured input current.
diff --git a/Documentation/hwmon/lm95234 b/Documentation/hwmon/lm95234
new file mode 100644 (file)
index 0000000..a0e95dd
--- /dev/null
@@ -0,0 +1,36 @@
+Kernel driver lm95234
+=====================
+
+Supported chips:
+  * National Semiconductor / Texas Instruments LM95234
+    Addresses scanned: I2C 0x18, 0x4d, 0x4e
+    Datasheet: Publicly available at the Texas Instruments website
+               http://www.ti.com/product/lm95234
+
+
+Author: Guenter Roeck <linux@roeck-us.net>
+
+Description
+-----------
+
+LM95234 is an 11-bit digital temperature sensor with a 2-wire System Management
+Bus (SMBus) interface and TrueTherm technology that can very accurately monitor
+the temperature of four remote diodes as well as its own temperature.
+The four remote diodes can be external devices such as microprocessors,
+graphics processors or diode-connected 2N3904s. The LM95234's TruTherm
+beta compensation technology allows sensing of 90 nm or 65 nm process
+thermal diodes accurately.
+
+All temperature values are given in millidegrees Celsius. Temperature
+is provided within a range of -127 to +255 degrees (+127.875 degrees for
+the internal sensor). Resolution depends on temperature input and range.
+
+Each sensor has its own maximum limit, but the hysteresis is common to all
+channels. The hysteresis is configurable with the tem1_max_hyst attribute and
+affects the hysteresis on all channels. The first two external sensors also
+have a critical limit.
+
+The lm95234 driver can change its update interval to a fixed set of values.
+It will round up to the next selectable interval. See the datasheet for exact
+values. Reading sensor values more often will do no harm, but will return
+'old' values.
index e4d75c606c97e88f9a45c2dfd956931b568b28e7..dc0d08c61305469305d86f2f3cd04a32fa2e56c6 100644 (file)
@@ -2,6 +2,10 @@ Kernel driver ltc2978
 =====================
 
 Supported chips:
+  * Linear Technology LTC2974
+    Prefix: 'ltc2974'
+    Addresses scanned: -
+    Datasheet: http://www.linear.com/product/ltc2974
   * Linear Technology LTC2978
     Prefix: 'ltc2978'
     Addresses scanned: -
@@ -10,6 +14,10 @@ Supported chips:
     Prefix: 'ltc3880'
     Addresses scanned: -
     Datasheet: http://www.linear.com/product/ltc3880
+  * Linear Technology LTC3883
+    Prefix: 'ltc3883'
+    Addresses scanned: -
+    Datasheet: http://www.linear.com/product/ltc3883
 
 Author: Guenter Roeck <linux@roeck-us.net>
 
@@ -17,9 +25,9 @@ Author: Guenter Roeck <linux@roeck-us.net>
 Description
 -----------
 
-The LTC2978 is an octal power supply monitor, supervisor, sequencer and
-margin controller. The LTC3880 is a dual, PolyPhase DC/DC synchronous
-step-down switching regulator controller.
+LTC2974 is a quad digital power supply manager. LTC2978 is an octal power supply
+monitor. LTC3880 is a dual output poly-phase step-down DC/DC controller. LTC3883
+is a single phase step-down DC/DC controller.
 
 
 Usage Notes
@@ -41,63 +49,90 @@ Sysfs attributes
 in1_label              "vin"
 in1_input              Measured input voltage.
 in1_min                        Minimum input voltage.
-in1_max                        Maximum input voltage.
-in1_lcrit              Critical minimum input voltage.
+in1_max                        Maximum input voltage. LTC2974 and LTC2978 only.
+in1_lcrit              Critical minimum input voltage. LTC2974 and LTC2978
+                       only.
 in1_crit               Critical maximum input voltage.
 in1_min_alarm          Input voltage low alarm.
-in1_max_alarm          Input voltage high alarm.
-in1_lcrit_alarm                Input voltage critical low alarm.
+in1_max_alarm          Input voltage high alarm. LTC2974 and LTC2978 only.
+in1_lcrit_alarm                Input voltage critical low alarm. LTC2974 and LTC2978
+                       only.
 in1_crit_alarm         Input voltage critical high alarm.
-in1_lowest             Lowest input voltage. LTC2978 only.
+in1_lowest             Lowest input voltage. LTC2974 and LTC2978 only.
 in1_highest            Highest input voltage.
-in1_reset_history      Reset history. Writing into this attribute will reset
-                       history for all attributes.
-
-in[2-9]_label          "vout[1-8]". Channels 3 to 9 on LTC2978 only.
-in[2-9]_input          Measured output voltage.
-in[2-9]_min            Minimum output voltage.
-in[2-9]_max            Maximum output voltage.
-in[2-9]_lcrit          Critical minimum output voltage.
-in[2-9]_crit           Critical maximum output voltage.
-in[2-9]_min_alarm      Output voltage low alarm.
-in[2-9]_max_alarm      Output voltage high alarm.
-in[2-9]_lcrit_alarm    Output voltage critical low alarm.
-in[2-9]_crit_alarm     Output voltage critical high alarm.
-in[2-9]_lowest         Lowest output voltage. LTC2978 only.
-in[2-9]_highest                Lowest output voltage.
-in[2-9]_reset_history  Reset history. Writing into this attribute will reset
-                       history for all attributes.
-
-temp[1-3]_input                Measured temperature.
+in1_reset_history      Reset input voltage history.
+
+in[N]_label            "vout[1-8]".
+                       LTC2974: N=2-5
+                       LTC2978: N=2-9
+                       LTC3880: N=2-3
+                       LTC3883: N=2
+in[N]_input            Measured output voltage.
+in[N]_min              Minimum output voltage.
+in[N]_max              Maximum output voltage.
+in[N]_lcrit            Critical minimum output voltage.
+in[N]_crit             Critical maximum output voltage.
+in[N]_min_alarm                Output voltage low alarm.
+in[N]_max_alarm                Output voltage high alarm.
+in[N]_lcrit_alarm      Output voltage critical low alarm.
+in[N]_crit_alarm       Output voltage critical high alarm.
+in[N]_lowest           Lowest output voltage. LTC2974 and LTC2978 only.
+in[N]_highest          Highest output voltage.
+in[N]_reset_history    Reset output voltage history.
+
+temp[N]_input          Measured temperature.
+                       On LTC2974, temp[1-4] report external temperatures,
+                       and temp5 reports the chip temperature.
                        On LTC2978, only one temperature measurement is
-                       supported and reflects the internal temperature.
+                       supported and reports the chip temperature.
                        On LTC3880, temp1 and temp2 report external
-                       temperatures, and temp3 reports the internal
-                       temperature.
-temp[1-3]_min          Mimimum temperature.
-temp[1-3]_max          Maximum temperature.
-temp[1-3]_lcrit                Critical low temperature.
-temp[1-3]_crit         Critical high temperature.
-temp[1-3]_min_alarm    Chip temperature low alarm.
-temp[1-3]_max_alarm    Chip temperature high alarm.
-temp[1-3]_lcrit_alarm  Chip temperature critical low alarm.
-temp[1-3]_crit_alarm   Chip temperature critical high alarm.
-temp[1-3]_lowest       Lowest measured temperature. LTC2978 only.
-temp[1-3]_highest      Highest measured temperature.
-temp[1-3]_reset_history        Reset history. Writing into this attribute will reset
-                       history for all attributes.
-
-power[1-2]_label       "pout[1-2]". LTC3880 only.
-power[1-2]_input       Measured power.
-
-curr1_label            "iin". LTC3880 only.
+                       temperatures, and temp3 reports the chip temperature.
+                       On LTC3883, temp1 reports an external temperature,
+                       and temp2 reports the chip temperature.
+temp[N]_min            Mimimum temperature. LTC2974 and LTC2978 only.
+temp[N]_max            Maximum temperature.
+temp[N]_lcrit          Critical low temperature.
+temp[N]_crit           Critical high temperature.
+temp[N]_min_alarm      Temperature low alarm. LTC2974 and LTC2978 only.
+temp[N]_max_alarm      Temperature high alarm.
+temp[N]_lcrit_alarm    Temperature critical low alarm.
+temp[N]_crit_alarm     Temperature critical high alarm.
+temp[N]_lowest         Lowest measured temperature. LTC2974 and LTC2978 only.
+                       Not supported for chip temperature sensor on LTC2974.
+temp[N]_highest                Highest measured temperature. Not supported for chip
+                       temperature sensor on LTC2974.
+temp[N]_reset_history  Reset temperature history. Not supported for chip
+                       temperature sensor on LTC2974.
+
+power1_label           "pin". LTC3883 only.
+power1_input           Measured input power.
+
+power[N]_label         "pout[1-4]".
+                       LTC2974: N=1-4
+                       LTC2978: Not supported
+                       LTC3880: N=1-2
+                       LTC3883: N=2
+power[N]_input         Measured output power.
+
+curr1_label            "iin". LTC3880 and LTC3883 only.
 curr1_input            Measured input current.
 curr1_max              Maximum input current.
 curr1_max_alarm                Input current high alarm.
-
-curr[2-3]_label                "iout[1-2]". LTC3880 only.
-curr[2-3]_input                Measured input current.
-curr[2-3]_max          Maximum input current.
-curr[2-3]_crit         Critical input current.
-curr[2-3]_max_alarm    Input current high alarm.
-curr[2-3]_crit_alarm   Input current critical high alarm.
+curr1_highest          Highest input current. LTC3883 only.
+curr1_reset_history    Reset input current history. LTC3883 only.
+
+curr[N]_label          "iout[1-4]".
+                       LTC2974: N=1-4
+                       LTC2978: not supported
+                       LTC3880: N=2-3
+                       LTC3883: N=2
+curr[N]_input          Measured output current.
+curr[N]_max            Maximum output current.
+curr[N]_crit           Critical high output current.
+curr[N]_lcrit          Critical low output current. LTC2974 only.
+curr[N]_max_alarm      Output current high alarm.
+curr[N]_crit_alarm     Output current critical high alarm.
+curr[N]_lcrit_alarm    Output current critical low alarm. LTC2974 only.
+curr[N]_lowest         Lowest output current. LTC2974 only.
+curr[N]_highest                Highest output current.
+curr[N]_reset_history  Reset output current history.
diff --git a/Documentation/hwmon/nct6775 b/Documentation/hwmon/nct6775
new file mode 100644 (file)
index 0000000..4e9ef60
--- /dev/null
@@ -0,0 +1,188 @@
+Note
+====
+
+This driver supersedes the NCT6775F and NCT6776F support in the W83627EHF
+driver.
+
+Kernel driver NCT6775
+=====================
+
+Supported chips:
+  * Nuvoton NCT5572D/NCT6771F/NCT6772F/NCT6775F/W83677HG-I
+    Prefix: 'nct6775'
+    Addresses scanned: ISA address retrieved from Super I/O registers
+    Datasheet: Available from Nuvoton upon request
+  * Nuvoton NCT5577D/NCT6776D/NCT6776F
+    Prefix: 'nct6776'
+    Addresses scanned: ISA address retrieved from Super I/O registers
+    Datasheet: Available from Nuvoton upon request
+  * Nuvoton NCT5532D/NCT6779D
+    Prefix: 'nct6779'
+    Addresses scanned: ISA address retrieved from Super I/O registers
+    Datasheet: Available from Nuvoton upon request
+
+Authors:
+        Guenter Roeck <linux@roeck-us.net>
+
+Description
+-----------
+
+This driver implements support for the Nuvoton NCT6775F, NCT6776F, and NCT6779D
+and compatible super I/O chips.
+
+The chips support up to 25 temperature monitoring sources. Up to 6 of those are
+direct temperature sensor inputs, the others are special sources such as PECI,
+PCH, and SMBUS. Depending on the chip type, 2 to 6 of the temperature sources
+can be monitored and compared against minimum, maximum, and critical
+temperatures. The driver reports up to 10 of the temperatures to the user.
+There are 4 to 5 fan rotation speed sensors, 8 to 15 analog voltage sensors,
+one VID, alarms with beep warnings (control unimplemented), and some automatic
+fan regulation strategies (plus manual fan control mode).
+
+The temperature sensor sources on all chips are configurable. The configured
+source for each of the temperature sensors is provided in tempX_label.
+
+Temperatures are measured in degrees Celsius and measurement resolution is
+either 1 degC or 0.5 degC, depending on the temperature source and
+configuration. An alarm is triggered when the temperature gets higher than
+the high limit; it stays on until the temperature falls below the hysteresis
+value. Alarms are only supported for temp1 to temp6, depending on the chip type.
+
+Fan rotation speeds are reported in RPM (rotations per minute). An alarm is
+triggered if the rotation speed has dropped below a programmable limit. On
+NCT6775F, fan readings can be divided by a programmable divider (1, 2, 4, 8,
+16, 32, 64 or 128) to give the readings more range or accuracy; the other chips
+do not have a fan speed divider. The driver sets the most suitable fan divisor
+itself; specifically, it increases the divider value each time a fan speed
+reading returns an invalid value, and it reduces it if the fan speed reading
+is lower than optimal. Some fans might not be present because they share pins
+with other functions.
+
+Voltage sensors (also known as IN sensors) report their values in millivolts.
+An alarm is triggered if the voltage has crossed a programmable minimum
+or maximum limit.
+
+The driver supports automatic fan control mode known as Thermal Cruise.
+In this mode, the chip attempts to keep the measured temperature in a
+predefined temperature range. If the temperature goes out of range, fan
+is driven slower/faster to reach the predefined range again.
+
+The mode works for fan1-fan5.
+
+sysfs attributes
+----------------
+
+pwm[1-5] - this file stores PWM duty cycle or DC value (fan speed) in range:
+          0 (lowest speed) to 255 (full)
+
+pwm[1-5]_enable - this file controls mode of fan/temperature control:
+       * 0 Fan control disabled (fans set to maximum speed)
+       * 1 Manual mode, write to pwm[0-5] any value 0-255
+       * 2 "Thermal Cruise" mode
+       * 3 "Fan Speed Cruise" mode
+       * 4 "Smart Fan III" mode (NCT6775F only)
+       * 5 "Smart Fan IV" mode
+
+pwm[1-5]_mode - controls if output is PWM or DC level
+        * 0 DC output
+        * 1 PWM output
+
+Common fan control attributes
+-----------------------------
+
+pwm[1-5]_temp_sel      Temperature source. Value is temperature sensor index.
+                       For example, select '1' for temp1_input.
+pwm[1-5]_weight_temp_sel
+                       Secondary temperature source. Value is temperature
+                       sensor index. For example, select '1' for temp1_input.
+                       Set to 0 to disable secondary temperature control.
+
+If secondary temperature functionality is enabled, it is controlled with the
+following attributes.
+
+pwm[1-5]_weight_duty_step
+                       Duty step size.
+pwm[1-5]_weight_temp_step
+                       Temperature step size. With each step over
+                       temp_step_base, the value of weight_duty_step is added
+                       to the current pwm value.
+pwm[1-5]_weight_temp_step_base
+                       Temperature at which secondary temperature control kicks
+                       in.
+pwm[1-5]_weight_temp_step_tol
+                       Temperature step tolerance.
+
+Thermal Cruise mode (2)
+-----------------------
+
+If the temperature is in the range defined by:
+
+pwm[1-5]_target_temp   Target temperature, unit millidegree Celsius
+                       (range 0 - 127000)
+pwm[1-5]_temp_tolerance
+                       Target temperature tolerance, unit millidegree Celsius
+
+there are no changes to fan speed. Once the temperature leaves the interval, fan
+speed increases (if temperature is higher that desired) or decreases (if
+temperature is lower than desired), using the following limits and time
+intervals.
+
+pwm[1-5]_start         fan pwm start value (range 1 - 255), to start fan
+                       when the temperature is above defined range.
+pwm[1-5]_floor         lowest fan pwm (range 0 - 255) if temperature is below
+                       the defined range. If set to 0, the fan is expected to
+                       stop if the temperature is below the defined range.
+pwm[1-5]_step_up_time  milliseconds before fan speed is increased
+pwm[1-5]_step_down_time        milliseconds before fan speed is decreased
+pwm[1-5]_stop_time     how many milliseconds must elapse to switch
+                       corresponding fan off (when the temperature was below
+                       defined range).
+
+Speed Cruise mode (3)
+---------------------
+
+This modes tries to keep the fan speed constant.
+
+fan[1-5]_target                Target fan speed
+fan[1-5]_tolerance
+                       Target speed tolerance
+
+
+Untested; use at your own risk.
+
+Smart Fan IV mode (5)
+---------------------
+
+This mode offers multiple slopes to control the fan speed. The slopes can be
+controlled by setting the pwm and temperature attributes. When the temperature
+rises, the chip will calculate the DC/PWM output based on the current slope.
+There are up to seven data points depending on the chip type. Subsequent data
+points should be set to higher temperatures and higher pwm values to achieve
+higher fan speeds with increasing temperature. The last data point reflects
+critical temperature mode, in which the fans should run at full speed.
+
+pwm[1-5]_auto_point[1-7]_pwm
+                       pwm value to be set if temperature reaches matching
+                       temperature range.
+pwm[1-5]_auto_point[1-7]_temp
+                       Temperature over which the matching pwm is enabled.
+pwm[1-5]_temp_tolerance
+                       Temperature tolerance, unit millidegree Celsius
+pwm[1-5]_crit_temp_tolerance
+                       Temperature tolerance for critical temperature,
+                       unit millidegree Celsius
+
+pwm[1-5]_step_up_time  milliseconds before fan speed is increased
+pwm[1-5]_step_down_time        milliseconds before fan speed is decreased
+
+Usage Notes
+-----------
+
+On various ASUS boards with NCT6776F, it appears that CPUTIN is not really
+connected to anything and floats, or that it is connected to some non-standard
+temperature measurement device. As a result, the temperature reported on CPUTIN
+will not reflect a usable value. It often reports unreasonably high
+temperatures, and in some cases the reported temperature declines if the actual
+temperature increases (similar to the raw PECI temperature value - see PECI
+specification for details). CPUTIN should therefore be be ignored on ASUS
+boards. The CPU temperature on ASUS boards is reported from PECI 0.
index 02850bdfac182c0308eaacbffc13b24c6a36e276..778987d1856fd2c117cb779c3ef988cb2127218d 100644 (file)
@@ -40,7 +40,7 @@ bits for humidity, or 12 bits for temperature and 8 bits for humidity.
 The humidity calibration coefficients are programmed into an OTP memory on the
 chip. These coefficients are used to internally calibrate the signals from the
 sensors. Disabling the reload of those coefficients allows saving 10ms for each
-measurement and decrease power consumption, while loosing on precision.
+measurement and decrease power consumption, while losing on precision.
 
 Some options may be set directly in the sht15_platform_data structure
 or via sysfs attributes.
index 9fc447249212c9c9abcf419f2add839e4199df59..f91e3fa7e5ec5fe0df6515203d62a2f6d844ff9f 100644 (file)
@@ -8,8 +8,16 @@ Supported chips:
     Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp401.html
   * Texas Instruments TMP411
     Prefix: 'tmp411'
-    Addresses scanned: I2C 0x4c
+    Addresses scanned: I2C 0x4c, 0x4d, 0x4e
     Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp411.html
+  * Texas Instruments TMP431
+    Prefix: 'tmp431'
+    Addresses scanned: I2C 0x4c, 0x4d
+    Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp431.html
+  * Texas Instruments TMP432
+    Prefix: 'tmp432'
+    Addresses scanned: I2C 0x4c, 0x4d
+    Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp432.html
 
 Authors:
          Hans de Goede <hdegoede@redhat.com>
@@ -18,19 +26,19 @@ Authors:
 Description
 -----------
 
-This driver implements support for Texas Instruments TMP401 and
-TMP411 chips. These chips implements one remote and one local
-temperature sensor. Temperature is measured in degrees
+This driver implements support for Texas Instruments TMP401, TMP411,
+TMP431, and TMP432 chips. These chips implement one or two remote and
+one local temperature sensors. Temperature is measured in degrees
 Celsius. Resolution of the remote sensor is 0.0625 degree. Local
 sensor resolution can be set to 0.5, 0.25, 0.125 or 0.0625 degree (not
 supported by the driver so far, so using the default resolution of 0.5
 degree).
 
 The driver provides the common sysfs-interface for temperatures (see
-/Documentation/hwmon/sysfs-interface under Temperatures).
+Documentation/hwmon/sysfs-interface under Temperatures).
 
-The TMP411 chip is compatible with TMP401. It provides some additional
-features.
+The TMP411 and TMP431 chips are compatible with TMP401. TMP411 provides
+some additional features.
 
 * Minimum and Maximum temperature measured since power-on, chip-reset
 
@@ -40,3 +48,6 @@ features.
 
   Exported via sysfs attribute temp_reset_history. Writing 1 to this
   file triggers a reset.
+
+TMP432 is compatible with TMP401 and TMP431. It supports two external
+temperature sensors.
index 756b57c6b73e8683308641aaad38887d7c4797d0..33908a4d68ff9e0b47e26560f15ff5f4e550161b 100644 (file)
@@ -125,7 +125,7 @@ in2_label           "vmon"
 in2_input              Measured voltage on VMON (ZL2004) or VDRV (ZL9101M,
                        ZL9117M) pin. Reported voltage is 16x the voltage on the
                        pin (adjusted internally by the chip).
-in2_lcrit              Critical minumum VMON/VDRV Voltage.
+in2_lcrit              Critical minimum VMON/VDRV Voltage.
 in2_crit               Critical maximum VMON/VDRV voltage.
 in2_lcrit_alarm                VMON/VDRV voltage critical low alarm.
 in2_crit_alarm         VMON/VDRV voltage critical high alarm.
index 8bdd7a7ef2f4687aa26ee66a70fe8ad428e14890..fb2fc30269686525588cdfadd8402051790be6f3 100644 (file)
@@ -5412,6 +5412,13 @@ L:       linux-scsi@vger.kernel.org
 S:     Maintained
 F:     drivers/scsi/NCR_D700.*
 
+NCT6775 HARDWARE MONITOR DRIVER
+M:     Guenter Roeck <linux@roeck-us.net>
+L:     lm-sensors@lm-sensors.org
+S:     Maintained
+F:     Documentation/hwmon/nct6775
+F:     drivers/hwmon/nct6775.c
+
 NETEFFECT IWARP RNIC DRIVER (IW_NES)
 M:     Faisal Latif <faisal.latif@intel.com>
 L:     linux-rdma@vger.kernel.org
index 89ac1cb26f24e6649b40ec253436bc4ee96437a5..4f29117651b6d16d27df177f473d0fe33cec9175 100644 (file)
@@ -179,9 +179,29 @@ config SENSORS_ADM9240
          This driver can also be built as a module.  If so, the module
          will be called adm9240.
 
+config SENSORS_ADT7X10
+       tristate
+       help
+         This module contains common code shared by the ADT7310/ADT7320 and
+         ADT7410/ADT7420 temperature monitoring chip drivers.
+
+         If build as a module, the module will be called adt7x10.
+
+config SENSORS_ADT7310
+       tristate "Analog Devices ADT7310/ADT7320"
+       depends on SPI_MASTER
+       select SENSORS_ADT7X10
+       help
+         If you say yes here you get support for the Analog Devices
+         ADT7310 and ADT7320 temperature monitoring chips.
+
+         This driver can also be built as a module. If so, the module
+         will be called adt7310.
+
 config SENSORS_ADT7410
        tristate "Analog Devices ADT7410/ADT7420"
        depends on I2C
+       select SENSORS_ADT7X10
        help
          If you say yes here you get support for the Analog Devices
          ADT7410 and ADT7420 temperature monitoring chips.
@@ -751,6 +771,16 @@ config SENSORS_LTC4261
          This driver can also be built as a module. If so, the module will
          be called ltc4261.
 
+config SENSORS_LM95234
+       tristate "National Semiconductor LM95234"
+       depends on I2C
+       help
+         If you say yes here you get support for the LM95234 temperature
+         sensor.
+
+         This driver can also be built as a module.  If so, the module
+         will be called lm95234.
+
 config SENSORS_LM95241
        tristate "National Semiconductor LM95241 and compatibles"
        depends on I2C
@@ -877,8 +907,22 @@ config SENSORS_MCP3021
          This driver can also be built as a module.  If so, the module
          will be called mcp3021.
 
+config SENSORS_NCT6775
+       tristate "Nuvoton NCT6775F and compatibles"
+       depends on !PPC
+       select HWMON_VID
+       help
+         If you say yes here you get support for the hardware monitoring
+         functionality of the Nuvoton NCT6775F, NCT6776F, NCT6779D
+         and compatible Super-I/O chips. This driver replaces the
+         w83627ehf driver for NCT6775F and NCT6776F.
+
+         This driver can also be built as a module.  If so, the module
+         will be called nct6775.
+
 config SENSORS_NTC_THERMISTOR
        tristate "NTC thermistor support"
+       depends on (!OF && !IIO) || (OF && IIO)
        help
          This driver supports NTC thermistors sensor reading and its
          interpretation. The driver can also monitor the temperature and
@@ -1204,8 +1248,8 @@ config SENSORS_TMP401
        tristate "Texas Instruments TMP401 and compatibles"
        depends on I2C
        help
-         If you say yes here you get support for Texas Instruments TMP401 and
-         TMP411 temperature sensor chips.
+         If you say yes here you get support for Texas Instruments TMP401,
+         TMP411, TMP431, and TMP432 temperature sensor chips.
 
          This driver can also be built as a module.  If so, the module
          will be called tmp401.
index 8d6d97ea7c1e3d9e161f425cb236c79fce18c295..5c71fe6a9c795cb75170d6fb94ab1f4361c3a2d6 100644 (file)
@@ -34,6 +34,8 @@ obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o
 obj-$(CONFIG_SENSORS_ADS1015)  += ads1015.o
 obj-$(CONFIG_SENSORS_ADS7828)  += ads7828.o
 obj-$(CONFIG_SENSORS_ADS7871)  += ads7871.o
+obj-$(CONFIG_SENSORS_ADT7X10)  += adt7x10.o
+obj-$(CONFIG_SENSORS_ADT7310)  += adt7310.o
 obj-$(CONFIG_SENSORS_ADT7410)  += adt7410.o
 obj-$(CONFIG_SENSORS_ADT7411)  += adt7411.o
 obj-$(CONFIG_SENSORS_ADT7462)  += adt7462.o
@@ -86,6 +88,7 @@ obj-$(CONFIG_SENSORS_LM87)    += lm87.o
 obj-$(CONFIG_SENSORS_LM90)     += lm90.o
 obj-$(CONFIG_SENSORS_LM92)     += lm92.o
 obj-$(CONFIG_SENSORS_LM93)     += lm93.o
+obj-$(CONFIG_SENSORS_LM95234)  += lm95234.o
 obj-$(CONFIG_SENSORS_LM95241)  += lm95241.o
 obj-$(CONFIG_SENSORS_LM95245)  += lm95245.o
 obj-$(CONFIG_SENSORS_LTC4151)  += ltc4151.o
@@ -103,6 +106,7 @@ obj-$(CONFIG_SENSORS_MAX6650)       += max6650.o
 obj-$(CONFIG_SENSORS_MAX6697)  += max6697.o
 obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
 obj-$(CONFIG_SENSORS_MCP3021)  += mcp3021.o
+obj-$(CONFIG_SENSORS_NCT6775)  += nct6775.o
 obj-$(CONFIG_SENSORS_NTC_THERMISTOR)   += ntc_thermistor.o
 obj-$(CONFIG_SENSORS_PC87360)  += pc87360.o
 obj-$(CONFIG_SENSORS_PC87427)  += pc87427.o
index 6119ff8e8c87597da85afa4eb4edda34c77b4b76..df0b69987914d0c2241d746956db3d9d4e02a351 100644 (file)
 #define ABIT_UGURU_MAX_TIMEOUTS                        2
 /* utility macros */
 #define ABIT_UGURU_NAME                                "abituguru"
-#define ABIT_UGURU_DEBUG(level, format, arg...)                                \
-       if (level <= verbose)                                           \
-               printk(KERN_DEBUG ABIT_UGURU_NAME ": "  format , ## arg)
+#define ABIT_UGURU_DEBUG(level, format, arg...)                \
+       do {                                            \
+               if (level <= verbose)                   \
+                       pr_debug(format , ## arg);      \
+       } while (0)
+
 /* Macros to help calculate the sysfs_names array length */
 /*
  * sum of strlen of: in??_input\0, in??_{min,max}\0, in??_{min,max}_alarm\0,
@@ -1533,7 +1536,7 @@ static int abituguru_resume(struct device *dev)
 }
 
 static SIMPLE_DEV_PM_OPS(abituguru_pm, abituguru_suspend, abituguru_resume);
-#define ABIT_UGURU_PM  &abituguru_pm
+#define ABIT_UGURU_PM  (&abituguru_pm)
 #else
 #define ABIT_UGURU_PM  NULL
 #endif /* CONFIG_PM */
index 205327d33c4d5f81a3cef918624b42d1f34f0c92..1d2da31c27c64194169a41ad44e5a18252b67c77 100644 (file)
 #define ABIT_UGURU3_SYNCHRONIZE_TIMEOUT                5
 /* utility macros */
 #define ABIT_UGURU3_NAME                       "abituguru3"
-#define ABIT_UGURU3_DEBUG(format, arg...)      \
-       if (verbose)                            \
-               printk(KERN_DEBUG ABIT_UGURU3_NAME ": " format , ## arg)
+#define ABIT_UGURU3_DEBUG(format, arg...)              \
+       do {                                            \
+               if (verbose)                            \
+                       pr_debug(format , ## arg);      \
+       } while (0)
 
 /* Macros to help calculate the sysfs_names array length */
 #define ABIT_UGURU3_MAX_NO_SENSORS 26
@@ -1159,7 +1161,7 @@ static int abituguru3_resume(struct device *dev)
 }
 
 static SIMPLE_DEV_PM_OPS(abituguru3_pm, abituguru3_suspend, abituguru3_resume);
-#define ABIT_UGURU3_PM &abituguru3_pm
+#define ABIT_UGURU3_PM (&abituguru3_pm)
 #else
 #define ABIT_UGURU3_PM NULL
 #endif /* CONFIG_PM */
index a57584d28a404036589a50dec3aac202072dd45d..f4f9b219bf1619392e203dfb95f897c742e17357 100644 (file)
@@ -116,7 +116,7 @@ static int ad7314_probe(struct spi_device *spi_dev)
        if (chip == NULL)
                return -ENOMEM;
 
-       dev_set_drvdata(&spi_dev->dev, chip);
+       spi_set_drvdata(spi_dev, chip);
 
        ret = sysfs_create_group(&spi_dev->dev.kobj, &ad7314_group);
        if (ret < 0)
@@ -137,7 +137,7 @@ error_remove_group:
 
 static int ad7314_remove(struct spi_device *spi_dev)
 {
-       struct ad7314_data *chip = dev_get_drvdata(&spi_dev->dev);
+       struct ad7314_data *chip = spi_get_drvdata(spi_dev);
 
        hwmon_device_unregister(chip->hwmon_dev);
        sysfs_remove_group(&spi_dev->dev.kobj, &ad7314_group);
@@ -166,6 +166,5 @@ static struct spi_driver ad7314_driver = {
 module_spi_driver(ad7314_driver);
 
 MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
-MODULE_DESCRIPTION("Analog Devices AD7314, ADT7301 and ADT7302 digital"
-                       " temperature sensor driver");
+MODULE_DESCRIPTION("Analog Devices AD7314, ADT7301 and ADT7302 digital temperature sensor driver");
 MODULE_LICENSE("GPL v2");
index 71bcba8abfc0a927ffe88403ea2d2ce3339913d7..7e76922a4ba9b0f9a56191b2afb1fba5814ccdf0 100644 (file)
@@ -312,8 +312,7 @@ static int adm1021_detect(struct i2c_client *client,
        int conv_rate, status, config, man_id, dev_id;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
-               pr_debug("adm1021: detect failed, "
-                        "smbus byte data not supported!\n");
+               pr_debug("detect failed, smbus byte data not supported!\n");
                return -ENODEV;
        }
 
@@ -324,7 +323,7 @@ static int adm1021_detect(struct i2c_client *client,
 
        /* Check unused bits */
        if ((status & 0x03) || (config & 0x3F) || (conv_rate & 0xF8)) {
-               pr_debug("adm1021: detect failed, chip not detected!\n");
+               pr_debug("detect failed, chip not detected!\n");
                return -ENODEV;
        }
 
@@ -353,7 +352,7 @@ static int adm1021_detect(struct i2c_client *client,
        else
                type_name = "max1617";
 
-       pr_debug("adm1021: Detected chip %s at adapter %d, address 0x%02x.\n",
+       pr_debug("Detected chip %s at adapter %d, address 0x%02x.\n",
                 type_name, i2c_adapter_id(adapter), client->addr);
        strlcpy(info->type, type_name, I2C_NAME_SIZE);
 
@@ -368,10 +367,8 @@ static int adm1021_probe(struct i2c_client *client,
 
        data = devm_kzalloc(&client->dev, sizeof(struct adm1021_data),
                            GFP_KERNEL);
-       if (!data) {
-               pr_debug("adm1021: detect failed, devm_kzalloc failed!\n");
+       if (!data)
                return -ENOMEM;
-       }
 
        i2c_set_clientdata(client, data);
        data->type = id->driver_data;
index ea09046e651d19d8585bb26b07bc7ba2339f9e3d..3a6d9ef1c16caa30612b15ca4fd698c10ba0a983 100644 (file)
@@ -49,14 +49,14 @@ static int gpio_fan[8] = { -1, -1, -1, -1, -1, -1, -1, -1 };
 module_param_array(gpio_input, int, NULL, 0);
 MODULE_PARM_DESC(gpio_input, "List of GPIO pins (0-16) to program as inputs");
 module_param_array(gpio_output, int, NULL, 0);
-MODULE_PARM_DESC(gpio_output, "List of GPIO pins (0-16) to program as "
-       "outputs");
+MODULE_PARM_DESC(gpio_output,
+                "List of GPIO pins (0-16) to program as outputs");
 module_param_array(gpio_inverted, int, NULL, 0);
-MODULE_PARM_DESC(gpio_inverted, "List of GPIO pins (0-16) to program as "
-       "inverted");
+MODULE_PARM_DESC(gpio_inverted,
+                "List of GPIO pins (0-16) to program as inverted");
 module_param_array(gpio_normal, int, NULL, 0);
-MODULE_PARM_DESC(gpio_normal, "List of GPIO pins (0-16) to program as "
-       "normal/non-inverted");
+MODULE_PARM_DESC(gpio_normal,
+                "List of GPIO pins (0-16) to program as normal/non-inverted");
 module_param_array(gpio_fan, int, NULL, 0);
 MODULE_PARM_DESC(gpio_fan, "List of GPIO pins (0-7) to program as fan tachs");
 
@@ -372,31 +372,31 @@ static void adm1026_init_client(struct i2c_client *client)
        dev_dbg(&client->dev, "ADM1026_REG_CONFIG1 is: 0x%02x\n",
                data->config1);
        if ((data->config1 & CFG1_MONITOR) == 0) {
-               dev_dbg(&client->dev, "Monitoring not currently "
-                       "enabled.\n");
+               dev_dbg(&client->dev,
+                       "Monitoring not currently enabled.\n");
        }
        if (data->config1 & CFG1_INT_ENABLE) {
-               dev_dbg(&client->dev, "SMBALERT interrupts are "
-                       "enabled.\n");
+               dev_dbg(&client->dev,
+                       "SMBALERT interrupts are enabled.\n");
        }
        if (data->config1 & CFG1_AIN8_9) {
-               dev_dbg(&client->dev, "in8 and in9 enabled. "
-                       "temp3 disabled.\n");
+               dev_dbg(&client->dev,
+                       "in8 and in9 enabled. temp3 disabled.\n");
        } else {
-               dev_dbg(&client->dev, "temp3 enabled.  in8 and "
-                       "in9 disabled.\n");
+               dev_dbg(&client->dev,
+                       "temp3 enabled.  in8 and in9 disabled.\n");
        }
        if (data->config1 & CFG1_THERM_HOT) {
-               dev_dbg(&client->dev, "Automatic THERM, PWM, "
-                       "and temp limits enabled.\n");
+               dev_dbg(&client->dev,
+                       "Automatic THERM, PWM, and temp limits enabled.\n");
        }
 
        if (data->config3 & CFG3_GPIO16_ENABLE) {
-               dev_dbg(&client->dev, "GPIO16 enabled.  THERM "
-                       "pin disabled.\n");
+               dev_dbg(&client->dev,
+                       "GPIO16 enabled.  THERM pin disabled.\n");
        } else {
-               dev_dbg(&client->dev, "THERM pin enabled.  "
-                       "GPIO16 disabled.\n");
+               dev_dbg(&client->dev,
+                       "THERM pin enabled.  GPIO16 disabled.\n");
        }
        if (data->config3 & CFG3_VREF_250)
                dev_dbg(&client->dev, "Vref is 2.50 Volts.\n");
@@ -1798,8 +1798,8 @@ static int adm1026_detect(struct i2c_client *client,
        company = adm1026_read_value(client, ADM1026_REG_COMPANY);
        verstep = adm1026_read_value(client, ADM1026_REG_VERSTEP);
 
-       dev_dbg(&adapter->dev, "Detecting device at %d,0x%02x with"
-               " COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
+       dev_dbg(&adapter->dev,
+               "Detecting device at %d,0x%02x with COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
                i2c_adapter_id(client->adapter), client->addr,
                company, verstep);
 
@@ -1811,11 +1811,12 @@ static int adm1026_detect(struct i2c_client *client,
                /* Analog Devices ADM1026 */
        } else if (company == ADM1026_COMPANY_ANALOG_DEV
                && (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
-               dev_err(&adapter->dev, "Unrecognized stepping "
-                       "0x%02x. Defaulting to ADM1026.\n", verstep);
+               dev_err(&adapter->dev,
+                       "Unrecognized stepping 0x%02x. Defaulting to ADM1026.\n",
+                       verstep);
        } else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
-               dev_err(&adapter->dev, "Found version/stepping "
-                       "0x%02x. Assuming generic ADM1026.\n",
+               dev_err(&adapter->dev,
+                       "Found version/stepping 0x%02x. Assuming generic ADM1026.\n",
                        verstep);
        } else {
                dev_dbg(&adapter->dev, "Autodetection failed\n");
index 97f4718382f6c5e6ab1f8bb9e617f237866a1194..9ee5e066423bee462f17dd6fc81979b618b25e20 100644 (file)
@@ -224,8 +224,9 @@ static ssize_t set_fan_div(struct device *dev,
                break;
        default:
                mutex_unlock(&data->update_lock);
-               dev_err(&client->dev, "fan_div value %ld not "
-                       "supported. Choose one of 1, 2 or 4!\n", val);
+               dev_err(&client->dev,
+                       "fan_div value %ld not supported. Choose one of 1, 2 or 4!\n",
+                       val);
                return -EINVAL;
        }
        /* Update the value */
@@ -326,8 +327,8 @@ static int adm1029_detect(struct i2c_client *client,
                 * There are no "official" CHIP ID, so actually
                 * we use Major/Minor revision for that
                 */
-               pr_info("adm1029: Unknown major revision %x, "
-                       "please let us know\n", chip_id);
+               pr_info("Unknown major revision %x, please let us know\n",
+                       chip_id);
                return -ENODEV;
        }
 
index 2416628e0ab1d78a264a4c8bc6a606a0057ed3fb..086d02a9ecdc2b270dd9d142a1473b5dc01bce04 100644 (file)
@@ -351,8 +351,9 @@ static void adm9240_write_fan_div(struct i2c_client *client, int nr,
        reg &= ~(3 << shift);
        reg |= (fan_div << shift);
        i2c_smbus_write_byte_data(client, ADM9240_REG_VID_FAN_DIV, reg);
-       dev_dbg(&client->dev, "fan%d clock divider changed from %u "
-                       "to %u\n", nr + 1, 1 << old, 1 << fan_div);
+       dev_dbg(&client->dev,
+               "fan%d clock divider changed from %u to %u\n",
+               nr + 1, 1 << old, 1 << fan_div);
 }
 
 /*
@@ -699,8 +700,8 @@ static void adm9240_init_client(struct i2c_client *client)
                /* start measurement cycle */
                i2c_smbus_write_byte_data(client, ADM9240_REG_CONFIG, 1);
 
-               dev_info(&client->dev, "cold start: config was 0x%02x "
-                               "mode %u\n", conf, mode);
+               dev_info(&client->dev,
+                        "cold start: config was 0x%02x mode %u\n", conf, mode);
        }
 }
 
index a79875986f911a62d4aeb8fd61a4081dff2d9356..3eff73b6220d87d92cc43b8aff5e8fcd7b774ab1 100644 (file)
  * the instruction byte
  */
 /*Instruction Bit masks*/
-#define INST_MODE_bm   (1<<7)
-#define INST_READ_bm   (1<<6)
-#define INST_16BIT_bm  (1<<5)
+#define INST_MODE_BM   (1 << 7)
+#define INST_READ_BM   (1 << 6)
+#define INST_16BIT_BM  (1 << 5)
 
 /*From figure 18 in the datasheet*/
 /*bit masks for Rev/Oscillator Control Register*/
-#define MUX_CNV_bv     7
-#define MUX_CNV_bm     (1<<MUX_CNV_bv)
-#define MUX_M3_bm      (1<<3) /*M3 selects single ended*/
-#define MUX_G_bv       4 /*allows for reg = (gain << MUX_G_bv) | ...*/
+#define MUX_CNV_BV     7
+#define MUX_CNV_BM     (1 << MUX_CNV_BV)
+#define MUX_M3_BM      (1 << 3) /*M3 selects single ended*/
+#define MUX_G_BV       4 /*allows for reg = (gain << MUX_G_BV) | ...*/
 
 /*From figure 18 in the datasheet*/
 /*bit masks for Rev/Oscillator Control Register*/
-#define OSC_OSCR_bm    (1<<5)
-#define OSC_OSCE_bm    (1<<4)
-#define OSC_REFE_bm    (1<<3)
-#define OSC_BUFE_bm    (1<<2)
-#define OSC_R2V_bm     (1<<1)
-#define OSC_RBG_bm     (1<<0)
+#define OSC_OSCR_BM    (1 << 5)
+#define OSC_OSCE_BM    (1 << 4)
+#define OSC_REFE_BM    (1 << 3)
+#define OSC_BUFE_BM    (1 << 2)
+#define OSC_R2V_BM     (1 << 1)
+#define OSC_RBG_BM     (1 << 0)
 
 #include <linux/module.h>
 #include <linux/init.h>
@@ -79,7 +79,7 @@ struct ads7871_data {
 static int ads7871_read_reg8(struct spi_device *spi, int reg)
 {
        int ret;
-       reg = reg | INST_READ_bm;
+       reg = reg | INST_READ_BM;
        ret = spi_w8r8(spi, reg);
        return ret;
 }
@@ -87,7 +87,7 @@ static int ads7871_read_reg8(struct spi_device *spi, int reg)
 static int ads7871_read_reg16(struct spi_device *spi, int reg)
 {
        int ret;
-       reg = reg | INST_READ_bm | INST_16BIT_bm;
+       reg = reg | INST_READ_BM | INST_16BIT_BM;
        ret = spi_w8r16(spi, reg);
        return ret;
 }
@@ -111,13 +111,13 @@ static ssize_t show_voltage(struct device *dev,
         * TODO: add support for conversions
         * other than single ended with a gain of 1
         */
-       /*MUX_M3_bm forces single ended*/
+       /*MUX_M3_BM forces single ended*/
        /*This is also where the gain of the PGA would be set*/
        ads7871_write_reg8(spi, REG_GAIN_MUX,
-               (MUX_CNV_bm | MUX_M3_bm | channel));
+               (MUX_CNV_BM | MUX_M3_BM | channel));
 
        ret = ads7871_read_reg8(spi, REG_GAIN_MUX);
-       mux_cnv = ((ret & MUX_CNV_bm)>>MUX_CNV_bv);
+       mux_cnv = ((ret & MUX_CNV_BM) >> MUX_CNV_BV);
        /*
         * on 400MHz arm9 platform the conversion
         * is already done when we do this test
@@ -125,14 +125,14 @@ static ssize_t show_voltage(struct device *dev,
        while ((i < 2) && mux_cnv) {
                i++;
                ret = ads7871_read_reg8(spi, REG_GAIN_MUX);
-               mux_cnv = ((ret & MUX_CNV_bm)>>MUX_CNV_bv);
+               mux_cnv = ((ret & MUX_CNV_BM) >> MUX_CNV_BV);
                msleep_interruptible(1);
        }
 
        if (mux_cnv == 0) {
                val = ads7871_read_reg16(spi, REG_LS_BYTE);
                /*result in volts*10000 = (val/8192)*2.5*10000*/
-               val = ((val>>2) * 25000) / 8192;
+               val = ((val >> 2) * 25000) / 8192;
                return sprintf(buf, "%d\n", val);
        } else {
                return -1;
@@ -189,7 +189,7 @@ static int ads7871_probe(struct spi_device *spi)
        ads7871_write_reg8(spi, REG_SER_CONTROL, 0);
        ads7871_write_reg8(spi, REG_AD_CONTROL, 0);
 
-       val = (OSC_OSCR_bm | OSC_OSCE_bm | OSC_REFE_bm | OSC_BUFE_bm);
+       val = (OSC_OSCR_BM | OSC_OSCE_BM | OSC_REFE_BM | OSC_BUFE_BM);
        ads7871_write_reg8(spi, REG_OSC_CONTROL, val);
        ret = ads7871_read_reg8(spi, REG_OSC_CONTROL);
 
diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c
new file mode 100644 (file)
index 0000000..da5f078
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * ADT7310/ADT7310 digital temperature sensor driver
+ *
+ * Copyright 2012-2013 Analog Devices Inc.
+ *   Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spi/spi.h>
+#include <asm/unaligned.h>
+
+#include "adt7x10.h"
+
+#define ADT7310_STATUS                 0
+#define ADT7310_CONFIG                 1
+#define ADT7310_TEMPERATURE            2
+#define ADT7310_ID                     3
+#define ADT7310_T_CRIT                 4
+#define ADT7310_T_HYST                 5
+#define ADT7310_T_ALARM_HIGH           6
+#define ADT7310_T_ALARM_LOW            7
+
+static const u8 adt7310_reg_table[] = {
+       [ADT7X10_TEMPERATURE]   = ADT7310_TEMPERATURE,
+       [ADT7X10_STATUS]        = ADT7310_STATUS,
+       [ADT7X10_CONFIG]        = ADT7310_CONFIG,
+       [ADT7X10_T_ALARM_HIGH]  = ADT7310_T_ALARM_HIGH,
+       [ADT7X10_T_ALARM_LOW]   = ADT7310_T_ALARM_LOW,
+       [ADT7X10_T_CRIT]        = ADT7310_T_CRIT,
+       [ADT7X10_T_HYST]        = ADT7310_T_HYST,
+       [ADT7X10_ID]            = ADT7310_ID,
+};
+
+#define ADT7310_CMD_REG_OFFSET 3
+#define ADT7310_CMD_READ       0x40
+
+#define AD7310_COMMAND(reg) (adt7310_reg_table[(reg)] << ADT7310_CMD_REG_OFFSET)
+
+static int adt7310_spi_read_word(struct device *dev, u8 reg)
+{
+       struct spi_device *spi = to_spi_device(dev);
+       int ret;
+
+       ret = spi_w8r16(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ);
+       if (ret < 0)
+               return ret;
+
+       return be16_to_cpu((__force __be16)ret);
+}
+
+static int adt7310_spi_write_word(struct device *dev, u8 reg, u16 data)
+{
+       struct spi_device *spi = to_spi_device(dev);
+       u8 buf[3];
+
+       buf[0] = AD7310_COMMAND(reg);
+       put_unaligned_be16(data, &buf[1]);
+
+       return spi_write(spi, buf, sizeof(buf));
+}
+
+static int adt7310_spi_read_byte(struct device *dev, u8 reg)
+{
+       struct spi_device *spi = to_spi_device(dev);
+
+       return spi_w8r8(spi, AD7310_COMMAND(reg) | ADT7310_CMD_READ);
+}
+
+static int adt7310_spi_write_byte(struct device *dev, u8 reg,
+       u8 data)
+{
+       struct spi_device *spi = to_spi_device(dev);
+       u8 buf[2];
+
+       buf[0] = AD7310_COMMAND(reg);
+       buf[1] = data;
+
+       return spi_write(spi, buf, sizeof(buf));
+}
+
+static const struct adt7x10_ops adt7310_spi_ops = {
+       .read_word = adt7310_spi_read_word,
+       .write_word = adt7310_spi_write_word,
+       .read_byte = adt7310_spi_read_byte,
+       .write_byte = adt7310_spi_write_byte,
+};
+
+static int adt7310_spi_probe(struct spi_device *spi)
+{
+       return adt7x10_probe(&spi->dev, spi_get_device_id(spi)->name, spi->irq,
+                       &adt7310_spi_ops);
+}
+
+static int adt7310_spi_remove(struct spi_device *spi)
+{
+       return adt7x10_remove(&spi->dev, spi->irq);
+}
+
+static const struct spi_device_id adt7310_id[] = {
+       { "adt7310", 0 },
+       { "adt7320", 0 },
+       {}
+};
+MODULE_DEVICE_TABLE(spi, adt7310_id);
+
+static struct spi_driver adt7310_driver = {
+       .driver = {
+               .name   = "adt7310",
+               .owner  = THIS_MODULE,
+               .pm     = ADT7X10_DEV_PM_OPS,
+       },
+       .probe          = adt7310_spi_probe,
+       .remove         = adt7310_spi_remove,
+       .id_table       = adt7310_id,
+};
+module_spi_driver(adt7310_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ADT7310/ADT7320 driver");
+MODULE_LICENSE("GPL");
index 99a7290da0a343c4e0e970c57eebfeca86486589..0dc066a939b4d25f94a573aa13dda54b43cc5fff 100644 (file)
 /*
- * adt7410.c - Part of lm_sensors, Linux kernel modules for hardware
- *      monitoring
- * This driver handles the ADT7410 and compatible digital temperature sensors.
- * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22
- * based on lm75.c by Frodo Looijaard <frodol@dds.nl>
- * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.com>
+ * ADT7410/ADT7420 digital temperature sensor driver
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Copyright 2012-2013 Analog Devices Inc.
+ *   Author: Lars-Peter Clausen <lars@metafoo.de>
  *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Licensed under the GPL-2 or later.
  */
 
 #include <linux/module.h>
 #include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/jiffies.h>
 #include <linux/i2c.h>
-#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-#include <linux/err.h>
-#include <linux/mutex.h>
-#include <linux/delay.h>
-
-/*
- * ADT7410 registers definition
- */
-
-#define ADT7410_TEMPERATURE            0
-#define ADT7410_STATUS                 2
-#define ADT7410_CONFIG                 3
-#define ADT7410_T_ALARM_HIGH           4
-#define ADT7410_T_ALARM_LOW            6
-#define ADT7410_T_CRIT                 8
-#define ADT7410_T_HYST                 0xA
-
-/*
- * ADT7410 status
- */
-#define ADT7410_STAT_T_LOW             (1 << 4)
-#define ADT7410_STAT_T_HIGH            (1 << 5)
-#define ADT7410_STAT_T_CRIT            (1 << 6)
-#define ADT7410_STAT_NOT_RDY           (1 << 7)
-
-/*
- * ADT7410 config
- */
-#define ADT7410_FAULT_QUEUE_MASK       (1 << 0 | 1 << 1)
-#define ADT7410_CT_POLARITY            (1 << 2)
-#define ADT7410_INT_POLARITY           (1 << 3)
-#define ADT7410_EVENT_MODE             (1 << 4)
-#define ADT7410_MODE_MASK              (1 << 5 | 1 << 6)
-#define ADT7410_FULL                   (0 << 5 | 0 << 6)
-#define ADT7410_PD                     (1 << 5 | 1 << 6)
-#define ADT7410_RESOLUTION             (1 << 7)
-
-/*
- * ADT7410 masks
- */
-#define ADT7410_T13_VALUE_MASK                 0xFFF8
-#define ADT7410_T_HYST_MASK                    0xF
-
-/* straight from the datasheet */
-#define ADT7410_TEMP_MIN (-55000)
-#define ADT7410_TEMP_MAX 150000
-
-enum adt7410_type {            /* keep sorted in alphabetical order */
-       adt7410,
-};
-
-static const u8 ADT7410_REG_TEMP[4] = {
-       ADT7410_TEMPERATURE,            /* input */
-       ADT7410_T_ALARM_HIGH,           /* high */
-       ADT7410_T_ALARM_LOW,            /* low */
-       ADT7410_T_CRIT,                 /* critical */
-};
-
-/* Each client has this additional data */
-struct adt7410_data {
-       struct device           *hwmon_dev;
-       struct mutex            update_lock;
-       u8                      config;
-       u8                      oldconfig;
-       bool                    valid;          /* true if registers valid */
-       unsigned long           last_updated;   /* In jiffies */
-       s16                     temp[4];        /* Register values,
-                                                  0 = input
-                                                  1 = high
-                                                  2 = low
-                                                  3 = critical */
-       u8                      hyst;           /* hysteresis offset */
-};
-
-/*
- * adt7410 register access by I2C
- */
-static int adt7410_temp_ready(struct i2c_client *client)
-{
-       int i, status;
-
-       for (i = 0; i < 6; i++) {
-               status = i2c_smbus_read_byte_data(client, ADT7410_STATUS);
-               if (status < 0)
-                       return status;
-               if (!(status & ADT7410_STAT_NOT_RDY))
-                       return 0;
-               msleep(60);
-       }
-       return -ETIMEDOUT;
-}
-
-static struct adt7410_data *adt7410_update_device(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct adt7410_data *data = i2c_get_clientdata(client);
-       struct adt7410_data *ret = data;
-       mutex_lock(&data->update_lock);
-
-       if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
-           || !data->valid) {
-               int i, status;
 
-               dev_dbg(&client->dev, "Starting update\n");
+#include "adt7x10.h"
 
-               status = adt7410_temp_ready(client); /* check for new value */
-               if (unlikely(status)) {
-                       ret = ERR_PTR(status);
-                       goto abort;
-               }
-               for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
-                       status = i2c_smbus_read_word_swapped(client,
-                                                       ADT7410_REG_TEMP[i]);
-                       if (unlikely(status < 0)) {
-                               dev_dbg(dev,
-                                       "Failed to read value: reg %d, error %d\n",
-                                       ADT7410_REG_TEMP[i], status);
-                               ret = ERR_PTR(status);
-                               goto abort;
-                       }
-                       data->temp[i] = status;
-               }
-               status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
-               if (unlikely(status < 0)) {
-                       dev_dbg(dev,
-                               "Failed to read value: reg %d, error %d\n",
-                               ADT7410_T_HYST, status);
-                       ret = ERR_PTR(status);
-                       goto abort;
-               }
-               data->hyst = status;
-               data->last_updated = jiffies;
-               data->valid = true;
-       }
-
-abort:
-       mutex_unlock(&data->update_lock);
-       return ret;
-}
-
-static s16 ADT7410_TEMP_TO_REG(long temp)
+static int adt7410_i2c_read_word(struct device *dev, u8 reg)
 {
-       return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7410_TEMP_MIN,
-                                          ADT7410_TEMP_MAX) * 128, 1000);
+       return i2c_smbus_read_word_swapped(to_i2c_client(dev), reg);
 }
 
-static int ADT7410_REG_TO_TEMP(struct adt7410_data *data, s16 reg)
+static int adt7410_i2c_write_word(struct device *dev, u8 reg, u16 data)
 {
-       /* in 13 bit mode, bits 0-2 are status flags - mask them out */
-       if (!(data->config & ADT7410_RESOLUTION))
-               reg &= ADT7410_T13_VALUE_MASK;
-       /*
-        * temperature is stored in twos complement format, in steps of
-        * 1/128°C
-        */
-       return DIV_ROUND_CLOSEST(reg * 1000, 128);
+       return i2c_smbus_write_word_swapped(to_i2c_client(dev), reg, data);
 }
 
-/*-----------------------------------------------------------------------*/
-
-/* sysfs attributes for hwmon */
-
-static ssize_t adt7410_show_temp(struct device *dev,
-                                struct device_attribute *da, char *buf)
+static int adt7410_i2c_read_byte(struct device *dev, u8 reg)
 {
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
-       struct adt7410_data *data = adt7410_update_device(dev);
-
-       if (IS_ERR(data))
-               return PTR_ERR(data);
-
-       return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data,
-                      data->temp[attr->index]));
+       return i2c_smbus_read_byte_data(to_i2c_client(dev), reg);
 }
 
-static ssize_t adt7410_set_temp(struct device *dev,
-                               struct device_attribute *da,
-                               const char *buf, size_t count)
+static int adt7410_i2c_write_byte(struct device *dev, u8 reg, u8 data)
 {
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
-       struct i2c_client *client = to_i2c_client(dev);
-       struct adt7410_data *data = i2c_get_clientdata(client);
-       int nr = attr->index;
-       long temp;
-       int ret;
-
-       ret = kstrtol(buf, 10, &temp);
-       if (ret)
-               return ret;
-
-       mutex_lock(&data->update_lock);
-       data->temp[nr] = ADT7410_TEMP_TO_REG(temp);
-       ret = i2c_smbus_write_word_swapped(client, ADT7410_REG_TEMP[nr],
-                                          data->temp[nr]);
-       if (ret)
-               count = ret;
-       mutex_unlock(&data->update_lock);
-       return count;
+       return i2c_smbus_write_byte_data(to_i2c_client(dev), reg, data);
 }
 
-static ssize_t adt7410_show_t_hyst(struct device *dev,
-                                  struct device_attribute *da,
-                                  char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
-       struct adt7410_data *data;
-       int nr = attr->index;
-       int hyst;
-
-       data = adt7410_update_device(dev);
-       if (IS_ERR(data))
-               return PTR_ERR(data);
-       hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;
-
-       /*
-        * hysteresis is stored as a 4 bit offset in the device, convert it
-        * to an absolute value
-        */
-       if (nr == 2)    /* min has positive offset, others have negative */
-               hyst = -hyst;
-       return sprintf(buf, "%d\n",
-                      ADT7410_REG_TO_TEMP(data, data->temp[nr]) - hyst);
-}
-
-static ssize_t adt7410_set_t_hyst(struct device *dev,
-                                 struct device_attribute *da,
-                                 const char *buf, size_t count)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct adt7410_data *data = i2c_get_clientdata(client);
-       int limit, ret;
-       long hyst;
-
-       ret = kstrtol(buf, 10, &hyst);
-       if (ret)
-               return ret;
-       /* convert absolute hysteresis value to a 4 bit delta value */
-       limit = ADT7410_REG_TO_TEMP(data, data->temp[1]);
-       hyst = clamp_val(hyst, ADT7410_TEMP_MIN, ADT7410_TEMP_MAX);
-       data->hyst = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000), 0,
-                              ADT7410_T_HYST_MASK);
-       ret = i2c_smbus_write_byte_data(client, ADT7410_T_HYST, data->hyst);
-       if (ret)
-               return ret;
-
-       return count;
-}
-
-static ssize_t adt7410_show_alarm(struct device *dev,
-                                 struct device_attribute *da,
-                                 char *buf)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
-       int ret;
-
-       ret = i2c_smbus_read_byte_data(client, ADT7410_STATUS);
-       if (ret < 0)
-               return ret;
-
-       return sprintf(buf, "%d\n", !!(ret & attr->index));
-}
-
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7410_show_temp, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
-                         adt7410_show_temp, adt7410_set_temp, 1);
-static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
-                         adt7410_show_temp, adt7410_set_temp, 2);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
-                         adt7410_show_temp, adt7410_set_temp, 3);
-static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
-                         adt7410_show_t_hyst, adt7410_set_t_hyst, 1);
-static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO,
-                         adt7410_show_t_hyst, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO,
-                         adt7410_show_t_hyst, NULL, 3);
-static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, adt7410_show_alarm,
-                         NULL, ADT7410_STAT_T_LOW);
-static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adt7410_show_alarm,
-                         NULL, ADT7410_STAT_T_HIGH);
-static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, adt7410_show_alarm,
-                         NULL, ADT7410_STAT_T_CRIT);
-
-static struct attribute *adt7410_attributes[] = {
-       &sensor_dev_attr_temp1_input.dev_attr.attr,
-       &sensor_dev_attr_temp1_max.dev_attr.attr,
-       &sensor_dev_attr_temp1_min.dev_attr.attr,
-       &sensor_dev_attr_temp1_crit.dev_attr.attr,
-       &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
-       &sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
-       &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
-       &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
-       &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
-       NULL
+static const struct adt7x10_ops adt7410_i2c_ops = {
+       .read_word = adt7410_i2c_read_word,
+       .write_word = adt7410_i2c_write_word,
+       .read_byte = adt7410_i2c_read_byte,
+       .write_byte = adt7410_i2c_write_byte,
 };
 
-static const struct attribute_group adt7410_group = {
-       .attrs = adt7410_attributes,
-};
-
-/*-----------------------------------------------------------------------*/
-
-/* device probe and removal */
-
-static int adt7410_probe(struct i2c_client *client,
-                        const struct i2c_device_id *id)
+static int adt7410_i2c_probe(struct i2c_client *client,
+       const struct i2c_device_id *id)
 {
-       struct adt7410_data *data;
-       int ret;
-
        if (!i2c_check_functionality(client->adapter,
                        I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
                return -ENODEV;
 
-       data = devm_kzalloc(&client->dev, sizeof(struct adt7410_data),
-                           GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       i2c_set_clientdata(client, data);
-       mutex_init(&data->update_lock);
-
-       /* configure as specified */
-       ret = i2c_smbus_read_byte_data(client, ADT7410_CONFIG);
-       if (ret < 0) {
-               dev_dbg(&client->dev, "Can't read config? %d\n", ret);
-               return ret;
-       }
-       data->oldconfig = ret;
-       /*
-        * Set to 16 bit resolution, continous conversion and comparator mode.
-        */
-       ret &= ~ADT7410_MODE_MASK;
-       data->config = ret | ADT7410_FULL | ADT7410_RESOLUTION |
-                       ADT7410_EVENT_MODE;
-       if (data->config != data->oldconfig) {
-               ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
-                                               data->config);
-               if (ret)
-                       return ret;
-       }
-       dev_dbg(&client->dev, "Config %02x\n", data->config);
-
-       /* Register sysfs hooks */
-       ret = sysfs_create_group(&client->dev.kobj, &adt7410_group);
-       if (ret)
-               goto exit_restore;
-
-       data->hwmon_dev = hwmon_device_register(&client->dev);
-       if (IS_ERR(data->hwmon_dev)) {
-               ret = PTR_ERR(data->hwmon_dev);
-               goto exit_remove;
-       }
-
-       dev_info(&client->dev, "sensor '%s'\n", client->name);
-
-       return 0;
-
-exit_remove:
-       sysfs_remove_group(&client->dev.kobj, &adt7410_group);
-exit_restore:
-       i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->oldconfig);
-       return ret;
+       return adt7x10_probe(&client->dev, NULL, client->irq, &adt7410_i2c_ops);
 }
 
-static int adt7410_remove(struct i2c_client *client)
+static int adt7410_i2c_remove(struct i2c_client *client)
 {
-       struct adt7410_data *data = i2c_get_clientdata(client);
-
-       hwmon_device_unregister(data->hwmon_dev);
-       sysfs_remove_group(&client->dev.kobj, &adt7410_group);
-       if (data->oldconfig != data->config)
-               i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
-                                         data->oldconfig);
-       return 0;
+       return adt7x10_remove(&client->dev, client->irq);
 }
 
 static const struct i2c_device_id adt7410_ids[] = {
-       { "adt7410", adt7410, },
-       { "adt7420", adt7410, },
-       { /* LIST END */ }
+       { "adt7410", 0 },
+       { "adt7420", 0 },
+       {}
 };
 MODULE_DEVICE_TABLE(i2c, adt7410_ids);
 
-#ifdef CONFIG_PM_SLEEP
-static int adt7410_suspend(struct device *dev)
-{
-       int ret;
-       struct i2c_client *client = to_i2c_client(dev);
-       struct adt7410_data *data = i2c_get_clientdata(client);
-
-       ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
-                                       data->config | ADT7410_PD);
-       return ret;
-}
-
-static int adt7410_resume(struct device *dev)
-{
-       int ret;
-       struct i2c_client *client = to_i2c_client(dev);
-       struct adt7410_data *data = i2c_get_clientdata(client);
-
-       ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->config);
-       return ret;
-}
-
-static SIMPLE_DEV_PM_OPS(adt7410_dev_pm_ops, adt7410_suspend, adt7410_resume);
-
-#define ADT7410_DEV_PM_OPS (&adt7410_dev_pm_ops)
-#else
-#define ADT7410_DEV_PM_OPS NULL
-#endif /* CONFIG_PM */
-
 static struct i2c_driver adt7410_driver = {
        .class          = I2C_CLASS_HWMON,
        .driver = {
                .name   = "adt7410",
-               .pm     = ADT7410_DEV_PM_OPS,
+               .pm     = ADT7X10_DEV_PM_OPS,
        },
-       .probe          = adt7410_probe,
-       .remove         = adt7410_remove,
+       .probe          = adt7410_i2c_probe,
+       .remove         = adt7410_i2c_remove,
        .id_table       = adt7410_ids,
        .address_list   = I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b),
 };
-
 module_i2c_driver(adt7410_driver);
 
-MODULE_AUTHOR("Hartmut Knaack");
-MODULE_DESCRIPTION("ADT7410/ADT7420 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ADT7410/AD7420 driver");
 MODULE_LICENSE("GPL");
index 34ff03abb50bdff5a8b87a207733d273959cc628..d9299dee37d17b9a4cdedc9fdd1f7427e693244c 100644 (file)
@@ -259,15 +259,17 @@ static int adt7411_detect(struct i2c_client *client,
 
        val = i2c_smbus_read_byte_data(client, ADT7411_REG_MANUFACTURER_ID);
        if (val < 0 || val != ADT7411_MANUFACTURER_ID) {
-               dev_dbg(&client->dev, "Wrong manufacturer ID. Got %d, "
-                       "expected %d\n", val, ADT7411_MANUFACTURER_ID);
+               dev_dbg(&client->dev,
+                       "Wrong manufacturer ID. Got %d, expected %d\n",
+                       val, ADT7411_MANUFACTURER_ID);
                return -ENODEV;
        }
 
        val = i2c_smbus_read_byte_data(client, ADT7411_REG_DEVICE_ID);
        if (val < 0 || val != ADT7411_DEVICE_ID) {
-               dev_dbg(&client->dev, "Wrong device ID. Got %d, "
-                       "expected %d\n", val, ADT7411_DEVICE_ID);
+               dev_dbg(&client->dev,
+                       "Wrong device ID. Got %d, expected %d\n",
+                       val, ADT7411_DEVICE_ID);
                return -ENODEV;
        }
 
diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c
new file mode 100644 (file)
index 0000000..98141f4
--- /dev/null
@@ -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 (file)
index 0000000..d491c69
--- /dev/null
@@ -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
index b41baffa20f0287ecb69de92ffbc66944535048f..62c2e32e25ef6823290380074d50829af30e5751 100644 (file)
@@ -922,7 +922,7 @@ static void applesmc_brightness_set(struct led_classdev *led_cdev,
        ret = queue_work(applesmc_led_wq, &backlight_work);
 
        if (debug && (!ret))
-               printk(KERN_DEBUG "applesmc: work was already on the queue.\n");
+               dev_dbg(led_cdev->dev, "work was already on the queue.\n");
 }
 
 static ssize_t applesmc_key_count_show(struct device *dev,
index 6ac612cabda1e1acace7382c74a553346468097f..f96063680e584ff5f7cbb1d6dec8147a0592e186 100644 (file)
@@ -55,8 +55,8 @@ static const unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END };
 
 static unsigned short force_subclients[4];
 module_param_array(force_subclients, short, NULL, 0);
-MODULE_PARM_DESC(force_subclients, "List of subclient addresses: "
-       "{bus, clientaddr, subclientaddr1, subclientaddr2}");
+MODULE_PARM_DESC(force_subclients,
+       "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}");
 
 /* Voltage IN registers 0-6 */
 #define ASB100_REG_IN(nr)      (0x20 + (nr))
@@ -689,8 +689,8 @@ static int asb100_detect_subclients(struct i2c_client *client)
                for (i = 2; i <= 3; i++) {
                        if (force_subclients[i] < 0x48 ||
                            force_subclients[i] > 0x4f) {
-                               dev_err(&client->dev, "invalid subclient "
-                                       "address %d; must be 0x48-0x4f\n",
+                               dev_err(&client->dev,
+                                       "invalid subclient address %d; must be 0x48-0x4f\n",
                                        force_subclients[i]);
                                err = -ENODEV;
                                goto ERROR_SC_2;
@@ -708,24 +708,27 @@ static int asb100_detect_subclients(struct i2c_client *client)
        }
 
        if (sc_addr[0] == sc_addr[1]) {
-               dev_err(&client->dev, "duplicate addresses 0x%x "
-                               "for subclients\n", sc_addr[0]);
+               dev_err(&client->dev,
+                       "duplicate addresses 0x%x for subclients\n",
+                       sc_addr[0]);
                err = -ENODEV;
                goto ERROR_SC_2;
        }
 
        data->lm75[0] = i2c_new_dummy(adapter, sc_addr[0]);
        if (!data->lm75[0]) {
-               dev_err(&client->dev, "subclient %d registration "
-                       "at address 0x%x failed.\n", 1, sc_addr[0]);
+               dev_err(&client->dev,
+                       "subclient %d registration at address 0x%x failed.\n",
+                       1, sc_addr[0]);
                err = -ENOMEM;
                goto ERROR_SC_2;
        }
 
        data->lm75[1] = i2c_new_dummy(adapter, sc_addr[1]);
        if (!data->lm75[1]) {
-               dev_err(&client->dev, "subclient %d registration "
-                       "at address 0x%x failed.\n", 2, sc_addr[1]);
+               dev_err(&client->dev,
+                       "subclient %d registration at address 0x%x failed.\n",
+                       2, sc_addr[1]);
                err = -ENOMEM;
                goto ERROR_SC_3;
        }
index da7f5b5d5db5a2e36ca1295fc9052651f35ab0c2..3ad9d849add2d95f96b263b590f41d70f6f78b84 100644 (file)
@@ -159,12 +159,12 @@ static inline int write_byte(struct i2c_client *client, u8 reg, u8 data)
  * and retrieval of like parameters.
  */
 
-#define SETUP_SHOW_data_param(d, a) \
+#define SETUP_SHOW_DATA_PARAM(d, a) \
        struct sensor_device_attribute *sda = to_sensor_dev_attr(a); \
        struct asc7621_data *data = asc7621_update_device(d); \
        struct asc7621_param *param = to_asc7621_param(sda)
 
-#define SETUP_STORE_data_param(d, a) \
+#define SETUP_STORE_DATA_PARAM(d, a) \
        struct sensor_device_attribute *sda = to_sensor_dev_attr(a); \
        struct i2c_client *client = to_i2c_client(d); \
        struct asc7621_data *data = i2c_get_clientdata(client); \
@@ -177,7 +177,7 @@ static inline int write_byte(struct i2c_client *client, u8 reg, u8 data)
 static ssize_t show_u8(struct device *dev, struct device_attribute *attr,
                       char *buf)
 {
-       SETUP_SHOW_data_param(dev, attr);
+       SETUP_SHOW_DATA_PARAM(dev, attr);
 
        return sprintf(buf, "%u\n", data->reg[param->msb[0]]);
 }
@@ -185,7 +185,7 @@ static ssize_t show_u8(struct device *dev, struct device_attribute *attr,
 static ssize_t store_u8(struct device *dev, struct device_attribute *attr,
                        const char *buf, size_t count)
 {
-       SETUP_STORE_data_param(dev, attr);
+       SETUP_STORE_DATA_PARAM(dev, attr);
        long reqval;
 
        if (kstrtol(buf, 10, &reqval))
@@ -206,7 +206,7 @@ static ssize_t store_u8(struct device *dev, struct device_attribute *attr,
 static ssize_t show_bitmask(struct device *dev,
                            struct device_attribute *attr, char *buf)
 {
-       SETUP_SHOW_data_param(dev, attr);
+       SETUP_SHOW_DATA_PARAM(dev, attr);
 
        return sprintf(buf, "%u\n",
                       (data->reg[param->msb[0]] >> param->
@@ -217,7 +217,7 @@ static ssize_t store_bitmask(struct device *dev,
                             struct device_attribute *attr,
                             const char *buf, size_t count)
 {
-       SETUP_STORE_data_param(dev, attr);
+       SETUP_STORE_DATA_PARAM(dev, attr);
        long reqval;
        u8 currval;
 
@@ -246,7 +246,7 @@ static ssize_t store_bitmask(struct device *dev,
 static ssize_t show_fan16(struct device *dev,
                          struct device_attribute *attr, char *buf)
 {
-       SETUP_SHOW_data_param(dev, attr);
+       SETUP_SHOW_DATA_PARAM(dev, attr);
        u16 regval;
 
        mutex_lock(&data->update_lock);
@@ -262,7 +262,7 @@ static ssize_t store_fan16(struct device *dev,
                           struct device_attribute *attr, const char *buf,
                           size_t count)
 {
-       SETUP_STORE_data_param(dev, attr);
+       SETUP_STORE_DATA_PARAM(dev, attr);
        long reqval;
 
        if (kstrtol(buf, 10, &reqval))
@@ -307,7 +307,7 @@ static int asc7621_in_scaling[] = {
 static ssize_t show_in10(struct device *dev, struct device_attribute *attr,
                         char *buf)
 {
-       SETUP_SHOW_data_param(dev, attr);
+       SETUP_SHOW_DATA_PARAM(dev, attr);
        u16 regval;
        u8 nr = sda->index;
 
@@ -325,7 +325,7 @@ static ssize_t show_in10(struct device *dev, struct device_attribute *attr,
 static ssize_t show_in8(struct device *dev, struct device_attribute *attr,
                        char *buf)
 {
-       SETUP_SHOW_data_param(dev, attr);
+       SETUP_SHOW_DATA_PARAM(dev, attr);
        u8 nr = sda->index;
 
        return sprintf(buf, "%u\n",
@@ -336,7 +336,7 @@ static ssize_t show_in8(struct device *dev, struct device_attribute *attr,
 static ssize_t store_in8(struct device *dev, struct device_attribute *attr,
                         const char *buf, size_t count)
 {
-       SETUP_STORE_data_param(dev, attr);
+       SETUP_STORE_DATA_PARAM(dev, attr);
        long reqval;
        u8 nr = sda->index;
 
@@ -360,7 +360,7 @@ static ssize_t store_in8(struct device *dev, struct device_attribute *attr,
 static ssize_t show_temp8(struct device *dev,
                          struct device_attribute *attr, char *buf)
 {
-       SETUP_SHOW_data_param(dev, attr);
+       SETUP_SHOW_DATA_PARAM(dev, attr);
 
        return sprintf(buf, "%d\n", ((s8) data->reg[param->msb[0]]) * 1000);
 }
@@ -369,7 +369,7 @@ static ssize_t store_temp8(struct device *dev,
                           struct device_attribute *attr, const char *buf,
                           size_t count)
 {
-       SETUP_STORE_data_param(dev, attr);
+       SETUP_STORE_DATA_PARAM(dev, attr);
        long reqval;
        s8 temp;
 
@@ -397,7 +397,7 @@ static ssize_t store_temp8(struct device *dev,
 static ssize_t show_temp10(struct device *dev,
                           struct device_attribute *attr, char *buf)
 {
-       SETUP_SHOW_data_param(dev, attr);
+       SETUP_SHOW_DATA_PARAM(dev, attr);
        u8 msb, lsb;
        int temp;
 
@@ -414,7 +414,7 @@ static ssize_t show_temp10(struct device *dev,
 static ssize_t show_temp62(struct device *dev,
                           struct device_attribute *attr, char *buf)
 {
-       SETUP_SHOW_data_param(dev, attr);
+       SETUP_SHOW_DATA_PARAM(dev, attr);
        u8 regval = data->reg[param->msb[0]];
        int temp = ((s8) (regval & 0xfc) * 1000) + ((regval & 0x03) * 250);
 
@@ -425,7 +425,7 @@ static ssize_t store_temp62(struct device *dev,
                            struct device_attribute *attr, const char *buf,
                            size_t count)
 {
-       SETUP_STORE_data_param(dev, attr);
+       SETUP_STORE_DATA_PARAM(dev, attr);
        long reqval, i, f;
        s8 temp;
 
@@ -459,7 +459,7 @@ static u32 asc7621_range_map[] = {
 static ssize_t show_ap2_temp(struct device *dev,
                             struct device_attribute *attr, char *buf)
 {
-       SETUP_SHOW_data_param(dev, attr);
+       SETUP_SHOW_DATA_PARAM(dev, attr);
        long auto_point1;
        u8 regval;
        int temp;
@@ -479,7 +479,7 @@ static ssize_t store_ap2_temp(struct device *dev,
                              struct device_attribute *attr,
                              const char *buf, size_t count)
 {
-       SETUP_STORE_data_param(dev, attr);
+       SETUP_STORE_DATA_PARAM(dev, attr);
        long reqval, auto_point1;
        int i;
        u8 currval, newval = 0;
@@ -510,7 +510,7 @@ static ssize_t store_ap2_temp(struct device *dev,
 static ssize_t show_pwm_ac(struct device *dev,
                           struct device_attribute *attr, char *buf)
 {
-       SETUP_SHOW_data_param(dev, attr);
+       SETUP_SHOW_DATA_PARAM(dev, attr);
        u8 config, altbit, regval;
        u8 map[] = {
                0x01, 0x02, 0x04, 0x1f, 0x00, 0x06, 0x07, 0x10,
@@ -530,7 +530,7 @@ static ssize_t store_pwm_ac(struct device *dev,
                            struct device_attribute *attr,
                            const char *buf, size_t count)
 {
-       SETUP_STORE_data_param(dev, attr);
+       SETUP_STORE_DATA_PARAM(dev, attr);
        unsigned long reqval;
        u8 currval, config, altbit, newval;
        u16 map[] = {
@@ -569,7 +569,7 @@ static ssize_t store_pwm_ac(struct device *dev,
 static ssize_t show_pwm_enable(struct device *dev,
                               struct device_attribute *attr, char *buf)
 {
-       SETUP_SHOW_data_param(dev, attr);
+       SETUP_SHOW_DATA_PARAM(dev, attr);
        u8 config, altbit, minoff, val, newval;
 
        mutex_lock(&data->update_lock);
@@ -599,7 +599,7 @@ static ssize_t store_pwm_enable(struct device *dev,
                                struct device_attribute *attr,
                                const char *buf, size_t count)
 {
-       SETUP_STORE_data_param(dev, attr);
+       SETUP_STORE_DATA_PARAM(dev, attr);
        long reqval;
        u8 currval, config, altbit, newval, minoff = 255;
 
@@ -659,7 +659,7 @@ static u32 asc7621_pwm_freq_map[] = {
 static ssize_t show_pwm_freq(struct device *dev,
                             struct device_attribute *attr, char *buf)
 {
-       SETUP_SHOW_data_param(dev, attr);
+       SETUP_SHOW_DATA_PARAM(dev, attr);
        u8 regval =
            (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
 
@@ -672,7 +672,7 @@ static ssize_t store_pwm_freq(struct device *dev,
                              struct device_attribute *attr,
                              const char *buf, size_t count)
 {
-       SETUP_STORE_data_param(dev, attr);
+       SETUP_STORE_DATA_PARAM(dev, attr);
        unsigned long reqval;
        u8 currval, newval = 255;
        int i;
@@ -707,7 +707,7 @@ static u32 asc7621_pwm_auto_spinup_map[] =  {
 static ssize_t show_pwm_ast(struct device *dev,
                            struct device_attribute *attr, char *buf)
 {
-       SETUP_SHOW_data_param(dev, attr);
+       SETUP_SHOW_DATA_PARAM(dev, attr);
        u8 regval =
            (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
 
@@ -721,7 +721,7 @@ static ssize_t store_pwm_ast(struct device *dev,
                             struct device_attribute *attr,
                             const char *buf, size_t count)
 {
-       SETUP_STORE_data_param(dev, attr);
+       SETUP_STORE_DATA_PARAM(dev, attr);
        long reqval;
        u8 currval, newval = 255;
        u32 i;
@@ -756,7 +756,7 @@ static u32 asc7621_temp_smoothing_time_map[] = {
 static ssize_t show_temp_st(struct device *dev,
                            struct device_attribute *attr, char *buf)
 {
-       SETUP_SHOW_data_param(dev, attr);
+       SETUP_SHOW_DATA_PARAM(dev, attr);
        u8 regval =
            (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
        regval = clamp_val(regval, 0, 7);
@@ -768,7 +768,7 @@ static ssize_t store_temp_st(struct device *dev,
                             struct device_attribute *attr,
                             const char *buf, size_t count)
 {
-       SETUP_STORE_data_param(dev, attr);
+       SETUP_STORE_DATA_PARAM(dev, attr);
        long reqval;
        u8 currval, newval = 255;
        u32 i;
index 3f1e297663ada0fefa412ce2e0fd4e362d84af50..658ce3a8717f6af0fa44f8eec4707d91bd44988d 100644 (file)
@@ -411,8 +411,7 @@ static int __cpuinit chk_ucode_version(unsigned int cpu)
         * fixed for stepping D0 (6EC).
         */
        if (c->x86_model == 0xe && c->x86_mask < 0xc && c->microcode < 0x39) {
-               pr_err("Errata AE18 not fixed, update BIOS or "
-                      "microcode of the CPU!\n");
+               pr_err("Errata AE18 not fixed, update BIOS or microcode of the CPU!\n");
                return -ENODEV;
        }
        return 0;
index ab4452c5a98c629c1d66b583d7ccd1ac359d5ea5..960fac3fb16648ce327eabf2d84018e42c871cb0 100644 (file)
@@ -43,19 +43,19 @@ static const char * const input_names[] = {
 };
 
 /* Conversion function for VDDOUT and VBAT */
-static inline int volt_reg_to_mV(int value)
+static inline int volt_reg_to_mv(int value)
 {
        return DIV_ROUND_CLOSEST(value * 1000, 512) + 2500;
 }
 
 /* Conversion function for ADC channels 4, 5 and 6 */
-static inline int input_reg_to_mV(int value)
+static inline int input_reg_to_mv(int value)
 {
        return DIV_ROUND_CLOSEST(value * 2500, 1023);
 }
 
 /* Conversion function for VBBAT */
-static inline int vbbat_reg_to_mV(int value)
+static inline int vbbat_reg_to_mv(int value)
 {
        return DIV_ROUND_CLOSEST(value * 2500, 512);
 }
@@ -96,7 +96,7 @@ static ssize_t da9052_read_vddout(struct device *dev,
                goto hwmon_err;
 
        mutex_unlock(&hwmon->hwmon_lock);
-       return sprintf(buf, "%d\n", volt_reg_to_mV(vdd));
+       return sprintf(buf, "%d\n", volt_reg_to_mv(vdd));
 
 hwmon_err_release:
        da9052_disable_vddout_channel(hwmon->da9052);
@@ -137,7 +137,7 @@ static ssize_t da9052_read_vbat(struct device *dev,
        if (ret < 0)
                return ret;
 
-       return sprintf(buf, "%d\n", volt_reg_to_mV(ret));
+       return sprintf(buf, "%d\n", volt_reg_to_mv(ret));
 }
 
 static ssize_t da9052_read_misc_channel(struct device *dev,
@@ -152,7 +152,7 @@ static ssize_t da9052_read_misc_channel(struct device *dev,
        if (ret < 0)
                return ret;
 
-       return sprintf(buf, "%d\n", input_reg_to_mV(ret));
+       return sprintf(buf, "%d\n", input_reg_to_mv(ret));
 }
 
 static ssize_t da9052_read_tjunc(struct device *dev,
@@ -187,7 +187,7 @@ static ssize_t da9052_read_vbbat(struct device *dev,
        if (ret < 0)
                return ret;
 
-       return sprintf(buf, "%d\n", vbbat_reg_to_mV(ret));
+       return sprintf(buf, "%d\n", vbbat_reg_to_mv(ret));
 }
 
 static ssize_t da9052_hwmon_show_name(struct device *dev,
index 9465c050c326c6dadf58ed06e210c4fabfccdca3..029ecabc4380dddae08fc62fb15d67c111caaaf2 100644 (file)
@@ -119,7 +119,7 @@ static irqreturn_t da9055_auxadc_irq(int irq, void *irq_data)
 }
 
 /* Conversion function for VSYS and ADCINx */
-static inline int volt_reg_to_mV(int value, int channel)
+static inline int volt_reg_to_mv(int value, int channel)
 {
        if (channel == DA9055_ADC_VSYS)
                return DIV_ROUND_CLOSEST(value * 1000, DA9055_VSYS_DIV) + 2500;
@@ -168,7 +168,7 @@ static ssize_t da9055_read_auto_ch(struct device *dev,
 
        mutex_unlock(&hwmon->hwmon_lock);
 
-       return sprintf(buf, "%d\n", volt_reg_to_mV(adc, channel));
+       return sprintf(buf, "%d\n", volt_reg_to_mv(adc, channel));
 
 hwmon_err_release:
        da9055_disable_auto_mode(hwmon->da9055, channel);
index c347c94f2f7336dbc4b86f3e841f7ebf1a44954b..4ae3fff13f4498dbef26281679cfc6b9dc0bfe48 100644 (file)
@@ -55,14 +55,16 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID");
 
 static bool probe_all_addr;
 module_param(probe_all_addr, bool, 0);
-MODULE_PARM_DESC(probe_all_addr, "Include probing of non-standard LPC "
-                "addresses");
+MODULE_PARM_DESC(probe_all_addr,
+                "Include probing of non-standard LPC addresses");
 
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END};
 
 enum chips { dme1737, sch5027, sch311x, sch5127 };
 
+#define        DO_REPORT "Please report to the driver maintainer."
+
 /* ---------------------------------------------------------------------
  * Registers
  *
@@ -566,9 +568,9 @@ static u8 dme1737_read(const struct dme1737_data *data, u8 reg)
                val = i2c_smbus_read_byte_data(client, reg);
 
                if (val < 0) {
-                       dev_warn(&client->dev, "Read from register "
-                                "0x%02x failed! Please report to the driver "
-                                "maintainer.\n", reg);
+                       dev_warn(&client->dev,
+                                "Read from register 0x%02x failed! %s\n",
+                                reg, DO_REPORT);
                }
        } else { /* ISA device */
                outb(reg, data->addr);
@@ -587,9 +589,9 @@ static s32 dme1737_write(const struct dme1737_data *data, u8 reg, u8 val)
                res = i2c_smbus_write_byte_data(client, reg, val);
 
                if (res < 0) {
-                       dev_warn(&client->dev, "Write to register "
-                                "0x%02x failed! Please report to the driver "
-                                "maintainer.\n", reg);
+                       dev_warn(&client->dev,
+                                "Write to register 0x%02x failed! %s\n",
+                                reg, DO_REPORT);
                }
        } else { /* ISA device */
                outb(reg, data->addr);
@@ -1167,8 +1169,8 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
                /* Only valid for fan[1-4] */
                if (!(val == 1 || val == 2 || val == 4)) {
                        count = -EINVAL;
-                       dev_warn(dev, "Fan type value %ld not "
-                                "supported. Choose one of 1, 2, or 4.\n",
+                       dev_warn(dev,
+                                "Fan type value %ld not supported. Choose one of 1, 2, or 4.\n",
                                 val);
                        goto exit;
                }
@@ -1294,8 +1296,8 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
                /* Only valid for pwm[1-3] */
                if (val < 0 || val > 2) {
                        count = -EINVAL;
-                       dev_warn(dev, "PWM enable %ld not "
-                                "supported. Choose one of 0, 1, or 2.\n",
+                       dev_warn(dev,
+                                "PWM enable %ld not supported. Choose one of 0, 1, or 2.\n",
                                 val);
                        goto exit;
                }
@@ -1399,8 +1401,8 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
                if (!(val == 1 || val == 2 || val == 4 ||
                      val == 6 || val == 7)) {
                        count = -EINVAL;
-                       dev_warn(dev, "PWM auto channels zone %ld "
-                                "not supported. Choose one of 1, 2, 4, 6, "
+                       dev_warn(dev,
+                                "PWM auto channels zone %ld not supported. Choose one of 1, 2, 4, 6, "
                                 "or 7.\n", val);
                        goto exit;
                }
@@ -2178,8 +2180,8 @@ static int dme1737_create_files(struct device *dev)
         * selected attributes from read-only to read-writeable.
         */
        if (data->config & 0x02) {
-               dev_info(dev, "Device is locked. Some attributes "
-                        "will be read-only.\n");
+               dev_info(dev,
+                        "Device is locked. Some attributes will be read-only.\n");
        } else {
                /* Change permissions of zone sysfs attributes */
                dme1737_chmod_group(dev, &dme1737_zone_chmod_group,
@@ -2247,9 +2249,8 @@ static int dme1737_init_device(struct device *dev)
        /* Inform if part is not monitoring/started */
        if (!(data->config & 0x01)) {
                if (!force_start) {
-                       dev_err(dev, "Device is not monitoring. "
-                               "Use the force_start load parameter to "
-                               "override.\n");
+                       dev_err(dev,
+                               "Device is not monitoring. Use the force_start load parameter to override.\n");
                        return -EFAULT;
                }
 
@@ -2289,8 +2290,8 @@ static int dme1737_init_device(struct device *dev)
                 */
                if (dme1737_i2c_get_features(0x2e, data) &&
                    dme1737_i2c_get_features(0x4e, data)) {
-                       dev_warn(dev, "Failed to query Super-IO for optional "
-                                "features.\n");
+                       dev_warn(dev,
+                                "Failed to query Super-IO for optional features.\n");
                }
        }
 
@@ -2317,8 +2318,8 @@ static int dme1737_init_device(struct device *dev)
                break;
        }
 
-       dev_info(dev, "Optional features: pwm3=%s, pwm5=%s, pwm6=%s, "
-                "fan3=%s, fan4=%s, fan5=%s, fan6=%s.\n",
+       dev_info(dev,
+                "Optional features: pwm3=%s, pwm5=%s, pwm6=%s, fan3=%s, fan4=%s, fan5=%s, fan6=%s.\n",
                 (data->has_features & HAS_PWM(2)) ? "yes" : "no",
                 (data->has_features & HAS_PWM(4)) ? "yes" : "no",
                 (data->has_features & HAS_PWM(5)) ? "yes" : "no",
@@ -2330,18 +2331,16 @@ static int dme1737_init_device(struct device *dev)
        reg = dme1737_read(data, DME1737_REG_TACH_PWM);
        /* Inform if fan-to-pwm mapping differs from the default */
        if (client && reg != 0xa4) {   /* I2C chip */
-               dev_warn(dev, "Non-standard fan to pwm mapping: "
-                        "fan1->pwm%d, fan2->pwm%d, fan3->pwm%d, "
-                        "fan4->pwm%d. Please report to the driver "
-                        "maintainer.\n",
+               dev_warn(dev,
+                        "Non-standard fan to pwm mapping: fan1->pwm%d, fan2->pwm%d, fan3->pwm%d, fan4->pwm%d. %s\n",
                         (reg & 0x03) + 1, ((reg >> 2) & 0x03) + 1,
-                        ((reg >> 4) & 0x03) + 1, ((reg >> 6) & 0x03) + 1);
+                        ((reg >> 4) & 0x03) + 1, ((reg >> 6) & 0x03) + 1,
+                        DO_REPORT);
        } else if (!client && reg != 0x24) {   /* ISA chip */
-               dev_warn(dev, "Non-standard fan to pwm mapping: "
-                        "fan1->pwm%d, fan2->pwm%d, fan3->pwm%d. "
-                        "Please report to the driver maintainer.\n",
+               dev_warn(dev,
+                        "Non-standard fan to pwm mapping: fan1->pwm%d, fan2->pwm%d, fan3->pwm%d. %s\n",
                         (reg & 0x03) + 1, ((reg >> 2) & 0x03) + 1,
-                        ((reg >> 4) & 0x03) + 1);
+                        ((reg >> 4) & 0x03) + 1, DO_REPORT);
        }
 
        /*
@@ -2355,8 +2354,9 @@ static int dme1737_init_device(struct device *dev)
                                                DME1737_REG_PWM_CONFIG(ix));
                        if ((data->has_features & HAS_PWM(ix)) &&
                            (PWM_EN_FROM_REG(data->pwm_config[ix]) == -1)) {
-                               dev_info(dev, "Switching pwm%d to "
-                                        "manual mode.\n", ix + 1);
+                               dev_info(dev,
+                                        "Switching pwm%d to manual mode.\n",
+                                        ix + 1);
                                data->pwm_config[ix] = PWM_EN_TO_REG(1,
                                                        data->pwm_config[ix]);
                                dme1737_write(data, DME1737_REG_PWM(ix), 0);
index a9816979c5dec92f4f0638c6b65f46fe6b801429..0c9f3da242bf91be9a6688f71de6d96385ce8453 100644 (file)
@@ -1350,8 +1350,7 @@ static void f71805f_init_device(struct f71805f_data *data)
 
        reg = f71805f_read8(data, F71805F_REG_START);
        if ((reg & 0x41) != 0x01) {
-               printk(KERN_DEBUG DRVNAME ": Starting monitoring "
-                      "operations\n");
+               pr_debug("Starting monitoring operations\n");
                f71805f_write8(data, F71805F_REG_START, (reg | 0x01) & ~0x40);
        }
 
index b757088aeddbaad84a1ae4a246e7c282f38f2c96..dff841085bafc9bb170c81309c3f75c8a0d92f4b 100644 (file)
@@ -189,8 +189,8 @@ static void fam15h_power_init_data(struct pci_dev *f4,
 
        /* result not allowed to be >= 256W */
        if ((tmp >> 16) >= 256)
-               dev_warn(&f4->dev, "Bogus value for ProcessorPwrWatts "
-                        "(processor_pwr_watts>=%u)\n",
+               dev_warn(&f4->dev,
+                        "Bogus value for ProcessorPwrWatts (processor_pwr_watts>=%u)\n",
                         (unsigned int) (tmp >> 16));
 
        /* convert to microWatt */
index 8af2755cdb871201fc56f6a0ab4ad149b21634c6..d58abdc5a4cff74e961f79a385d439b4f02cb09b 100644 (file)
@@ -463,8 +463,9 @@ static ssize_t store_fan_div(struct device *dev, struct device_attribute
                v = 3;
                break;
        default:
-               dev_err(dev, "fan_div value %lu not supported. "
-                       "Choose one of 2, 4 or 8!\n", v);
+               dev_err(dev,
+                       "fan_div value %lu not supported. Choose one of 2, 4 or 8!\n",
+                       v);
                return -EINVAL;
        }
 
@@ -1249,8 +1250,8 @@ static int fschmd_probe(struct i2c_client *client,
        }
        if (i == ARRAY_SIZE(watchdog_minors)) {
                data->watchdog_miscdev.minor = 0;
-               dev_warn(&client->dev, "Couldn't register watchdog chardev "
-                       "(due to no free minor)\n");
+               dev_warn(&client->dev,
+                        "Couldn't register watchdog chardev (due to no free minor)\n");
        }
        mutex_unlock(&watchdog_data_mutex);
 
index e2e5909a34df05fba91e7ac71f33905b9d0d91c0..95257a5621d8f4da50f3175de073fed6d84f1456 100644 (file)
@@ -344,8 +344,9 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
                val = 3;
                break;
        default:
-               dev_err(dev, "Invalid fan clock divider %lu, choose one "
-                       "of 1, 2, 4 or 8\n", val);
+               dev_err(dev,
+                       "Invalid fan clock divider %lu, choose one of 1, 2, 4 or 8\n",
+                       val);
                return -EINVAL;
        }
 
index 39781945a5d236152c3668f040ef670a25b75a6e..3104149795c582e8ceac8780aca390059eafc721 100644 (file)
@@ -105,10 +105,6 @@ static int fan_alarm_init(struct gpio_fan_data *fan_data,
        if (err)
                return err;
 
-       err = device_create_file(&pdev->dev, &dev_attr_fan1_alarm);
-       if (err)
-               return err;
-
        /*
         * If the alarm GPIO don't support interrupts, just leave
         * without initializing the fail notification support.
@@ -121,23 +117,9 @@ static int fan_alarm_init(struct gpio_fan_data *fan_data,
        irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH);
        err = devm_request_irq(&pdev->dev, alarm_irq, fan_alarm_irq_handler,
                               IRQF_SHARED, "GPIO fan alarm", fan_data);
-       if (err)
-               goto err_free_sysfs;
-
-       return 0;
-
-err_free_sysfs:
-       device_remove_file(&pdev->dev, &dev_attr_fan1_alarm);
        return err;
 }
 
-static void fan_alarm_free(struct gpio_fan_data *fan_data)
-{
-       struct platform_device *pdev = fan_data->pdev;
-
-       device_remove_file(&pdev->dev, &dev_attr_fan1_alarm);
-}
-
 /*
  * Control GPIOs.
  */
@@ -327,6 +309,12 @@ exit_unlock:
        return ret;
 }
 
+static ssize_t show_name(struct device *dev,
+                        struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "gpio-fan\n");
+}
+
 static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm);
 static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
                   show_pwm_enable, set_pwm_enable);
@@ -336,8 +324,26 @@ static DEVICE_ATTR(fan1_max, S_IRUGO, show_rpm_max, NULL);
 static DEVICE_ATTR(fan1_input, S_IRUGO, show_rpm, NULL);
 static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, show_rpm, set_rpm);
 
-static struct attribute *gpio_fan_ctrl_attributes[] = {
-       &dev_attr_pwm1.attr,
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static umode_t gpio_fan_is_visible(struct kobject *kobj,
+                                  struct attribute *attr, int index)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct gpio_fan_data *data = dev_get_drvdata(dev);
+
+       if (index == 1 && !data->alarm)
+               return 0;
+       if (index > 1 && !data->ctrl)
+               return 0;
+
+       return attr->mode;
+}
+
+static struct attribute *gpio_fan_attributes[] = {
+       &dev_attr_name.attr,
+       &dev_attr_fan1_alarm.attr,              /* 1 */
+       &dev_attr_pwm1.attr,                    /* 2 */
        &dev_attr_pwm1_enable.attr,
        &dev_attr_pwm1_mode.attr,
        &dev_attr_fan1_input.attr,
@@ -347,8 +353,9 @@ static struct attribute *gpio_fan_ctrl_attributes[] = {
        NULL
 };
 
-static const struct attribute_group gpio_fan_ctrl_group = {
-       .attrs = gpio_fan_ctrl_attributes,
+static const struct attribute_group gpio_fan_group = {
+       .attrs = gpio_fan_attributes,
+       .is_visible = gpio_fan_is_visible,
 };
 
 static int fan_ctrl_init(struct gpio_fan_data *fan_data,
@@ -379,30 +386,9 @@ static int fan_ctrl_init(struct gpio_fan_data *fan_data,
        if (fan_data->speed_index < 0)
                return -ENODEV;
 
-       err = sysfs_create_group(&pdev->dev.kobj, &gpio_fan_ctrl_group);
-       return err;
-}
-
-static void fan_ctrl_free(struct gpio_fan_data *fan_data)
-{
-       struct platform_device *pdev = fan_data->pdev;
-
-       sysfs_remove_group(&pdev->dev.kobj, &gpio_fan_ctrl_group);
-}
-
-/*
- * Platform driver.
- */
-
-static ssize_t show_name(struct device *dev,
-                        struct device_attribute *attr, char *buf)
-{
-       return sprintf(buf, "gpio-fan\n");
+       return 0;
 }
 
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
-
-
 #ifdef CONFIG_OF_GPIO
 /*
  * Translate OpenFirmware node properties into platform_data
@@ -546,38 +532,30 @@ static int gpio_fan_probe(struct platform_device *pdev)
 
        /* Configure control GPIOs if available. */
        if (pdata->ctrl && pdata->num_ctrl > 0) {
-               if (!pdata->speed || pdata->num_speed <= 1) {
-                       err = -EINVAL;
-                       goto err_free_alarm;
-               }
+               if (!pdata->speed || pdata->num_speed <= 1)
+                       return -EINVAL;
                err = fan_ctrl_init(fan_data, pdata);
                if (err)
-                       goto err_free_alarm;
+                       return err;
        }
 
-       err = device_create_file(&pdev->dev, &dev_attr_name);
+       err = sysfs_create_group(&pdev->dev.kobj, &gpio_fan_group);
        if (err)
-               goto err_free_ctrl;
+               return err;
 
        /* Make this driver part of hwmon class. */
        fan_data->hwmon_dev = hwmon_device_register(&pdev->dev);
        if (IS_ERR(fan_data->hwmon_dev)) {
                err = PTR_ERR(fan_data->hwmon_dev);
-               goto err_remove_name;
+               goto err_remove;
        }
 
        dev_info(&pdev->dev, "GPIO fan initialized\n");
 
        return 0;
 
-err_remove_name:
-       device_remove_file(&pdev->dev, &dev_attr_name);
-err_free_ctrl:
-       if (fan_data->ctrl)
-               fan_ctrl_free(fan_data);
-err_free_alarm:
-       if (fan_data->alarm)
-               fan_alarm_free(fan_data);
+err_remove:
+       sysfs_remove_group(&pdev->dev.kobj, &gpio_fan_group);
        return err;
 }
 
@@ -586,11 +564,7 @@ static int gpio_fan_remove(struct platform_device *pdev)
        struct gpio_fan_data *fan_data = platform_get_drvdata(pdev);
 
        hwmon_device_unregister(fan_data->hwmon_dev);
-       device_remove_file(&pdev->dev, &dev_attr_name);
-       if (fan_data->alarm)
-               fan_alarm_free(fan_data);
-       if (fan_data->ctrl)
-               fan_ctrl_free(fan_data);
+       sysfs_remove_group(&pdev->dev.kobj, &gpio_fan_group);
 
        return 0;
 }
@@ -619,7 +593,7 @@ static int gpio_fan_resume(struct device *dev)
 }
 
 static SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume);
-#define GPIO_FAN_PM    &gpio_fan_pm
+#define GPIO_FAN_PM    (&gpio_fan_pm)
 #else
 #define GPIO_FAN_PM    NULL
 #endif
index a14f634248e72c9c7574bcc6593ac835d66eb19a..1429f6e177f4c4b5ed4bfcce3bbaa57322345818 100644 (file)
@@ -289,8 +289,9 @@ static int aem_init_ipmi_data(struct aem_ipmi_data *data, int iface,
        err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
                               data, &data->user);
        if (err < 0) {
-               dev_err(bmc, "Unable to register user with IPMI "
-                       "interface %d\n", data->interface);
+               dev_err(bmc,
+                       "Unable to register user with IPMI interface %d\n",
+                       data->interface);
                return -EACCES;
        }
 
@@ -328,8 +329,8 @@ static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
        struct aem_ipmi_data *data = user_msg_data;
 
        if (msg->msgid != data->tx_msgid) {
-               dev_err(data->bmc_device, "Mismatch between received msgid "
-                       "(%02x) and transmitted msgid (%02x)!\n",
+               dev_err(data->bmc_device,
+                       "Mismatch between received msgid (%02x) and transmitted msgid (%02x)!\n",
                        (int)msg->msgid,
                        (int)data->tx_msgid);
                ipmi_free_recv_msg(msg);
@@ -575,8 +576,8 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle)
        /* Register with hwmon */
        data->hwmon_dev = hwmon_device_register(&data->pdev->dev);
        if (IS_ERR(data->hwmon_dev)) {
-               dev_err(&data->pdev->dev, "Unable to register hwmon "
-                       "device for IPMI interface %d\n",
+               dev_err(&data->pdev->dev,
+                       "Unable to register hwmon device for IPMI interface %d\n",
                        probe->interface);
                res = PTR_ERR(data->hwmon_dev);
                goto hwmon_reg_err;
@@ -715,8 +716,8 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe,
        /* Register with hwmon */
        data->hwmon_dev = hwmon_device_register(&data->pdev->dev);
        if (IS_ERR(data->hwmon_dev)) {
-               dev_err(&data->pdev->dev, "Unable to register hwmon "
-                       "device for IPMI interface %d\n",
+               dev_err(&data->pdev->dev,
+                       "Unable to register hwmon device for IPMI interface %d\n",
                        probe->interface);
                res = PTR_ERR(data->hwmon_dev);
                goto hwmon_reg_err;
@@ -768,8 +769,8 @@ static void aem_init_aem2(struct aem_ipmi_data *probe)
 
        while (!aem_find_aem2(probe, &fi_resp, i)) {
                if (fi_resp.major != 2) {
-                       dev_err(probe->bmc_device, "Unknown AEM v%d; please "
-                               "report this to the maintainer.\n",
+                       dev_err(probe->bmc_device,
+                               "Unknown AEM v%d; please report this to the maintainer.\n",
                                fi_resp.major);
                        i++;
                        continue;
index b622a93ec32cea4622d72037ee41cfd3612d2b38..74b365ea01c727d7bfecf865e1919482d0a83ecb 100644 (file)
@@ -163,8 +163,8 @@ static int ibmpex_ver_check(struct ibmpex_bmc_data *data)
        data->sensor_major = data->rx_msg_data[0];
        data->sensor_minor = data->rx_msg_data[1];
 
-       dev_info(data->bmc_device, "Found BMC with sensor interface "
-                "v%d.%d %d-%02d-%02d on interface %d\n",
+       dev_info(data->bmc_device,
+                "Found BMC with sensor interface v%d.%d %d-%02d-%02d on interface %d\n",
                 data->sensor_major,
                 data->sensor_minor,
                 extract_value(data->rx_msg_data, 2),
@@ -478,8 +478,9 @@ static void ibmpex_register_bmc(int iface, struct device *dev)
        err = ipmi_create_user(data->interface, &driver_data.ipmi_hndlrs,
                               data, &data->user);
        if (err < 0) {
-               dev_err(dev, "Unable to register user with IPMI "
-                       "interface %d\n", data->interface);
+               dev_err(dev,
+                       "Unable to register user with IPMI interface %d\n",
+                       data->interface);
                goto out;
        }
 
@@ -501,8 +502,8 @@ static void ibmpex_register_bmc(int iface, struct device *dev)
        data->hwmon_dev = hwmon_device_register(data->bmc_device);
 
        if (IS_ERR(data->hwmon_dev)) {
-               dev_err(data->bmc_device, "Unable to register hwmon "
-                       "device for IPMI interface %d\n",
+               dev_err(data->bmc_device,
+                       "Unable to register hwmon device for IPMI interface %d\n",
                        data->interface);
                goto out_user;
        }
@@ -567,8 +568,8 @@ static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
        struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data;
 
        if (msg->msgid != data->tx_msgid) {
-               dev_err(data->bmc_device, "Mismatch between received msgid "
-                       "(%02x) and transmitted msgid (%02x)!\n",
+               dev_err(data->bmc_device,
+                       "Mismatch between received msgid (%02x) and transmitted msgid (%02x)!\n",
                        (int)msg->msgid,
                        (int)data->tx_msgid);
                ipmi_free_recv_msg(msg);
index 8e7158c3ad2fa37bb76bf6a484666a7c9cb7f0dd..4958b2f89dcefff53354e18c6c37f6f356f6eeb2 100644 (file)
@@ -186,20 +186,20 @@ static ssize_t ina2xx_show_value(struct device *dev,
 }
 
 /* shunt voltage */
-static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, \
-       ina2xx_show_value, NULL, INA2XX_SHUNT_VOLTAGE);
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ina2xx_show_value, NULL,
+                         INA2XX_SHUNT_VOLTAGE);
 
 /* bus voltage */
-static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, \
-       ina2xx_show_value, NULL, INA2XX_BUS_VOLTAGE);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ina2xx_show_value, NULL,
+                         INA2XX_BUS_VOLTAGE);
 
 /* calculated current */
-static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, \
-       ina2xx_show_value, NULL, INA2XX_CURRENT);
+static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ina2xx_show_value, NULL,
+                         INA2XX_CURRENT);
 
 /* calculated power */
-static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, \
-       ina2xx_show_value, NULL, INA2XX_POWER);
+static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ina2xx_show_value, NULL,
+                         INA2XX_POWER);
 
 /* pointers to created device attributes */
 static struct attribute *ina2xx_attributes[] = {
index 37fc980fde240d7a7dd023cf27fc12aa8577a3f8..72b21d5b1c621ece4f8059ba48ae6afe26642c21 100644 (file)
@@ -1778,7 +1778,7 @@ static int __init it87_find(unsigned short *address,
                superio_select(5);
                sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
        } else if (sio_data->type == it8783) {
-               int reg25, reg27, reg2A, reg2C, regEF;
+               int reg25, reg27, reg2a, reg2c, regef;
 
                sio_data->skip_vid = 1; /* No VID */
 
@@ -1786,15 +1786,15 @@ static int __init it87_find(unsigned short *address,
 
                reg25 = superio_inb(IT87_SIO_GPIO1_REG);
                reg27 = superio_inb(IT87_SIO_GPIO3_REG);
-               reg2A = superio_inb(IT87_SIO_PINX1_REG);
-               reg2C = superio_inb(IT87_SIO_PINX2_REG);
-               regEF = superio_inb(IT87_SIO_SPI_REG);
+               reg2a = superio_inb(IT87_SIO_PINX1_REG);
+               reg2c = superio_inb(IT87_SIO_PINX2_REG);
+               regef = superio_inb(IT87_SIO_SPI_REG);
 
                /* Check if fan3 is there or not */
-               if ((reg27 & (1 << 0)) || !(reg2C & (1 << 2)))
+               if ((reg27 & (1 << 0)) || !(reg2c & (1 << 2)))
                        sio_data->skip_fan |= (1 << 2);
                if ((reg25 & (1 << 4))
-                   || (!(reg2A & (1 << 1)) && (regEF & (1 << 0))))
+                   || (!(reg2a & (1 << 1)) && (regef & (1 << 0))))
                        sio_data->skip_pwm |= (1 << 2);
 
                /* Check if fan2 is there or not */
@@ -1804,7 +1804,7 @@ static int __init it87_find(unsigned short *address,
                        sio_data->skip_pwm |= (1 << 1);
 
                /* VIN5 */
-               if ((reg27 & (1 << 0)) || (reg2C & (1 << 2)))
+               if ((reg27 & (1 << 0)) || (reg2c & (1 << 2)))
                        sio_data->skip_in |= (1 << 5); /* No VIN5 */
 
                /* VIN6 */
@@ -1829,18 +1829,18 @@ static int __init it87_find(unsigned short *address,
                         * not the case, and ask the user to report if the
                         * resulting voltage is sane.
                         */
-                       if (!(reg2C & (1 << 1))) {
-                               reg2C |= (1 << 1);
-                               superio_outb(IT87_SIO_PINX2_REG, reg2C);
+                       if (!(reg2c & (1 << 1))) {
+                               reg2c |= (1 << 1);
+                               superio_outb(IT87_SIO_PINX2_REG, reg2c);
                                pr_notice("Routing internal VCCH5V to in7.\n");
                        }
                        pr_notice("in7 routed to internal voltage divider, with external pin disabled.\n");
                        pr_notice("Please report if it displays a reasonable voltage.\n");
                }
 
-               if (reg2C & (1 << 0))
+               if (reg2c & (1 << 0))
                        sio_data->internal |= (1 << 0);
-               if (reg2C & (1 << 1))
+               if (reg2c & (1 << 1))
                        sio_data->internal |= (1 << 1);
 
                sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
index 9f3c0aeacdb97bb45c927b880ed5bcae36e6ed0f..5b50e9e4f96bb2ebf7e6630c846d591afca36248 100644 (file)
@@ -200,8 +200,8 @@ static int k8temp_probe(struct pci_dev *pdev,
         */
        if (model >= 0x40) {
                data->swap_core_select = 1;
-               dev_warn(&pdev->dev, "Temperature readouts might be wrong - "
-                        "check erratum #141\n");
+               dev_warn(&pdev->dev,
+                        "Temperature readouts might be wrong - check erratum #141\n");
        }
 
        /*
index 483538fa1bd58644b68607039be7ef8ca0cb8ed5..6cf6bff790033756a70d1d57ebe9f8f1096899f8 100644 (file)
@@ -386,8 +386,9 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *da,
                data->fan_div[nr] = 3;
                break;
        default:
-               dev_err(dev, "fan_div value %ld not "
-                       "supported. Choose one of 1, 2, 4 or 8!\n", val);
+               dev_err(dev,
+                       "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n",
+                       val);
                mutex_unlock(&data->update_lock);
                return -EINVAL;
        }
@@ -636,8 +637,9 @@ static int lm78_i2c_detect(struct i2c_client *client,
                goto err_nodev;
 
        if (lm78_alias_detect(client, i)) {
-               dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
-                       "be the same as ISA device\n", address);
+               dev_dbg(&adapter->dev,
+                       "Device at 0x%02x appears to be the same as ISA device\n",
+                       address);
                goto err_nodev;
        }
 
index 357fbb9987284cf87fa3faf9bf05235a5427e5a1..eba89aac3ecefbce3783329a546e5a66682bda94 100644 (file)
@@ -286,8 +286,9 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
                data->fan_div[nr] = 3;
                break;
        default:
-               dev_err(&client->dev, "fan_div value %ld not "
-                       "supported. Choose one of 1, 2, 4 or 8!\n", val);
+               dev_err(&client->dev,
+                       "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n",
+                       val);
                mutex_unlock(&data->update_lock);
                return -EINVAL;
        }
index 47ade8ba152d5c0484e3927f4a1f416e20abaf64..3894c408fda3cedc1230742cc5c0a9a653227cd5 100644 (file)
@@ -1293,8 +1293,8 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info)
        company = lm85_read_value(client, LM85_REG_COMPANY);
        verstep = lm85_read_value(client, LM85_REG_VERSTEP);
 
-       dev_dbg(&adapter->dev, "Detecting device at 0x%02x with "
-               "COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
+       dev_dbg(&adapter->dev,
+               "Detecting device at 0x%02x with COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
                address, company, verstep);
 
        /* All supported chips have the version in common */
index b40f34cdb3caf34d9a72d3c7e90c334740c5c229..a6f46058b1be3994d03cc04217f4789a4e1a7633 100644 (file)
@@ -354,12 +354,12 @@ static const unsigned long lm93_vin_val_max[16] = {
 
 static unsigned LM93_IN_FROM_REG(int nr, u8 reg)
 {
-       const long uV_max = lm93_vin_val_max[nr] * 1000;
-       const long uV_min = lm93_vin_val_min[nr] * 1000;
+       const long uv_max = lm93_vin_val_max[nr] * 1000;
+       const long uv_min = lm93_vin_val_min[nr] * 1000;
 
-       const long slope = (uV_max - uV_min) /
+       const long slope = (uv_max - uv_min) /
                (lm93_vin_reg_max[nr] - lm93_vin_reg_min[nr]);
-       const long intercept = uV_min - slope * lm93_vin_reg_min[nr];
+       const long intercept = uv_min - slope * lm93_vin_reg_min[nr];
 
        return (slope * reg + intercept + 500) / 1000;
 }
@@ -371,20 +371,20 @@ static unsigned LM93_IN_FROM_REG(int nr, u8 reg)
 static u8 LM93_IN_TO_REG(int nr, unsigned val)
 {
        /* range limit */
-       const long mV = clamp_val(val,
+       const long mv = clamp_val(val,
                                  lm93_vin_val_min[nr], lm93_vin_val_max[nr]);
 
        /* try not to lose too much precision here */
-       const long uV = mV * 1000;
-       const long uV_max = lm93_vin_val_max[nr] * 1000;
-       const long uV_min = lm93_vin_val_min[nr] * 1000;
+       const long uv = mv * 1000;
+       const long uv_max = lm93_vin_val_max[nr] * 1000;
+       const long uv_min = lm93_vin_val_min[nr] * 1000;
 
        /* convert */
-       const long slope = (uV_max - uV_min) /
+       const long slope = (uv_max - uv_min) /
                (lm93_vin_reg_max[nr] - lm93_vin_reg_min[nr]);
-       const long intercept = uV_min - slope * lm93_vin_reg_min[nr];
+       const long intercept = uv_min - slope * lm93_vin_reg_min[nr];
 
-       u8 result = ((uV - intercept + (slope/2)) / slope);
+       u8 result = ((uv - intercept + (slope/2)) / slope);
        result = clamp_val(result,
                           lm93_vin_reg_min[nr], lm93_vin_reg_max[nr]);
        return result;
@@ -393,10 +393,10 @@ static u8 LM93_IN_TO_REG(int nr, unsigned val)
 /* vid in mV, upper == 0 indicates low limit, otherwise upper limit */
 static unsigned LM93_IN_REL_FROM_REG(u8 reg, int upper, int vid)
 {
-       const long uV_offset = upper ? (((reg >> 4 & 0x0f) + 1) * 12500) :
+       const long uv_offset = upper ? (((reg >> 4 & 0x0f) + 1) * 12500) :
                                (((reg >> 0 & 0x0f) + 1) * -25000);
-       const long uV_vid = vid * 1000;
-       return (uV_vid + uV_offset + 5000) / 10000;
+       const long uv_vid = vid * 1000;
+       return (uv_vid + uv_offset + 5000) / 10000;
 }
 
 #define LM93_IN_MIN_FROM_REG(reg, vid) LM93_IN_REL_FROM_REG((reg), 0, (vid))
@@ -409,13 +409,13 @@ static unsigned LM93_IN_REL_FROM_REG(u8 reg, int upper, int vid)
  */
 static u8 LM93_IN_REL_TO_REG(unsigned val, int upper, int vid)
 {
-       long uV_offset = vid * 1000 - val * 10000;
+       long uv_offset = vid * 1000 - val * 10000;
        if (upper) {
-               uV_offset = clamp_val(uV_offset, 12500, 200000);
-               return (u8)((uV_offset /  12500 - 1) << 4);
+               uv_offset = clamp_val(uv_offset, 12500, 200000);
+               return (u8)((uv_offset /  12500 - 1) << 4);
        } else {
-               uV_offset = clamp_val(uV_offset, -400000, -25000);
-               return (u8)((uV_offset / -25000 - 1) << 0);
+               uv_offset = clamp_val(uv_offset, -400000, -25000);
+               return (u8)((uv_offset / -25000 - 1) << 0);
        }
 }
 
@@ -818,8 +818,9 @@ static u8 lm93_read_byte(struct i2c_client *client, u8 reg)
                if (value >= 0) {
                        return value;
                } else {
-                       dev_warn(&client->dev, "lm93: read byte data failed, "
-                               "address 0x%02x.\n", reg);
+                       dev_warn(&client->dev,
+                                "lm93: read byte data failed, address 0x%02x.\n",
+                                reg);
                        mdelay(i + 3);
                }
 
@@ -838,8 +839,9 @@ static int lm93_write_byte(struct i2c_client *client, u8 reg, u8 value)
        result = i2c_smbus_write_byte_data(client, reg, value);
 
        if (result < 0)
-               dev_warn(&client->dev, "lm93: write byte data failed, "
-                        "0x%02x at address 0x%02x.\n", value, reg);
+               dev_warn(&client->dev,
+                        "lm93: write byte data failed, 0x%02x at address 0x%02x.\n",
+                        value, reg);
 
        return result;
 }
@@ -854,8 +856,9 @@ static u16 lm93_read_word(struct i2c_client *client, u8 reg)
                if (value >= 0) {
                        return value;
                } else {
-                       dev_warn(&client->dev, "lm93: read word data failed, "
-                                "address 0x%02x.\n", reg);
+                       dev_warn(&client->dev,
+                                "lm93: read word data failed, address 0x%02x.\n",
+                                reg);
                        mdelay(i + 3);
                }
 
@@ -874,8 +877,9 @@ static int lm93_write_word(struct i2c_client *client, u8 reg, u16 value)
        result = i2c_smbus_write_word_data(client, reg, value);
 
        if (result < 0)
-               dev_warn(&client->dev, "lm93: write word data failed, "
-                        "0x%04x at address 0x%02x.\n", value, reg);
+               dev_warn(&client->dev,
+                        "lm93: write word data failed, 0x%04x at address 0x%02x.\n",
+                        value, reg);
 
        return result;
 }
@@ -898,8 +902,8 @@ static void lm93_read_block(struct i2c_client *client, u8 fbn, u8 *values)
                if (result == lm93_block_read_cmds[fbn].len) {
                        break;
                } else {
-                       dev_warn(&client->dev, "lm93: block read data failed, "
-                                "command 0x%02x.\n",
+                       dev_warn(&client->dev,
+                                "lm93: block read data failed, command 0x%02x.\n",
                                 lm93_block_read_cmds[fbn].cmd);
                        mdelay(i + 3);
                }
@@ -2672,8 +2676,8 @@ static void lm93_init_client(struct i2c_client *client)
                        return;
        }
 
-       dev_warn(&client->dev, "timed out waiting for sensor "
-                "chip to signal ready!\n");
+       dev_warn(&client->dev,
+                "timed out waiting for sensor chip to signal ready!\n");
 }
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
@@ -2733,12 +2737,12 @@ static int lm93_probe(struct i2c_client *client,
                dev_dbg(&client->dev, "using SMBus block data transactions\n");
                update = lm93_update_client_full;
        } else if ((LM93_SMBUS_FUNC_MIN & func) == LM93_SMBUS_FUNC_MIN) {
-               dev_dbg(&client->dev, "disabled SMBus block data "
-                       "transactions\n");
+               dev_dbg(&client->dev,
+                       "disabled SMBus block data transactions\n");
                update = lm93_update_client_min;
        } else {
-               dev_dbg(&client->dev, "detect failed, "
-                       "smbus byte and/or word data not supported!\n");
+               dev_dbg(&client->dev,
+                       "detect failed, smbus byte and/or word data not supported!\n");
                return -ENODEV;
        }
 
diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c
new file mode 100644 (file)
index 0000000..307c9ea
--- /dev/null
@@ -0,0 +1,769 @@
+/*
+ * Driver for Texas Instruments / National Semiconductor LM95234
+ *
+ * Copyright (c) 2013 Guenter Roeck <linux@roeck-us.net>
+ *
+ * Derived from lm95241.c
+ * Copyright (C) 2008, 2010 Davide Rizzo <elpa.rizzo@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+
+#define DRVNAME "lm95234"
+
+static const unsigned short normal_i2c[] = { 0x18, 0x4d, 0x4e, I2C_CLIENT_END };
+
+/* LM95234 registers */
+#define LM95234_REG_MAN_ID             0xFE
+#define LM95234_REG_CHIP_ID            0xFF
+#define LM95234_REG_STATUS             0x02
+#define LM95234_REG_CONFIG             0x03
+#define LM95234_REG_CONVRATE           0x04
+#define LM95234_REG_STS_FAULT          0x07
+#define LM95234_REG_STS_TCRIT1         0x08
+#define LM95234_REG_STS_TCRIT2         0x09
+#define LM95234_REG_TEMPH(x)           ((x) + 0x10)
+#define LM95234_REG_TEMPL(x)           ((x) + 0x20)
+#define LM95234_REG_UTEMPH(x)          ((x) + 0x19)    /* Remote only */
+#define LM95234_REG_UTEMPL(x)          ((x) + 0x29)
+#define LM95234_REG_REM_MODEL          0x30
+#define LM95234_REG_REM_MODEL_STS      0x38
+#define LM95234_REG_OFFSET(x)          ((x) + 0x31)    /* Remote only */
+#define LM95234_REG_TCRIT1(x)          ((x) + 0x40)
+#define LM95234_REG_TCRIT2(x)          ((x) + 0x49)    /* Remote channel 1,2 */
+#define LM95234_REG_TCRIT_HYST         0x5a
+
+#define NATSEMI_MAN_ID                 0x01
+#define LM95234_CHIP_ID                        0x79
+
+/* Client data (each client gets its own) */
+struct lm95234_data {
+       struct device *hwmon_dev;
+       struct mutex update_lock;
+       unsigned long last_updated, interval;   /* in jiffies */
+       bool valid;             /* false until following fields are valid */
+       /* registers values */
+       int temp[5];            /* temperature (signed) */
+       u32 status;             /* fault/alarm status */
+       u8 tcrit1[5];           /* critical temperature limit */
+       u8 tcrit2[2];           /* high temperature limit */
+       s8 toffset[4];          /* remote temperature offset */
+       u8 thyst;               /* common hysteresis */
+
+       u8 sensor_type;         /* temperature sensor type */
+};
+
+static int lm95234_read_temp(struct i2c_client *client, int index, int *t)
+{
+       int val;
+       u16 temp = 0;
+
+       if (index) {
+               val = i2c_smbus_read_byte_data(client,
+                                              LM95234_REG_UTEMPH(index - 1));
+               if (val < 0)
+                       return val;
+               temp = val << 8;
+               val = i2c_smbus_read_byte_data(client,
+                                              LM95234_REG_UTEMPL(index - 1));
+               if (val < 0)
+                       return val;
+               temp |= val;
+               *t = temp;
+       }
+       /*
+        * Read signed temperature if unsigned temperature is 0,
+        * or if this is the local sensor.
+        */
+       if (!temp) {
+               val = i2c_smbus_read_byte_data(client,
+                                              LM95234_REG_TEMPH(index));
+               if (val < 0)
+                       return val;
+               temp = val << 8;
+               val = i2c_smbus_read_byte_data(client,
+                                              LM95234_REG_TEMPL(index));
+               if (val < 0)
+                       return val;
+               temp |= val;
+               *t = (s16)temp;
+       }
+       return 0;
+}
+
+static u16 update_intervals[] = { 143, 364, 1000, 2500 };
+
+/* Fill value cache. Must be called with update lock held. */
+
+static int lm95234_fill_cache(struct i2c_client *client)
+{
+       struct lm95234_data *data = i2c_get_clientdata(client);
+       int i, ret;
+
+       ret = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE);
+       if (ret < 0)
+               return ret;
+
+       data->interval = msecs_to_jiffies(update_intervals[ret & 0x03]);
+
+       for (i = 0; i < ARRAY_SIZE(data->tcrit1); i++) {
+               ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT1(i));
+               if (ret < 0)
+                       return ret;
+               data->tcrit1[i] = ret;
+       }
+       for (i = 0; i < ARRAY_SIZE(data->tcrit2); i++) {
+               ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT2(i));
+               if (ret < 0)
+                       return ret;
+               data->tcrit2[i] = ret;
+       }
+       for (i = 0; i < ARRAY_SIZE(data->toffset); i++) {
+               ret = i2c_smbus_read_byte_data(client, LM95234_REG_OFFSET(i));
+               if (ret < 0)
+                       return ret;
+               data->toffset[i] = ret;
+       }
+
+       ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT_HYST);
+       if (ret < 0)
+               return ret;
+       data->thyst = ret;
+
+       ret = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL);
+       if (ret < 0)
+               return ret;
+       data->sensor_type = ret;
+
+       return 0;
+}
+
+static int lm95234_update_device(struct i2c_client *client,
+                                struct lm95234_data *data)
+{
+       int ret;
+
+       mutex_lock(&data->update_lock);
+
+       if (time_after(jiffies, data->last_updated + data->interval) ||
+           !data->valid) {
+               int i;
+
+               if (!data->valid) {
+                       ret = lm95234_fill_cache(client);
+                       if (ret < 0)
+                               goto abort;
+               }
+
+               data->valid = false;
+               for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
+                       ret = lm95234_read_temp(client, i, &data->temp[i]);
+                       if (ret < 0)
+                               goto abort;
+               }
+
+               ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_FAULT);
+               if (ret < 0)
+                       goto abort;
+               data->status = ret;
+
+               ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_TCRIT1);
+               if (ret < 0)
+                       goto abort;
+               data->status |= ret << 8;
+
+               ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_TCRIT2);
+               if (ret < 0)
+                       goto abort;
+               data->status |= ret << 16;
+
+               data->last_updated = jiffies;
+               data->valid = true;
+       }
+       ret = 0;
+abort:
+       mutex_unlock(&data->update_lock);
+
+       return ret;
+}
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95234_data *data = i2c_get_clientdata(client);
+       int index = to_sensor_dev_attr(attr)->index;
+       int ret = lm95234_update_device(client, data);
+
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "%d\n",
+                      DIV_ROUND_CLOSEST(data->temp[index] * 125, 32));
+}
+
+static ssize_t show_alarm(struct device *dev,
+                         struct device_attribute *attr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95234_data *data = i2c_get_clientdata(client);
+       u32 mask = to_sensor_dev_attr(attr)->index;
+       int ret = lm95234_update_device(client, data);
+
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "%u", !!(data->status & mask));
+}
+
+static ssize_t show_type(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95234_data *data = i2c_get_clientdata(client);
+       u8 mask = to_sensor_dev_attr(attr)->index;
+       int ret = lm95234_update_device(client, data);
+
+       if (ret)
+               return ret;
+
+       return sprintf(buf, data->sensor_type & mask ? "1\n" : "2\n");
+}
+
+static ssize_t set_type(struct device *dev, struct device_attribute *attr,
+                       const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95234_data *data = i2c_get_clientdata(client);
+       unsigned long val;
+       u8 mask = to_sensor_dev_attr(attr)->index;
+       int ret = lm95234_update_device(client, data);
+
+       if (ret)
+               return ret;
+
+       ret = kstrtoul(buf, 10, &val);
+       if (ret < 0)
+               return ret;
+
+       if (val != 1 && val != 2)
+               return -EINVAL;
+
+       mutex_lock(&data->update_lock);
+       if (val == 1)
+               data->sensor_type |= mask;
+       else
+               data->sensor_type &= ~mask;
+       data->valid = false;
+       i2c_smbus_write_byte_data(client, LM95234_REG_REM_MODEL,
+                                 data->sensor_type);
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t show_tcrit2(struct device *dev, struct device_attribute *attr,
+                          char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95234_data *data = i2c_get_clientdata(client);
+       int index = to_sensor_dev_attr(attr)->index;
+       int ret = lm95234_update_device(client, data);
+
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "%u", data->tcrit2[index] * 1000);
+}
+
+static ssize_t set_tcrit2(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95234_data *data = i2c_get_clientdata(client);
+       int index = to_sensor_dev_attr(attr)->index;
+       long val;
+       int ret = lm95234_update_device(client, data);
+
+       if (ret)
+               return ret;
+
+       ret = kstrtol(buf, 10, &val);
+       if (ret < 0)
+               return ret;
+
+       val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, index ? 255 : 127);
+
+       mutex_lock(&data->update_lock);
+       data->tcrit2[index] = val;
+       i2c_smbus_write_byte_data(client, LM95234_REG_TCRIT2(index), val);
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t show_tcrit2_hyst(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95234_data *data = i2c_get_clientdata(client);
+       int index = to_sensor_dev_attr(attr)->index;
+       int ret = lm95234_update_device(client, data);
+
+       if (ret)
+               return ret;
+
+       /* Result can be negative, so be careful with unsigned operands */
+       return sprintf(buf, "%d",
+                      ((int)data->tcrit2[index] - (int)data->thyst) * 1000);
+}
+
+static ssize_t show_tcrit1(struct device *dev, struct device_attribute *attr,
+                          char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95234_data *data = i2c_get_clientdata(client);
+       int index = to_sensor_dev_attr(attr)->index;
+
+       return sprintf(buf, "%u", data->tcrit1[index] * 1000);
+}
+
+static ssize_t set_tcrit1(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95234_data *data = i2c_get_clientdata(client);
+       int index = to_sensor_dev_attr(attr)->index;
+       long val;
+       int ret = lm95234_update_device(client, data);
+
+       if (ret)
+               return ret;
+
+       ret = kstrtol(buf, 10, &val);
+       if (ret < 0)
+               return ret;
+
+       val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 255);
+
+       mutex_lock(&data->update_lock);
+       data->tcrit1[index] = val;
+       i2c_smbus_write_byte_data(client, LM95234_REG_TCRIT1(index), val);
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t show_tcrit1_hyst(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95234_data *data = i2c_get_clientdata(client);
+       int index = to_sensor_dev_attr(attr)->index;
+       int ret = lm95234_update_device(client, data);
+
+       if (ret)
+               return ret;
+
+       /* Result can be negative, so be careful with unsigned operands */
+       return sprintf(buf, "%d",
+                      ((int)data->tcrit1[index] - (int)data->thyst) * 1000);
+}
+
+static ssize_t set_tcrit1_hyst(struct device *dev,
+                              struct device_attribute *attr,
+                              const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95234_data *data = i2c_get_clientdata(client);
+       int index = to_sensor_dev_attr(attr)->index;
+       long val;
+       int ret = lm95234_update_device(client, data);
+
+       if (ret)
+               return ret;
+
+       ret = kstrtol(buf, 10, &val);
+       if (ret < 0)
+               return ret;
+
+       val = DIV_ROUND_CLOSEST(val, 1000);
+       val = clamp_val((int)data->tcrit1[index] - val, 0, 31);
+
+       mutex_lock(&data->update_lock);
+       data->thyst = val;
+       i2c_smbus_write_byte_data(client, LM95234_REG_TCRIT_HYST, val);
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t show_offset(struct device *dev, struct device_attribute *attr,
+                          char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95234_data *data = i2c_get_clientdata(client);
+       int index = to_sensor_dev_attr(attr)->index;
+       int ret = lm95234_update_device(client, data);
+
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "%d", data->toffset[index] * 500);
+}
+
+static ssize_t set_offset(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95234_data *data = i2c_get_clientdata(client);
+       int index = to_sensor_dev_attr(attr)->index;
+       long val;
+       int ret = lm95234_update_device(client, data);
+
+       if (ret)
+               return ret;
+
+       ret = kstrtol(buf, 10, &val);
+       if (ret < 0)
+               return ret;
+
+       /* Accuracy is 1/2 degrees C */
+       val = clamp_val(DIV_ROUND_CLOSEST(val, 500), -128, 127);
+
+       mutex_lock(&data->update_lock);
+       data->toffset[index] = val;
+       i2c_smbus_write_byte_data(client, LM95234_REG_OFFSET(index), val);
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t show_interval(struct device *dev, struct device_attribute *attr,
+                            char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95234_data *data = i2c_get_clientdata(client);
+       int ret = lm95234_update_device(client, data);
+
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "%lu\n",
+                      DIV_ROUND_CLOSEST(data->interval * 1000, HZ));
+}
+
+static ssize_t set_interval(struct device *dev, struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95234_data *data = i2c_get_clientdata(client);
+       unsigned long val;
+       u8 regval;
+       int ret = lm95234_update_device(client, data);
+
+       if (ret)
+               return ret;
+
+       ret = kstrtoul(buf, 10, &val);
+       if (ret < 0)
+               return ret;
+
+       for (regval = 0; regval < 3; regval++) {
+               if (val <= update_intervals[regval])
+                       break;
+       }
+
+       mutex_lock(&data->update_lock);
+       data->interval = msecs_to_jiffies(update_intervals[regval]);
+       i2c_smbus_write_byte_data(client, LM95234_REG_CONVRATE, regval);
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4);
+
+static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL,
+                         BIT(0) | BIT(1));
+static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL,
+                         BIT(2) | BIT(3));
+static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_alarm, NULL,
+                         BIT(4) | BIT(5));
+static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_alarm, NULL,
+                         BIT(6) | BIT(7));
+
+static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, set_type,
+                         BIT(1));
+static SENSOR_DEVICE_ATTR(temp3_type, S_IWUSR | S_IRUGO, show_type, set_type,
+                         BIT(2));
+static SENSOR_DEVICE_ATTR(temp4_type, S_IWUSR | S_IRUGO, show_type, set_type,
+                         BIT(3));
+static SENSOR_DEVICE_ATTR(temp5_type, S_IWUSR | S_IRUGO, show_type, set_type,
+                         BIT(4));
+
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_tcrit1,
+                         set_tcrit1, 0);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_tcrit2,
+                         set_tcrit2, 0);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_tcrit2,
+                         set_tcrit2, 1);
+static SENSOR_DEVICE_ATTR(temp4_max, S_IWUSR | S_IRUGO, show_tcrit1,
+                         set_tcrit1, 3);
+static SENSOR_DEVICE_ATTR(temp5_max, S_IWUSR | S_IRUGO, show_tcrit1,
+                         set_tcrit1, 4);
+
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_tcrit1_hyst,
+                         set_tcrit1_hyst, 0);
+static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IRUGO, show_tcrit2_hyst, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IRUGO, show_tcrit2_hyst, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp4_max_hyst, S_IRUGO, show_tcrit1_hyst, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_max_hyst, S_IRUGO, show_tcrit1_hyst, NULL, 4);
+
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL,
+                         BIT(0 + 8));
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL,
+                         BIT(1 + 16));
+static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL,
+                         BIT(2 + 16));
+static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL,
+                         BIT(3 + 8));
+static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_alarm, NULL,
+                         BIT(4 + 8));
+
+static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_tcrit1,
+                         set_tcrit1, 1);
+static SENSOR_DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_tcrit1,
+                         set_tcrit1, 2);
+
+static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_tcrit1_hyst, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_tcrit1_hyst, NULL, 2);
+
+static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL,
+                         BIT(1 + 8));
+static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL,
+                         BIT(2 + 8));
+
+static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_offset,
+                         set_offset, 0);
+static SENSOR_DEVICE_ATTR(temp3_offset, S_IWUSR | S_IRUGO, show_offset,
+                         set_offset, 1);
+static SENSOR_DEVICE_ATTR(temp4_offset, S_IWUSR | S_IRUGO, show_offset,
+                         set_offset, 2);
+static SENSOR_DEVICE_ATTR(temp5_offset, S_IWUSR | S_IRUGO, show_offset,
+                         set_offset, 3);
+
+static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval,
+                  set_interval);
+
+static struct attribute *lm95234_attributes[] = {
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp2_input.dev_attr.attr,
+       &sensor_dev_attr_temp3_input.dev_attr.attr,
+       &sensor_dev_attr_temp4_input.dev_attr.attr,
+       &sensor_dev_attr_temp5_input.dev_attr.attr,
+       &sensor_dev_attr_temp2_fault.dev_attr.attr,
+       &sensor_dev_attr_temp3_fault.dev_attr.attr,
+       &sensor_dev_attr_temp4_fault.dev_attr.attr,
+       &sensor_dev_attr_temp5_fault.dev_attr.attr,
+       &sensor_dev_attr_temp2_type.dev_attr.attr,
+       &sensor_dev_attr_temp3_type.dev_attr.attr,
+       &sensor_dev_attr_temp4_type.dev_attr.attr,
+       &sensor_dev_attr_temp5_type.dev_attr.attr,
+       &sensor_dev_attr_temp1_max.dev_attr.attr,
+       &sensor_dev_attr_temp2_max.dev_attr.attr,
+       &sensor_dev_attr_temp3_max.dev_attr.attr,
+       &sensor_dev_attr_temp4_max.dev_attr.attr,
+       &sensor_dev_attr_temp5_max.dev_attr.attr,
+       &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp5_max_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_crit.dev_attr.attr,
+       &sensor_dev_attr_temp3_crit.dev_attr.attr,
+       &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_offset.dev_attr.attr,
+       &sensor_dev_attr_temp3_offset.dev_attr.attr,
+       &sensor_dev_attr_temp4_offset.dev_attr.attr,
+       &sensor_dev_attr_temp5_offset.dev_attr.attr,
+       &dev_attr_update_interval.attr,
+       NULL
+};
+
+static const struct attribute_group lm95234_group = {
+       .attrs = lm95234_attributes,
+};
+
+static int lm95234_detect(struct i2c_client *client,
+                         struct i2c_board_info *info)
+{
+       struct i2c_adapter *adapter = client->adapter;
+       int mfg_id, chip_id, val;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return -ENODEV;
+
+       mfg_id = i2c_smbus_read_byte_data(client, LM95234_REG_MAN_ID);
+       if (mfg_id != NATSEMI_MAN_ID)
+               return -ENODEV;
+
+       chip_id = i2c_smbus_read_byte_data(client, LM95234_REG_CHIP_ID);
+       if (chip_id != LM95234_CHIP_ID)
+               return -ENODEV;
+
+       val = i2c_smbus_read_byte_data(client, LM95234_REG_STATUS);
+       if (val & 0x30)
+               return -ENODEV;
+
+       val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG);
+       if (val & 0xbc)
+               return -ENODEV;
+
+       val = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE);
+       if (val & 0xfc)
+               return -ENODEV;
+
+       val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL);
+       if (val & 0xe1)
+               return -ENODEV;
+
+       val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS);
+       if (val & 0xe1)
+               return -ENODEV;
+
+       strlcpy(info->type, "lm95234", I2C_NAME_SIZE);
+       return 0;
+}
+
+static int lm95234_init_client(struct i2c_client *client)
+{
+       int val, model;
+
+       /* start conversion if necessary */
+       val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG);
+       if (val < 0)
+               return val;
+       if (val & 0x40)
+               i2c_smbus_write_byte_data(client, LM95234_REG_CONFIG,
+                                         val & ~0x40);
+
+       /* If diode type status reports an error, try to fix it */
+       val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS);
+       if (val < 0)
+               return val;
+       model = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL);
+       if (model < 0)
+               return model;
+       if (model & val) {
+               dev_notice(&client->dev,
+                          "Fixing remote diode type misconfiguration (0x%x)\n",
+                          val);
+               i2c_smbus_write_byte_data(client, LM95234_REG_REM_MODEL,
+                                         model & ~val);
+       }
+       return 0;
+}
+
+static int lm95234_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct device *dev = &client->dev;
+       struct lm95234_data *data;
+       int err;
+
+       data = devm_kzalloc(dev, sizeof(struct lm95234_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, data);
+       mutex_init(&data->update_lock);
+
+       /* Initialize the LM95234 chip */
+       err = lm95234_init_client(client);
+       if (err < 0)
+               return err;
+
+       /* Register sysfs hooks */
+       err = sysfs_create_group(&dev->kobj, &lm95234_group);
+       if (err)
+               return err;
+
+       data->hwmon_dev = hwmon_device_register(dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
+               goto exit_remove_files;
+       }
+
+       return 0;
+
+exit_remove_files:
+       sysfs_remove_group(&dev->kobj, &lm95234_group);
+       return err;
+}
+
+static int lm95234_remove(struct i2c_client *client)
+{
+       struct lm95234_data *data = i2c_get_clientdata(client);
+
+       hwmon_device_unregister(data->hwmon_dev);
+       sysfs_remove_group(&client->dev.kobj, &lm95234_group);
+
+       return 0;
+}
+
+/* Driver data (common to all clients) */
+static const struct i2c_device_id lm95234_id[] = {
+       { "lm95234", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, lm95234_id);
+
+static struct i2c_driver lm95234_driver = {
+       .class          = I2C_CLASS_HWMON,
+       .driver = {
+               .name   = DRVNAME,
+       },
+       .probe          = lm95234_probe,
+       .remove         = lm95234_remove,
+       .id_table       = lm95234_id,
+       .detect         = lm95234_detect,
+       .address_list   = normal_i2c,
+};
+
+module_i2c_driver(lm95234_driver);
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("LM95234 sensor driver");
+MODULE_LICENSE("GPL");
index 4319a94f549d2bdd83541f3e0a293c1c419597c4..af81be1237c950739abf897e3c7f0b7f4307850d 100644 (file)
@@ -146,14 +146,14 @@ static ssize_t ltc4151_show_value(struct device *dev,
 /*
  * Input voltages.
  */
-static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, \
-       ltc4151_show_value, NULL, LTC4151_VIN_H);
-static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, \
-       ltc4151_show_value, NULL, LTC4151_ADIN_H);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4151_show_value, NULL,
+                         LTC4151_VIN_H);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4151_show_value, NULL,
+                         LTC4151_ADIN_H);
 
 /* Currents (via sense resistor) */
-static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, \
-       ltc4151_show_value, NULL, LTC4151_SENSE_H);
+static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4151_show_value, NULL,
+                         LTC4151_SENSE_H);
 
 /*
  * Finally, construct an array of pointers to members of the above objects,
index e8876108a6b3278a9e447912f3a3a5a9e1baf73a..8a142960d69ef1cbdbf884bf1b382cb78c1d5eb9 100644 (file)
@@ -172,12 +172,12 @@ static ssize_t ltc4215_show_alarm(struct device *dev,
                                          struct device_attribute *da,
                                          char *buf)
 {
-       struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
        struct ltc4215_data *data = ltc4215_update_device(dev);
-       const u8 reg = data->regs[attr->index];
-       const u32 mask = attr->nr;
+       const u8 reg = data->regs[LTC4215_STATUS];
+       const u32 mask = attr->index;
 
-       return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0);
+       return snprintf(buf, PAGE_SIZE, "%u\n", !!(reg & mask));
 }
 
 /*
@@ -186,39 +186,29 @@ static ssize_t ltc4215_show_alarm(struct device *dev,
  * for each register.
  */
 
-#define LTC4215_VOLTAGE(name, ltc4215_cmd_idx) \
-       static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
-       ltc4215_show_voltage, NULL, ltc4215_cmd_idx)
-
-#define LTC4215_CURRENT(name) \
-       static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
-       ltc4215_show_current, NULL, 0);
-
-#define LTC4215_POWER(name) \
-       static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
-       ltc4215_show_power, NULL, 0);
-
-#define LTC4215_ALARM(name, mask, reg) \
-       static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \
-       ltc4215_show_alarm, NULL, (mask), reg)
-
 /* Construct a sensor_device_attribute structure for each register */
 
 /* Current */
-LTC4215_CURRENT(curr1_input);
-LTC4215_ALARM(curr1_max_alarm, (1 << 2),       LTC4215_STATUS);
+static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4215_show_current, NULL, 0);
+static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc4215_show_alarm, NULL,
+                         1 << 2);
 
 /* Power (virtual) */
-LTC4215_POWER(power1_input);
+static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ltc4215_show_power, NULL, 0);
 
 /* Input Voltage */
-LTC4215_VOLTAGE(in1_input,                     LTC4215_ADIN);
-LTC4215_ALARM(in1_max_alarm,   (1 << 0),       LTC4215_STATUS);
-LTC4215_ALARM(in1_min_alarm,   (1 << 1),       LTC4215_STATUS);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4215_show_voltage, NULL,
+                         LTC4215_ADIN);
+static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ltc4215_show_alarm, NULL,
+                         1 << 0);
+static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ltc4215_show_alarm, NULL,
+                         1 << 1);
 
 /* Output Voltage */
-LTC4215_VOLTAGE(in2_input,                     LTC4215_SOURCE);
-LTC4215_ALARM(in2_min_alarm,   (1 << 3),       LTC4215_STATUS);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4215_show_voltage, NULL,
+                         LTC4215_SOURCE);
+static SENSOR_DEVICE_ATTR(in2_min_alarm, S_IRUGO, ltc4215_show_alarm, NULL,
+                         1 << 3);
 
 /*
  * Finally, construct an array of pointers to members of the above objects,
index 3653f79dc2de0870f4d642a8e556e525ed8bac95..cdc1ecc6734d4122f9aab8736f5b4f37217aaf6b 100644 (file)
@@ -319,80 +319,82 @@ static ssize_t ltc4245_show_gpio(struct device *dev,
        return snprintf(buf, PAGE_SIZE, "%u\n", val * 10);
 }
 
-/*
- * These macros are used below in constructing device attribute objects
- * for use with sysfs_create_group() to make a sysfs device file
- * for each register.
- */
-
-#define LTC4245_VOLTAGE(name, ltc4245_cmd_idx) \
-       static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
-       ltc4245_show_voltage, NULL, ltc4245_cmd_idx)
-
-#define LTC4245_CURRENT(name, ltc4245_cmd_idx) \
-       static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
-       ltc4245_show_current, NULL, ltc4245_cmd_idx)
-
-#define LTC4245_POWER(name, ltc4245_cmd_idx) \
-       static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
-       ltc4245_show_power, NULL, ltc4245_cmd_idx)
-
-#define LTC4245_ALARM(name, mask, reg) \
-       static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \
-       ltc4245_show_alarm, NULL, (mask), reg)
-
-#define LTC4245_GPIO_VOLTAGE(name, gpio_num) \
-       static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
-       ltc4245_show_gpio, NULL, gpio_num)
-
 /* Construct a sensor_device_attribute structure for each register */
 
 /* Input voltages */
-LTC4245_VOLTAGE(in1_input,                     LTC4245_12VIN);
-LTC4245_VOLTAGE(in2_input,                     LTC4245_5VIN);
-LTC4245_VOLTAGE(in3_input,                     LTC4245_3VIN);
-LTC4245_VOLTAGE(in4_input,                     LTC4245_VEEIN);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4245_show_voltage, NULL,
+                         LTC4245_12VIN);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4245_show_voltage, NULL,
+                         LTC4245_5VIN);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc4245_show_voltage, NULL,
+                         LTC4245_3VIN);
+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc4245_show_voltage, NULL,
+                         LTC4245_VEEIN);
 
 /* Input undervoltage alarms */
-LTC4245_ALARM(in1_min_alarm,   (1 << 0),       LTC4245_FAULT1);
-LTC4245_ALARM(in2_min_alarm,   (1 << 1),       LTC4245_FAULT1);
-LTC4245_ALARM(in3_min_alarm,   (1 << 2),       LTC4245_FAULT1);
-LTC4245_ALARM(in4_min_alarm,   (1 << 3),       LTC4245_FAULT1);
+static SENSOR_DEVICE_ATTR_2(in1_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+                           1 << 0, LTC4245_FAULT1);
+static SENSOR_DEVICE_ATTR_2(in2_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+                           1 << 1, LTC4245_FAULT1);
+static SENSOR_DEVICE_ATTR_2(in3_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+                           1 << 2, LTC4245_FAULT1);
+static SENSOR_DEVICE_ATTR_2(in4_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+                           1 << 3, LTC4245_FAULT1);
 
 /* Currents (via sense resistor) */
-LTC4245_CURRENT(curr1_input,                   LTC4245_12VSENSE);
-LTC4245_CURRENT(curr2_input,                   LTC4245_5VSENSE);
-LTC4245_CURRENT(curr3_input,                   LTC4245_3VSENSE);
-LTC4245_CURRENT(curr4_input,                   LTC4245_VEESENSE);
+static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4245_show_current, NULL,
+                         LTC4245_12VSENSE);
+static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc4245_show_current, NULL,
+                         LTC4245_5VSENSE);
+static SENSOR_DEVICE_ATTR(curr3_input, S_IRUGO, ltc4245_show_current, NULL,
+                         LTC4245_3VSENSE);
+static SENSOR_DEVICE_ATTR(curr4_input, S_IRUGO, ltc4245_show_current, NULL,
+                         LTC4245_VEESENSE);
 
 /* Overcurrent alarms */
-LTC4245_ALARM(curr1_max_alarm, (1 << 4),       LTC4245_FAULT1);
-LTC4245_ALARM(curr2_max_alarm, (1 << 5),       LTC4245_FAULT1);
-LTC4245_ALARM(curr3_max_alarm, (1 << 6),       LTC4245_FAULT1);
-LTC4245_ALARM(curr4_max_alarm, (1 << 7),       LTC4245_FAULT1);
+static SENSOR_DEVICE_ATTR_2(curr1_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+                           1 << 4, LTC4245_FAULT1);
+static SENSOR_DEVICE_ATTR_2(curr2_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+                           1 << 5, LTC4245_FAULT1);
+static SENSOR_DEVICE_ATTR_2(curr3_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+                           1 << 6, LTC4245_FAULT1);
+static SENSOR_DEVICE_ATTR_2(curr4_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+                           1 << 7, LTC4245_FAULT1);
 
 /* Output voltages */
-LTC4245_VOLTAGE(in5_input,                     LTC4245_12VOUT);
-LTC4245_VOLTAGE(in6_input,                     LTC4245_5VOUT);
-LTC4245_VOLTAGE(in7_input,                     LTC4245_3VOUT);
-LTC4245_VOLTAGE(in8_input,                     LTC4245_VEEOUT);
+static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, ltc4245_show_voltage, NULL,
+                         LTC4245_12VOUT);
+static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, ltc4245_show_voltage, NULL,
+                         LTC4245_5VOUT);
+static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, ltc4245_show_voltage, NULL,
+                         LTC4245_3VOUT);
+static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, ltc4245_show_voltage, NULL,
+                         LTC4245_VEEOUT);
 
 /* Power Bad alarms */
-LTC4245_ALARM(in5_min_alarm,   (1 << 0),       LTC4245_FAULT2);
-LTC4245_ALARM(in6_min_alarm,   (1 << 1),       LTC4245_FAULT2);
-LTC4245_ALARM(in7_min_alarm,   (1 << 2),       LTC4245_FAULT2);
-LTC4245_ALARM(in8_min_alarm,   (1 << 3),       LTC4245_FAULT2);
+static SENSOR_DEVICE_ATTR_2(in5_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+                           1 << 0, LTC4245_FAULT2);
+static SENSOR_DEVICE_ATTR_2(in6_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+                           1 << 1, LTC4245_FAULT2);
+static SENSOR_DEVICE_ATTR_2(in7_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+                           1 << 2, LTC4245_FAULT2);
+static SENSOR_DEVICE_ATTR_2(in8_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
+                           1 << 3, LTC4245_FAULT2);
 
 /* GPIO voltages */
-LTC4245_GPIO_VOLTAGE(in9_input,                        0);
-LTC4245_GPIO_VOLTAGE(in10_input,               1);
-LTC4245_GPIO_VOLTAGE(in11_input,               2);
+static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, ltc4245_show_gpio, NULL, 0);
+static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, ltc4245_show_gpio, NULL, 1);
+static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, ltc4245_show_gpio, NULL, 2);
 
 /* Power Consumption (virtual) */
-LTC4245_POWER(power1_input,                    LTC4245_12VSENSE);
-LTC4245_POWER(power2_input,                    LTC4245_5VSENSE);
-LTC4245_POWER(power3_input,                    LTC4245_3VSENSE);
-LTC4245_POWER(power4_input,                    LTC4245_VEESENSE);
+static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ltc4245_show_power, NULL,
+                         LTC4245_12VSENSE);
+static SENSOR_DEVICE_ATTR(power2_input, S_IRUGO, ltc4245_show_power, NULL,
+                         LTC4245_5VSENSE);
+static SENSOR_DEVICE_ATTR(power3_input, S_IRUGO, ltc4245_show_power, NULL,
+                         LTC4245_3VSENSE);
+static SENSOR_DEVICE_ATTR(power4_input, S_IRUGO, ltc4245_show_power, NULL,
+                         LTC4245_VEESENSE);
 
 /*
  * Finally, construct an array of pointers to members of the above objects,
index 84a2d2872b20c4172a73a045ec1c1c6223dcd27e..487da58ec86c2fbab1cceacbc9b8b0ffa799ea20 100644 (file)
@@ -164,25 +164,13 @@ static ssize_t ltc4261_show_bool(struct device *dev,
        return snprintf(buf, PAGE_SIZE, "%d\n", fault ? 1 : 0);
 }
 
-/*
- * These macros are used below in constructing device attribute objects
- * for use with sysfs_create_group() to make a sysfs device file
- * for each register.
- */
-
-#define LTC4261_VALUE(name, ltc4261_cmd_idx) \
-       static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
-       ltc4261_show_value, NULL, ltc4261_cmd_idx)
-
-#define LTC4261_BOOL(name, mask) \
-       static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
-       ltc4261_show_bool, NULL, (mask))
-
 /*
  * Input voltages.
  */
-LTC4261_VALUE(in1_input, LTC4261_ADIN_H);
-LTC4261_VALUE(in2_input, LTC4261_ADIN2_H);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4261_show_value, NULL,
+                         LTC4261_ADIN_H);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4261_show_value, NULL,
+                         LTC4261_ADIN2_H);
 
 /*
  * Voltage alarms. The chip has only one set of voltage alarm status bits,
@@ -192,16 +180,22 @@ LTC4261_VALUE(in2_input, LTC4261_ADIN2_H);
  * To ensure that the alarm condition is reported to the user, report it
  * with both voltage sensors.
  */
-LTC4261_BOOL(in1_min_alarm, FAULT_UV);
-LTC4261_BOOL(in1_max_alarm, FAULT_OV);
-LTC4261_BOOL(in2_min_alarm, FAULT_UV);
-LTC4261_BOOL(in2_max_alarm, FAULT_OV);
+static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ltc4261_show_bool, NULL,
+                         FAULT_UV);
+static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ltc4261_show_bool, NULL,
+                         FAULT_OV);
+static SENSOR_DEVICE_ATTR(in2_min_alarm, S_IRUGO, ltc4261_show_bool, NULL,
+                         FAULT_UV);
+static SENSOR_DEVICE_ATTR(in2_max_alarm, S_IRUGO, ltc4261_show_bool, NULL,
+                         FAULT_OV);
 
 /* Currents (via sense resistor) */
-LTC4261_VALUE(curr1_input, LTC4261_SENSE_H);
+static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4261_show_value, NULL,
+                         LTC4261_SENSE_H);
 
 /* Overcurrent alarm */
-LTC4261_BOOL(curr1_max_alarm, FAULT_OC);
+static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc4261_show_bool, NULL,
+                         FAULT_OC);
 
 static struct attribute *ltc4261_attributes[] = {
        &sensor_dev_attr_in1_input.dev_attr.attr,
index bf4aa3777fc1f8d5db2bf670c4e97f5d89a8673b..328fb0353c171a1b879af1040185afa1be109ea4 100644 (file)
@@ -399,82 +399,95 @@ static SENSOR_DEVICE_ATTR(temp6_fault, S_IRUGO, show_alarm, NULL, 5);
 static SENSOR_DEVICE_ATTR(temp7_fault, S_IRUGO, show_alarm, NULL, 6);
 static SENSOR_DEVICE_ATTR(temp8_fault, S_IRUGO, show_alarm, NULL, 7);
 
-static struct attribute *max6697_attributes[8][7] = {
-       {
-               &sensor_dev_attr_temp1_input.dev_attr.attr,
-               &sensor_dev_attr_temp1_max.dev_attr.attr,
-               &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
-               &sensor_dev_attr_temp1_crit.dev_attr.attr,
-               &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
-               NULL
-       }, {
-               &sensor_dev_attr_temp2_input.dev_attr.attr,
-               &sensor_dev_attr_temp2_max.dev_attr.attr,
-               &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
-               &sensor_dev_attr_temp2_crit.dev_attr.attr,
-               &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
-               &sensor_dev_attr_temp2_fault.dev_attr.attr,
-               NULL
-       }, {
-               &sensor_dev_attr_temp3_input.dev_attr.attr,
-               &sensor_dev_attr_temp3_max.dev_attr.attr,
-               &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
-               &sensor_dev_attr_temp3_crit.dev_attr.attr,
-               &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
-               &sensor_dev_attr_temp3_fault.dev_attr.attr,
-               NULL
-       }, {
-               &sensor_dev_attr_temp4_input.dev_attr.attr,
-               &sensor_dev_attr_temp4_max.dev_attr.attr,
-               &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
-               &sensor_dev_attr_temp4_crit.dev_attr.attr,
-               &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
-               &sensor_dev_attr_temp4_fault.dev_attr.attr,
-               NULL
-       }, {
-               &sensor_dev_attr_temp5_input.dev_attr.attr,
-               &sensor_dev_attr_temp5_max.dev_attr.attr,
-               &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
-               &sensor_dev_attr_temp5_crit.dev_attr.attr,
-               &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr,
-               &sensor_dev_attr_temp5_fault.dev_attr.attr,
-               NULL
-       }, {
-               &sensor_dev_attr_temp6_input.dev_attr.attr,
-               &sensor_dev_attr_temp6_max.dev_attr.attr,
-               &sensor_dev_attr_temp6_max_alarm.dev_attr.attr,
-               &sensor_dev_attr_temp6_crit.dev_attr.attr,
-               &sensor_dev_attr_temp6_crit_alarm.dev_attr.attr,
-               &sensor_dev_attr_temp6_fault.dev_attr.attr,
-               NULL
-       }, {
-               &sensor_dev_attr_temp7_input.dev_attr.attr,
-               &sensor_dev_attr_temp7_max.dev_attr.attr,
-               &sensor_dev_attr_temp7_max_alarm.dev_attr.attr,
-               &sensor_dev_attr_temp7_crit.dev_attr.attr,
-               &sensor_dev_attr_temp7_crit_alarm.dev_attr.attr,
-               &sensor_dev_attr_temp7_fault.dev_attr.attr,
-               NULL
-       }, {
-               &sensor_dev_attr_temp8_input.dev_attr.attr,
-               &sensor_dev_attr_temp8_max.dev_attr.attr,
-               &sensor_dev_attr_temp8_max_alarm.dev_attr.attr,
-               &sensor_dev_attr_temp8_crit.dev_attr.attr,
-               &sensor_dev_attr_temp8_crit_alarm.dev_attr.attr,
-               &sensor_dev_attr_temp8_fault.dev_attr.attr,
-               NULL
-       }
+static DEVICE_ATTR(dummy, 0, NULL, NULL);
+
+static umode_t max6697_is_visible(struct kobject *kobj, struct attribute *attr,
+                                 int index)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct i2c_client *client = to_i2c_client(dev);
+       struct max6697_data *data = i2c_get_clientdata(client);
+       const struct max6697_chip_data *chip = data->chip;
+       int channel = index / 6;        /* channel number */
+       int nr = index % 6;             /* attribute index within channel */
+
+       if (channel >= chip->channels)
+               return 0;
+
+       if ((nr == 3 || nr == 4) && !(chip->have_crit & (1 << channel)))
+               return 0;
+       if (nr == 5 && !(chip->have_fault & (1 << channel)))
+               return 0;
+
+       return attr->mode;
+}
+
+/*
+ * max6697_is_visible uses the index into the following array to determine
+ * if attributes should be created or not. Any change in order or content
+ * must be matched in max6697_is_visible.
+ */
+static struct attribute *max6697_attributes[] = {
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp1_max.dev_attr.attr,
+       &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp1_crit.dev_attr.attr,
+       &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+       &dev_attr_dummy.attr,
+
+       &sensor_dev_attr_temp2_input.dev_attr.attr,
+       &sensor_dev_attr_temp2_max.dev_attr.attr,
+       &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_crit.dev_attr.attr,
+       &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_fault.dev_attr.attr,
+
+       &sensor_dev_attr_temp3_input.dev_attr.attr,
+       &sensor_dev_attr_temp3_max.dev_attr.attr,
+       &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp3_crit.dev_attr.attr,
+       &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp3_fault.dev_attr.attr,
+
+       &sensor_dev_attr_temp4_input.dev_attr.attr,
+       &sensor_dev_attr_temp4_max.dev_attr.attr,
+       &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp4_crit.dev_attr.attr,
+       &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp4_fault.dev_attr.attr,
+
+       &sensor_dev_attr_temp5_input.dev_attr.attr,
+       &sensor_dev_attr_temp5_max.dev_attr.attr,
+       &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp5_crit.dev_attr.attr,
+       &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp5_fault.dev_attr.attr,
+
+       &sensor_dev_attr_temp6_input.dev_attr.attr,
+       &sensor_dev_attr_temp6_max.dev_attr.attr,
+       &sensor_dev_attr_temp6_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp6_crit.dev_attr.attr,
+       &sensor_dev_attr_temp6_crit_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp6_fault.dev_attr.attr,
+
+       &sensor_dev_attr_temp7_input.dev_attr.attr,
+       &sensor_dev_attr_temp7_max.dev_attr.attr,
+       &sensor_dev_attr_temp7_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp7_crit.dev_attr.attr,
+       &sensor_dev_attr_temp7_crit_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp7_fault.dev_attr.attr,
+
+       &sensor_dev_attr_temp8_input.dev_attr.attr,
+       &sensor_dev_attr_temp8_max.dev_attr.attr,
+       &sensor_dev_attr_temp8_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp8_crit.dev_attr.attr,
+       &sensor_dev_attr_temp8_crit_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp8_fault.dev_attr.attr,
+       NULL
 };
 
-static const struct attribute_group max6697_group[8] = {
-       { .attrs = max6697_attributes[0] },
-       { .attrs = max6697_attributes[1] },
-       { .attrs = max6697_attributes[2] },
-       { .attrs = max6697_attributes[3] },
-       { .attrs = max6697_attributes[4] },
-       { .attrs = max6697_attributes[5] },
-       { .attrs = max6697_attributes[6] },
-       { .attrs = max6697_attributes[7] },
+static const struct attribute_group max6697_group = {
+       .attrs = max6697_attributes, .is_visible = max6697_is_visible,
 };
 
 static void max6697_get_config_of(struct device_node *node,
@@ -606,21 +619,13 @@ done:
        return 0;
 }
 
-static void max6697_remove_files(struct i2c_client *client)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(max6697_group); i++)
-               sysfs_remove_group(&client->dev.kobj, &max6697_group[i]);
-}
-
 static int max6697_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
        struct i2c_adapter *adapter = client->adapter;
        struct device *dev = &client->dev;
        struct max6697_data *data;
-       int i, err;
+       int err;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
@@ -639,37 +644,9 @@ static int max6697_probe(struct i2c_client *client,
        if (err)
                return err;
 
-       for (i = 0; i < data->chip->channels; i++) {
-               err = sysfs_create_file(&dev->kobj,
-                                       max6697_attributes[i][0]);
-               if (err)
-                       goto error;
-               err = sysfs_create_file(&dev->kobj,
-                                       max6697_attributes[i][1]);
-               if (err)
-                       goto error;
-               err = sysfs_create_file(&dev->kobj,
-                                       max6697_attributes[i][2]);
-               if (err)
-                       goto error;
-
-               if (data->chip->have_crit & (1 << i)) {
-                       err = sysfs_create_file(&dev->kobj,
-                                               max6697_attributes[i][3]);
-                       if (err)
-                               goto error;
-                       err = sysfs_create_file(&dev->kobj,
-                                               max6697_attributes[i][4]);
-                       if (err)
-                               goto error;
-               }
-               if (data->chip->have_fault & (1 << i)) {
-                       err = sysfs_create_file(&dev->kobj,
-                                               max6697_attributes[i][5]);
-                       if (err)
-                               goto error;
-               }
-       }
+       err = sysfs_create_group(&client->dev.kobj, &max6697_group);
+       if (err)
+               return err;
 
        data->hwmon_dev = hwmon_device_register(dev);
        if (IS_ERR(data->hwmon_dev)) {
@@ -680,7 +657,7 @@ static int max6697_probe(struct i2c_client *client,
        return 0;
 
 error:
-       max6697_remove_files(client);
+       sysfs_remove_group(&client->dev.kobj, &max6697_group);
        return err;
 }
 
@@ -689,7 +666,7 @@ static int max6697_remove(struct i2c_client *client)
        struct max6697_data *data = i2c_get_clientdata(client);
 
        hwmon_device_unregister(data->hwmon_dev);
-       max6697_remove_files(client);
+       sysfs_remove_group(&client->dev.kobj, &max6697_group);
 
        return 0;
 }
index 2a7f331cd3c012a48652fffed43c458482ec2f89..982d8622c09b375f080bc2c81bfbacb71cf0896c 100644 (file)
@@ -273,18 +273,7 @@ static struct platform_driver mc13783_adc_driver = {
        .id_table       = mc13783_adc_idtable,
 };
 
-static int __init mc13783_adc_init(void)
-{
-       return platform_driver_probe(&mc13783_adc_driver, mc13783_adc_probe);
-}
-
-static void __exit mc13783_adc_exit(void)
-{
-       platform_driver_unregister(&mc13783_adc_driver);
-}
-
-module_init(mc13783_adc_init);
-module_exit(mc13783_adc_exit);
+module_platform_driver_probe(mc13783_adc_driver, mc13783_adc_probe);
 
 MODULE_DESCRIPTION("MC13783 ADC driver");
 MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c
new file mode 100644 (file)
index 0000000..f43f5e5
--- /dev/null
@@ -0,0 +1,4191 @@
+/*
+ * nct6775 - Driver for the hardware monitoring functionality of
+ *            Nuvoton NCT677x Super-I/O chips
+ *
+ * Copyright (C) 2012  Guenter Roeck <linux@roeck-us.net>
+ *
+ * Derived from w83627ehf driver
+ * Copyright (C) 2005-2012  Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2006  Yuan Mu (Winbond),
+ *                    Rudolf Marek <r.marek@assembler.cz>
+ *                    David Hubbard <david.c.hubbard@gmail.com>
+ *                    Daniel J Blueman <daniel.blueman@gmail.com>
+ * Copyright (C) 2010  Sheng-Yuan Huang (Nuvoton) (PS00)
+ *
+ * Shamelessly ripped from the w83627hf driver
+ * Copyright (C) 2003  Mark Studebaker
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * Supports the following chips:
+ *
+ * Chip        #vin    #fan    #pwm    #temp  chip IDs       man ID
+ * nct6775f     9      4       3       6+3    0xb470 0xc1    0x5ca3
+ * nct6776f     9      5       3       6+3    0xc330 0xc1    0x5ca3
+ * nct6779d    15      5       5       2+6    0xc560 0xc1    0x5ca3
+ *
+ * #temp lists the number of monitored temperature sources (first value) plus
+ * the number of directly connectable temperature sensors (second value).
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon-vid.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include "lm75.h"
+
+#define USE_ALTERNATE
+
+enum kinds { nct6775, nct6776, nct6779 };
+
+/* used to set data->name = nct6775_device_names[data->sio_kind] */
+static const char * const nct6775_device_names[] = {
+       "nct6775",
+       "nct6776",
+       "nct6779",
+};
+
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+static unsigned short fan_debounce;
+module_param(fan_debounce, ushort, 0);
+MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
+
+#define DRVNAME "nct6775"
+
+/*
+ * Super-I/O constants and functions
+ */
+
+#define NCT6775_LD_ACPI                0x0a
+#define NCT6775_LD_HWM         0x0b
+#define NCT6775_LD_VID         0x0d
+
+#define SIO_REG_LDSEL          0x07    /* Logical device select */
+#define SIO_REG_DEVID          0x20    /* Device ID (2 bytes) */
+#define SIO_REG_ENABLE         0x30    /* Logical device enable */
+#define SIO_REG_ADDR           0x60    /* Logical device address (2 bytes) */
+
+#define SIO_NCT6775_ID         0xb470
+#define SIO_NCT6776_ID         0xc330
+#define SIO_NCT6779_ID         0xc560
+#define SIO_ID_MASK            0xFFF0
+
+enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };
+
+static inline void
+superio_outb(int ioreg, int reg, int val)
+{
+       outb(reg, ioreg);
+       outb(val, ioreg + 1);
+}
+
+static inline int
+superio_inb(int ioreg, int reg)
+{
+       outb(reg, ioreg);
+       return inb(ioreg + 1);
+}
+
+static inline void
+superio_select(int ioreg, int ld)
+{
+       outb(SIO_REG_LDSEL, ioreg);
+       outb(ld, ioreg + 1);
+}
+
+static inline int
+superio_enter(int ioreg)
+{
+       /*
+        * Try to reserve <ioreg> and <ioreg + 1> for exclusive access.
+        */
+       if (!request_muxed_region(ioreg, 2, DRVNAME))
+               return -EBUSY;
+
+       outb(0x87, ioreg);
+       outb(0x87, ioreg);
+
+       return 0;
+}
+
+static inline void
+superio_exit(int ioreg)
+{
+       outb(0xaa, ioreg);
+       outb(0x02, ioreg);
+       outb(0x02, ioreg + 1);
+       release_region(ioreg, 2);
+}
+
+/*
+ * ISA constants
+ */
+
+#define IOREGION_ALIGNMENT     (~7)
+#define IOREGION_OFFSET                5
+#define IOREGION_LENGTH                2
+#define ADDR_REG_OFFSET                0
+#define DATA_REG_OFFSET                1
+
+#define NCT6775_REG_BANK       0x4E
+#define NCT6775_REG_CONFIG     0x40
+
+/*
+ * Not currently used:
+ * REG_MAN_ID has the value 0x5ca3 for all supported chips.
+ * REG_CHIP_ID == 0x88/0xa1/0xc1 depending on chip model.
+ * REG_MAN_ID is at port 0x4f
+ * REG_CHIP_ID is at port 0x58
+ */
+
+#define NUM_TEMP       10      /* Max number of temp attribute sets w/ limits*/
+#define NUM_TEMP_FIXED 6       /* Max number of fixed temp attribute sets */
+
+#define NUM_REG_ALARM  4       /* Max number of alarm registers */
+
+/* Common and NCT6775 specific data */
+
+/* Voltage min/max registers for nr=7..14 are in bank 5 */
+
+static const u16 NCT6775_REG_IN_MAX[] = {
+       0x2b, 0x2d, 0x2f, 0x31, 0x33, 0x35, 0x37, 0x554, 0x556, 0x558, 0x55a,
+       0x55c, 0x55e, 0x560, 0x562 };
+static const u16 NCT6775_REG_IN_MIN[] = {
+       0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x555, 0x557, 0x559, 0x55b,
+       0x55d, 0x55f, 0x561, 0x563 };
+static const u16 NCT6775_REG_IN[] = {
+       0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x550, 0x551, 0x552
+};
+
+#define NCT6775_REG_VBAT               0x5D
+#define NCT6775_REG_DIODE              0x5E
+
+#define NCT6775_REG_FANDIV1            0x506
+#define NCT6775_REG_FANDIV2            0x507
+
+#define NCT6775_REG_CR_FAN_DEBOUNCE    0xf0
+
+static const u16 NCT6775_REG_ALARM[NUM_REG_ALARM] = { 0x459, 0x45A, 0x45B };
+
+/* 0..15 voltages, 16..23 fans, 24..31 temperatures */
+
+static const s8 NCT6775_ALARM_BITS[] = {
+       0, 1, 2, 3, 8, 21, 20, 16,      /* in0.. in7 */
+       17, -1, -1, -1, -1, -1, -1,     /* in8..in14 */
+       -1,                             /* unused */
+       6, 7, 11, 10, 23,               /* fan1..fan5 */
+       -1, -1, -1,                     /* unused */
+       4, 5, 13, -1, -1, -1,           /* temp1..temp6 */
+       12, -1 };                       /* intrusion0, intrusion1 */
+
+#define FAN_ALARM_BASE         16
+#define TEMP_ALARM_BASE                24
+#define INTRUSION_ALARM_BASE   30
+
+static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee };
+static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 };
+
+/* DC or PWM output fan configuration */
+static const u8 NCT6775_REG_PWM_MODE[] = { 0x04, 0x04, 0x12 };
+static const u8 NCT6775_PWM_MODE_MASK[] = { 0x01, 0x02, 0x01 };
+
+/* Advanced Fan control, some values are common for all fans */
+
+static const u16 NCT6775_REG_TARGET[] = { 0x101, 0x201, 0x301, 0x801, 0x901 };
+static const u16 NCT6775_REG_FAN_MODE[] = { 0x102, 0x202, 0x302, 0x802, 0x902 };
+static const u16 NCT6775_REG_FAN_STEP_DOWN_TIME[] = {
+       0x103, 0x203, 0x303, 0x803, 0x903 };
+static const u16 NCT6775_REG_FAN_STEP_UP_TIME[] = {
+       0x104, 0x204, 0x304, 0x804, 0x904 };
+static const u16 NCT6775_REG_FAN_STOP_OUTPUT[] = {
+       0x105, 0x205, 0x305, 0x805, 0x905 };
+static const u16 NCT6775_REG_FAN_START_OUTPUT[]
+       = { 0x106, 0x206, 0x306, 0x806, 0x906 };
+static const u16 NCT6775_REG_FAN_MAX_OUTPUT[] = { 0x10a, 0x20a, 0x30a };
+static const u16 NCT6775_REG_FAN_STEP_OUTPUT[] = { 0x10b, 0x20b, 0x30b };
+
+static const u16 NCT6775_REG_FAN_STOP_TIME[] = {
+       0x107, 0x207, 0x307, 0x807, 0x907 };
+static const u16 NCT6775_REG_PWM[] = { 0x109, 0x209, 0x309, 0x809, 0x909 };
+static const u16 NCT6775_REG_PWM_READ[] = { 0x01, 0x03, 0x11, 0x13, 0x15 };
+
+static const u16 NCT6775_REG_FAN[] = { 0x630, 0x632, 0x634, 0x636, 0x638 };
+static const u16 NCT6775_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d };
+static const u16 NCT6775_REG_FAN_PULSES[] = { 0x641, 0x642, 0x643, 0x644, 0 };
+
+static const u16 NCT6775_REG_TEMP[] = {
+       0x27, 0x150, 0x250, 0x62b, 0x62c, 0x62d };
+
+static const u16 NCT6775_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
+       0, 0x152, 0x252, 0x628, 0x629, 0x62A };
+static const u16 NCT6775_REG_TEMP_HYST[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
+       0x3a, 0x153, 0x253, 0x673, 0x678, 0x67D };
+static const u16 NCT6775_REG_TEMP_OVER[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
+       0x39, 0x155, 0x255, 0x672, 0x677, 0x67C };
+
+static const u16 NCT6775_REG_TEMP_SOURCE[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
+       0x621, 0x622, 0x623, 0x624, 0x625, 0x626 };
+
+static const u16 NCT6775_REG_TEMP_SEL[] = {
+       0x100, 0x200, 0x300, 0x800, 0x900 };
+
+static const u16 NCT6775_REG_WEIGHT_TEMP_SEL[] = {
+       0x139, 0x239, 0x339, 0x839, 0x939 };
+static const u16 NCT6775_REG_WEIGHT_TEMP_STEP[] = {
+       0x13a, 0x23a, 0x33a, 0x83a, 0x93a };
+static const u16 NCT6775_REG_WEIGHT_TEMP_STEP_TOL[] = {
+       0x13b, 0x23b, 0x33b, 0x83b, 0x93b };
+static const u16 NCT6775_REG_WEIGHT_DUTY_STEP[] = {
+       0x13c, 0x23c, 0x33c, 0x83c, 0x93c };
+static const u16 NCT6775_REG_WEIGHT_TEMP_BASE[] = {
+       0x13d, 0x23d, 0x33d, 0x83d, 0x93d };
+
+static const u16 NCT6775_REG_TEMP_OFFSET[] = { 0x454, 0x455, 0x456 };
+
+static const u16 NCT6775_REG_AUTO_TEMP[] = {
+       0x121, 0x221, 0x321, 0x821, 0x921 };
+static const u16 NCT6775_REG_AUTO_PWM[] = {
+       0x127, 0x227, 0x327, 0x827, 0x927 };
+
+#define NCT6775_AUTO_TEMP(data, nr, p) ((data)->REG_AUTO_TEMP[nr] + (p))
+#define NCT6775_AUTO_PWM(data, nr, p)  ((data)->REG_AUTO_PWM[nr] + (p))
+
+static const u16 NCT6775_REG_CRITICAL_ENAB[] = { 0x134, 0x234, 0x334 };
+
+static const u16 NCT6775_REG_CRITICAL_TEMP[] = {
+       0x135, 0x235, 0x335, 0x835, 0x935 };
+static const u16 NCT6775_REG_CRITICAL_TEMP_TOLERANCE[] = {
+       0x138, 0x238, 0x338, 0x838, 0x938 };
+
+static const char *const nct6775_temp_label[] = {
+       "",
+       "SYSTIN",
+       "CPUTIN",
+       "AUXTIN",
+       "AMD SB-TSI",
+       "PECI Agent 0",
+       "PECI Agent 1",
+       "PECI Agent 2",
+       "PECI Agent 3",
+       "PECI Agent 4",
+       "PECI Agent 5",
+       "PECI Agent 6",
+       "PECI Agent 7",
+       "PCH_CHIP_CPU_MAX_TEMP",
+       "PCH_CHIP_TEMP",
+       "PCH_CPU_TEMP",
+       "PCH_MCH_TEMP",
+       "PCH_DIM0_TEMP",
+       "PCH_DIM1_TEMP",
+       "PCH_DIM2_TEMP",
+       "PCH_DIM3_TEMP"
+};
+
+static const u16 NCT6775_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6775_temp_label) - 1]
+       = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x661, 0x662, 0x664 };
+
+static const u16 NCT6775_REG_TEMP_CRIT[ARRAY_SIZE(nct6775_temp_label) - 1]
+       = { 0, 0, 0, 0, 0xa00, 0xa01, 0xa02, 0xa03, 0xa04, 0xa05, 0xa06,
+           0xa07 };
+
+/* NCT6776 specific data */
+
+static const s8 NCT6776_ALARM_BITS[] = {
+       0, 1, 2, 3, 8, 21, 20, 16,      /* in0.. in7 */
+       17, -1, -1, -1, -1, -1, -1,     /* in8..in14 */
+       -1,                             /* unused */
+       6, 7, 11, 10, 23,               /* fan1..fan5 */
+       -1, -1, -1,                     /* unused */
+       4, 5, 13, -1, -1, -1,           /* temp1..temp6 */
+       12, 9 };                        /* intrusion0, intrusion1 */
+
+static const u16 NCT6776_REG_TOLERANCE_H[] = {
+       0x10c, 0x20c, 0x30c, 0x80c, 0x90c };
+
+static const u8 NCT6776_REG_PWM_MODE[] = { 0x04, 0, 0 };
+static const u8 NCT6776_PWM_MODE_MASK[] = { 0x01, 0, 0 };
+
+static const u16 NCT6776_REG_FAN_MIN[] = { 0x63a, 0x63c, 0x63e, 0x640, 0x642 };
+static const u16 NCT6776_REG_FAN_PULSES[] = { 0x644, 0x645, 0x646, 0, 0 };
+
+static const u16 NCT6776_REG_WEIGHT_DUTY_BASE[] = {
+       0x13e, 0x23e, 0x33e, 0x83e, 0x93e };
+
+static const u16 NCT6776_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
+       0x18, 0x152, 0x252, 0x628, 0x629, 0x62A };
+
+static const char *const nct6776_temp_label[] = {
+       "",
+       "SYSTIN",
+       "CPUTIN",
+       "AUXTIN",
+       "SMBUSMASTER 0",
+       "SMBUSMASTER 1",
+       "SMBUSMASTER 2",
+       "SMBUSMASTER 3",
+       "SMBUSMASTER 4",
+       "SMBUSMASTER 5",
+       "SMBUSMASTER 6",
+       "SMBUSMASTER 7",
+       "PECI Agent 0",
+       "PECI Agent 1",
+       "PCH_CHIP_CPU_MAX_TEMP",
+       "PCH_CHIP_TEMP",
+       "PCH_CPU_TEMP",
+       "PCH_MCH_TEMP",
+       "PCH_DIM0_TEMP",
+       "PCH_DIM1_TEMP",
+       "PCH_DIM2_TEMP",
+       "PCH_DIM3_TEMP",
+       "BYTE_TEMP"
+};
+
+static const u16 NCT6776_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6776_temp_label) - 1]
+       = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x401, 0x402, 0x404 };
+
+static const u16 NCT6776_REG_TEMP_CRIT[ARRAY_SIZE(nct6776_temp_label) - 1]
+       = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a };
+
+/* NCT6779 specific data */
+
+static const u16 NCT6779_REG_IN[] = {
+       0x480, 0x481, 0x482, 0x483, 0x484, 0x485, 0x486, 0x487,
+       0x488, 0x489, 0x48a, 0x48b, 0x48c, 0x48d, 0x48e };
+
+static const u16 NCT6779_REG_ALARM[NUM_REG_ALARM] = {
+       0x459, 0x45A, 0x45B, 0x568 };
+
+static const s8 NCT6779_ALARM_BITS[] = {
+       0, 1, 2, 3, 8, 21, 20, 16,      /* in0.. in7 */
+       17, 24, 25, 26, 27, 28, 29,     /* in8..in14 */
+       -1,                             /* unused */
+       6, 7, 11, 10, 23,               /* fan1..fan5 */
+       -1, -1, -1,                     /* unused */
+       4, 5, 13, -1, -1, -1,           /* temp1..temp6 */
+       12, 9 };                        /* intrusion0, intrusion1 */
+
+static const u16 NCT6779_REG_FAN[] = { 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8 };
+static const u16 NCT6779_REG_FAN_PULSES[] = {
+       0x644, 0x645, 0x646, 0x647, 0x648 };
+
+static const u16 NCT6779_REG_CRITICAL_PWM_ENABLE[] = {
+       0x136, 0x236, 0x336, 0x836, 0x936 };
+static const u16 NCT6779_REG_CRITICAL_PWM[] = {
+       0x137, 0x237, 0x337, 0x837, 0x937 };
+
+static const u16 NCT6779_REG_TEMP[] = { 0x27, 0x150 };
+static const u16 NCT6779_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6779_REG_TEMP)] = {
+       0x18, 0x152 };
+static const u16 NCT6779_REG_TEMP_HYST[ARRAY_SIZE(NCT6779_REG_TEMP)] = {
+       0x3a, 0x153 };
+static const u16 NCT6779_REG_TEMP_OVER[ARRAY_SIZE(NCT6779_REG_TEMP)] = {
+       0x39, 0x155 };
+
+static const u16 NCT6779_REG_TEMP_OFFSET[] = {
+       0x454, 0x455, 0x456, 0x44a, 0x44b, 0x44c };
+
+static const char *const nct6779_temp_label[] = {
+       "",
+       "SYSTIN",
+       "CPUTIN",
+       "AUXTIN0",
+       "AUXTIN1",
+       "AUXTIN2",
+       "AUXTIN3",
+       "",
+       "SMBUSMASTER 0",
+       "SMBUSMASTER 1",
+       "SMBUSMASTER 2",
+       "SMBUSMASTER 3",
+       "SMBUSMASTER 4",
+       "SMBUSMASTER 5",
+       "SMBUSMASTER 6",
+       "SMBUSMASTER 7",
+       "PECI Agent 0",
+       "PECI Agent 1",
+       "PCH_CHIP_CPU_MAX_TEMP",
+       "PCH_CHIP_TEMP",
+       "PCH_CPU_TEMP",
+       "PCH_MCH_TEMP",
+       "PCH_DIM0_TEMP",
+       "PCH_DIM1_TEMP",
+       "PCH_DIM2_TEMP",
+       "PCH_DIM3_TEMP",
+       "BYTE_TEMP"
+};
+
+static const u16 NCT6779_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6779_temp_label) - 1]
+       = { 0x490, 0x491, 0x492, 0x493, 0x494, 0x495, 0, 0,
+           0, 0, 0, 0, 0, 0, 0, 0,
+           0, 0x400, 0x401, 0x402, 0x404, 0x405, 0x406, 0x407,
+           0x408, 0 };
+
+static const u16 NCT6779_REG_TEMP_CRIT[ARRAY_SIZE(nct6779_temp_label) - 1]
+       = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a };
+
+static enum pwm_enable reg_to_pwm_enable(int pwm, int mode)
+{
+       if (mode == 0 && pwm == 255)
+               return off;
+       return mode + 1;
+}
+
+static int pwm_enable_to_reg(enum pwm_enable mode)
+{
+       if (mode == off)
+               return 0;
+       return mode - 1;
+}
+
+/*
+ * Conversions
+ */
+
+/* 1 is DC mode, output in ms */
+static unsigned int step_time_from_reg(u8 reg, u8 mode)
+{
+       return mode ? 400 * reg : 100 * reg;
+}
+
+static u8 step_time_to_reg(unsigned int msec, u8 mode)
+{
+       return clamp_val((mode ? (msec + 200) / 400 :
+                                       (msec + 50) / 100), 1, 255);
+}
+
+static unsigned int fan_from_reg8(u16 reg, unsigned int divreg)
+{
+       if (reg == 0 || reg == 255)
+               return 0;
+       return 1350000U / (reg << divreg);
+}
+
+static unsigned int fan_from_reg13(u16 reg, unsigned int divreg)
+{
+       if ((reg & 0xff1f) == 0xff1f)
+               return 0;
+
+       reg = (reg & 0x1f) | ((reg & 0xff00) >> 3);
+
+       if (reg == 0)
+               return 0;
+
+       return 1350000U / reg;
+}
+
+static unsigned int fan_from_reg16(u16 reg, unsigned int divreg)
+{
+       if (reg == 0 || reg == 0xffff)
+               return 0;
+
+       /*
+        * Even though the registers are 16 bit wide, the fan divisor
+        * still applies.
+        */
+       return 1350000U / (reg << divreg);
+}
+
+static u16 fan_to_reg(u32 fan, unsigned int divreg)
+{
+       if (!fan)
+               return 0;
+
+       return (1350000U / fan) >> divreg;
+}
+
+static inline unsigned int
+div_from_reg(u8 reg)
+{
+       return 1 << reg;
+}
+
+/*
+ * Some of the voltage inputs have internal scaling, the tables below
+ * contain 8 (the ADC LSB in mV) * scaling factor * 100
+ */
+static const u16 scale_in[15] = {
+       800, 800, 1600, 1600, 800, 800, 800, 1600, 1600, 800, 800, 800, 800,
+       800, 800
+};
+
+static inline long in_from_reg(u8 reg, u8 nr)
+{
+       return DIV_ROUND_CLOSEST(reg * scale_in[nr], 100);
+}
+
+static inline u8 in_to_reg(u32 val, u8 nr)
+{
+       return clamp_val(DIV_ROUND_CLOSEST(val * 100, scale_in[nr]), 0, 255);
+}
+
+/*
+ * Data structures and manipulation thereof
+ */
+
+struct nct6775_data {
+       int addr;       /* IO base of hw monitor block */
+       enum kinds kind;
+       const char *name;
+
+       struct device *hwmon_dev;
+
+       u16 reg_temp[4][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst,
+                                   * 3=temp_crit
+                                   */
+       u8 temp_src[NUM_TEMP];
+       u16 reg_temp_config[NUM_TEMP];
+       const char * const *temp_label;
+       int temp_label_num;
+
+       u16 REG_CONFIG;
+       u16 REG_VBAT;
+       u16 REG_DIODE;
+
+       const s8 *ALARM_BITS;
+
+       const u16 *REG_VIN;
+       const u16 *REG_IN_MINMAX[2];
+
+       const u16 *REG_TARGET;
+       const u16 *REG_FAN;
+       const u16 *REG_FAN_MODE;
+       const u16 *REG_FAN_MIN;
+       const u16 *REG_FAN_PULSES;
+       const u16 *REG_FAN_TIME[3];
+
+       const u16 *REG_TOLERANCE_H;
+
+       const u8 *REG_PWM_MODE;
+       const u8 *PWM_MODE_MASK;
+
+       const u16 *REG_PWM[7];  /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor,
+                                * [3]=pwm_max, [4]=pwm_step,
+                                * [5]=weight_duty_step, [6]=weight_duty_base
+                                */
+       const u16 *REG_PWM_READ;
+
+       const u16 *REG_AUTO_TEMP;
+       const u16 *REG_AUTO_PWM;
+
+       const u16 *REG_CRITICAL_TEMP;
+       const u16 *REG_CRITICAL_TEMP_TOLERANCE;
+
+       const u16 *REG_TEMP_SOURCE;     /* temp register sources */
+       const u16 *REG_TEMP_SEL;
+       const u16 *REG_WEIGHT_TEMP_SEL;
+       const u16 *REG_WEIGHT_TEMP[3];  /* 0=base, 1=tolerance, 2=step */
+
+       const u16 *REG_TEMP_OFFSET;
+
+       const u16 *REG_ALARM;
+
+       unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg);
+       unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg);
+
+       struct mutex update_lock;
+       bool valid;             /* true if following fields are valid */
+       unsigned long last_updated;     /* In jiffies */
+
+       /* Register values */
+       u8 bank;                /* current register bank */
+       u8 in_num;              /* number of in inputs we have */
+       u8 in[15][3];           /* [0]=in, [1]=in_max, [2]=in_min */
+       unsigned int rpm[5];
+       u16 fan_min[5];
+       u8 fan_pulses[5];
+       u8 fan_div[5];
+       u8 has_pwm;
+       u8 has_fan;             /* some fan inputs can be disabled */
+       u8 has_fan_min;         /* some fans don't have min register */
+       bool has_fan_div;
+
+       u8 temp_fixed_num;      /* 3 or 6 */
+       u8 temp_type[NUM_TEMP_FIXED];
+       s8 temp_offset[NUM_TEMP_FIXED];
+       s16 temp[4][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst,
+                               * 3=temp_crit */
+       u64 alarms;
+
+       u8 pwm_num;     /* number of pwm */
+       u8 pwm_mode[5]; /* 1->DC variable voltage, 0->PWM variable duty cycle */
+       enum pwm_enable pwm_enable[5];
+                       /* 0->off
+                        * 1->manual
+                        * 2->thermal cruise mode (also called SmartFan I)
+                        * 3->fan speed cruise mode
+                        * 4->SmartFan III
+                        * 5->enhanced variable thermal cruise (SmartFan IV)
+                        */
+       u8 pwm[7][5];   /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor,
+                        * [3]=pwm_max, [4]=pwm_step,
+                        * [5]=weight_duty_step, [6]=weight_duty_base
+                        */
+
+       u8 target_temp[5];
+       u8 target_temp_mask;
+       u32 target_speed[5];
+       u32 target_speed_tolerance[5];
+       u8 speed_tolerance_limit;
+
+       u8 temp_tolerance[2][5];
+       u8 tolerance_mask;
+
+       u8 fan_time[3][5]; /* 0 = stop_time, 1 = step_up, 2 = step_down */
+
+       /* Automatic fan speed control registers */
+       int auto_pwm_num;
+       u8 auto_pwm[5][7];
+       u8 auto_temp[5][7];
+       u8 pwm_temp_sel[5];
+       u8 pwm_weight_temp_sel[5];
+       u8 weight_temp[3][5];   /* 0->temp_step, 1->temp_step_tol,
+                                * 2->temp_base
+                                */
+
+       u8 vid;
+       u8 vrm;
+
+       u16 have_temp;
+       u16 have_temp_fixed;
+       u16 have_in;
+#ifdef CONFIG_PM
+       /* Remember extra register values over suspend/resume */
+       u8 vbat;
+       u8 fandiv1;
+       u8 fandiv2;
+#endif
+};
+
+struct nct6775_sio_data {
+       int sioreg;
+       enum kinds kind;
+};
+
+static bool is_word_sized(struct nct6775_data *data, u16 reg)
+{
+       switch (data->kind) {
+       case nct6775:
+               return (((reg & 0xff00) == 0x100 ||
+                   (reg & 0xff00) == 0x200) &&
+                  ((reg & 0x00ff) == 0x50 ||
+                   (reg & 0x00ff) == 0x53 ||
+                   (reg & 0x00ff) == 0x55)) ||
+                 (reg & 0xfff0) == 0x630 ||
+                 reg == 0x640 || reg == 0x642 ||
+                 reg == 0x662 ||
+                 ((reg & 0xfff0) == 0x650 && (reg & 0x000f) >= 0x06) ||
+                 reg == 0x73 || reg == 0x75 || reg == 0x77;
+       case nct6776:
+               return (((reg & 0xff00) == 0x100 ||
+                   (reg & 0xff00) == 0x200) &&
+                  ((reg & 0x00ff) == 0x50 ||
+                   (reg & 0x00ff) == 0x53 ||
+                   (reg & 0x00ff) == 0x55)) ||
+                 (reg & 0xfff0) == 0x630 ||
+                 reg == 0x402 ||
+                 reg == 0x640 || reg == 0x642 ||
+                 ((reg & 0xfff0) == 0x650 && (reg & 0x000f) >= 0x06) ||
+                 reg == 0x73 || reg == 0x75 || reg == 0x77;
+       case nct6779:
+               return reg == 0x150 || reg == 0x153 || reg == 0x155 ||
+                 ((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x09) ||
+                 reg == 0x402 ||
+                 reg == 0x63a || reg == 0x63c || reg == 0x63e ||
+                 reg == 0x640 || reg == 0x642 ||
+                 reg == 0x73 || reg == 0x75 || reg == 0x77 || reg == 0x79 ||
+                 reg == 0x7b;
+       }
+       return false;
+}
+
+/*
+ * On older chips, only registers 0x50-0x5f are banked.
+ * On more recent chips, all registers are banked.
+ * Assume that is the case and set the bank number for each access.
+ * Cache the bank number so it only needs to be set if it changes.
+ */
+static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg)
+{
+       u8 bank = reg >> 8;
+       if (data->bank != bank) {
+               outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET);
+               outb_p(bank, data->addr + DATA_REG_OFFSET);
+               data->bank = bank;
+       }
+}
+
+static u16 nct6775_read_value(struct nct6775_data *data, u16 reg)
+{
+       int res, word_sized = is_word_sized(data, reg);
+
+       nct6775_set_bank(data, reg);
+       outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET);
+       res = inb_p(data->addr + DATA_REG_OFFSET);
+       if (word_sized) {
+               outb_p((reg & 0xff) + 1,
+                      data->addr + ADDR_REG_OFFSET);
+               res = (res << 8) + inb_p(data->addr + DATA_REG_OFFSET);
+       }
+       return res;
+}
+
+static int nct6775_write_value(struct nct6775_data *data, u16 reg, u16 value)
+{
+       int word_sized = is_word_sized(data, reg);
+
+       nct6775_set_bank(data, reg);
+       outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET);
+       if (word_sized) {
+               outb_p(value >> 8, data->addr + DATA_REG_OFFSET);
+               outb_p((reg & 0xff) + 1,
+                      data->addr + ADDR_REG_OFFSET);
+       }
+       outb_p(value & 0xff, data->addr + DATA_REG_OFFSET);
+       return 0;
+}
+
+/* We left-align 8-bit temperature values to make the code simpler */
+static u16 nct6775_read_temp(struct nct6775_data *data, u16 reg)
+{
+       u16 res;
+
+       res = nct6775_read_value(data, reg);
+       if (!is_word_sized(data, reg))
+               res <<= 8;
+
+       return res;
+}
+
+static int nct6775_write_temp(struct nct6775_data *data, u16 reg, u16 value)
+{
+       if (!is_word_sized(data, reg))
+               value >>= 8;
+       return nct6775_write_value(data, reg, value);
+}
+
+/* This function assumes that the caller holds data->update_lock */
+static void nct6775_write_fan_div(struct nct6775_data *data, int nr)
+{
+       u8 reg;
+
+       switch (nr) {
+       case 0:
+               reg = (nct6775_read_value(data, NCT6775_REG_FANDIV1) & 0x70)
+                   | (data->fan_div[0] & 0x7);
+               nct6775_write_value(data, NCT6775_REG_FANDIV1, reg);
+               break;
+       case 1:
+               reg = (nct6775_read_value(data, NCT6775_REG_FANDIV1) & 0x7)
+                   | ((data->fan_div[1] << 4) & 0x70);
+               nct6775_write_value(data, NCT6775_REG_FANDIV1, reg);
+               break;
+       case 2:
+               reg = (nct6775_read_value(data, NCT6775_REG_FANDIV2) & 0x70)
+                   | (data->fan_div[2] & 0x7);
+               nct6775_write_value(data, NCT6775_REG_FANDIV2, reg);
+               break;
+       case 3:
+               reg = (nct6775_read_value(data, NCT6775_REG_FANDIV2) & 0x7)
+                   | ((data->fan_div[3] << 4) & 0x70);
+               nct6775_write_value(data, NCT6775_REG_FANDIV2, reg);
+               break;
+       }
+}
+
+static void nct6775_write_fan_div_common(struct nct6775_data *data, int nr)
+{
+       if (data->kind == nct6775)
+               nct6775_write_fan_div(data, nr);
+}
+
+static void nct6775_update_fan_div(struct nct6775_data *data)
+{
+       u8 i;
+
+       i = nct6775_read_value(data, NCT6775_REG_FANDIV1);
+       data->fan_div[0] = i & 0x7;
+       data->fan_div[1] = (i & 0x70) >> 4;
+       i = nct6775_read_value(data, NCT6775_REG_FANDIV2);
+       data->fan_div[2] = i & 0x7;
+       if (data->has_fan & (1 << 3))
+               data->fan_div[3] = (i & 0x70) >> 4;
+}
+
+static void nct6775_update_fan_div_common(struct nct6775_data *data)
+{
+       if (data->kind == nct6775)
+               nct6775_update_fan_div(data);
+}
+
+static void nct6775_init_fan_div(struct nct6775_data *data)
+{
+       int i;
+
+       nct6775_update_fan_div_common(data);
+       /*
+        * For all fans, start with highest divider value if the divider
+        * register is not initialized. This ensures that we get a
+        * reading from the fan count register, even if it is not optimal.
+        * We'll compute a better divider later on.
+        */
+       for (i = 0; i < ARRAY_SIZE(data->fan_div); i++) {
+               if (!(data->has_fan & (1 << i)))
+                       continue;
+               if (data->fan_div[i] == 0) {
+                       data->fan_div[i] = 7;
+                       nct6775_write_fan_div_common(data, i);
+               }
+       }
+}
+
+static void nct6775_init_fan_common(struct device *dev,
+                                   struct nct6775_data *data)
+{
+       int i;
+       u8 reg;
+
+       if (data->has_fan_div)
+               nct6775_init_fan_div(data);
+
+       /*
+        * If fan_min is not set (0), set it to 0xff to disable it. This
+        * prevents the unnecessary warning when fanX_min is reported as 0.
+        */
+       for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) {
+               if (data->has_fan_min & (1 << i)) {
+                       reg = nct6775_read_value(data, data->REG_FAN_MIN[i]);
+                       if (!reg)
+                               nct6775_write_value(data, data->REG_FAN_MIN[i],
+                                                   data->has_fan_div ? 0xff
+                                                                     : 0xff1f);
+               }
+       }
+}
+
+static void nct6775_select_fan_div(struct device *dev,
+                                  struct nct6775_data *data, int nr, u16 reg)
+{
+       u8 fan_div = data->fan_div[nr];
+       u16 fan_min;
+
+       if (!data->has_fan_div)
+               return;
+
+       /*
+        * If we failed to measure the fan speed, or the reported value is not
+        * in the optimal range, and the clock divider can be modified,
+        * let's try that for next time.
+        */
+       if (reg == 0x00 && fan_div < 0x07)
+               fan_div++;
+       else if (reg != 0x00 && reg < 0x30 && fan_div > 0)
+               fan_div--;
+
+       if (fan_div != data->fan_div[nr]) {
+               dev_dbg(dev, "Modifying fan%d clock divider from %u to %u\n",
+                       nr + 1, div_from_reg(data->fan_div[nr]),
+                       div_from_reg(fan_div));
+
+               /* Preserve min limit if possible */
+               if (data->has_fan_min & (1 << nr)) {
+                       fan_min = data->fan_min[nr];
+                       if (fan_div > data->fan_div[nr]) {
+                               if (fan_min != 255 && fan_min > 1)
+                                       fan_min >>= 1;
+                       } else {
+                               if (fan_min != 255) {
+                                       fan_min <<= 1;
+                                       if (fan_min > 254)
+                                               fan_min = 254;
+                               }
+                       }
+                       if (fan_min != data->fan_min[nr]) {
+                               data->fan_min[nr] = fan_min;
+                               nct6775_write_value(data, data->REG_FAN_MIN[nr],
+                                                   fan_min);
+                       }
+               }
+               data->fan_div[nr] = fan_div;
+               nct6775_write_fan_div_common(data, nr);
+       }
+}
+
+static void nct6775_update_pwm(struct device *dev)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       int i, j;
+       int fanmodecfg, reg;
+       bool duty_is_dc;
+
+       for (i = 0; i < data->pwm_num; i++) {
+               if (!(data->has_pwm & (1 << i)))
+                       continue;
+
+               duty_is_dc = data->REG_PWM_MODE[i] &&
+                 (nct6775_read_value(data, data->REG_PWM_MODE[i])
+                  & data->PWM_MODE_MASK[i]);
+               data->pwm_mode[i] = duty_is_dc;
+
+               fanmodecfg = nct6775_read_value(data, data->REG_FAN_MODE[i]);
+               for (j = 0; j < ARRAY_SIZE(data->REG_PWM); j++) {
+                       if (data->REG_PWM[j] && data->REG_PWM[j][i]) {
+                               data->pwm[j][i]
+                                 = nct6775_read_value(data,
+                                                      data->REG_PWM[j][i]);
+                       }
+               }
+
+               data->pwm_enable[i] = reg_to_pwm_enable(data->pwm[0][i],
+                                                       (fanmodecfg >> 4) & 7);
+
+               if (!data->temp_tolerance[0][i] ||
+                   data->pwm_enable[i] != speed_cruise)
+                       data->temp_tolerance[0][i] = fanmodecfg & 0x0f;
+               if (!data->target_speed_tolerance[i] ||
+                   data->pwm_enable[i] == speed_cruise) {
+                       u8 t = fanmodecfg & 0x0f;
+                       if (data->REG_TOLERANCE_H) {
+                               t |= (nct6775_read_value(data,
+                                     data->REG_TOLERANCE_H[i]) & 0x70) >> 1;
+                       }
+                       data->target_speed_tolerance[i] = t;
+               }
+
+               data->temp_tolerance[1][i] =
+                       nct6775_read_value(data,
+                                       data->REG_CRITICAL_TEMP_TOLERANCE[i]);
+
+               reg = nct6775_read_value(data, data->REG_TEMP_SEL[i]);
+               data->pwm_temp_sel[i] = reg & 0x1f;
+               /* If fan can stop, report floor as 0 */
+               if (reg & 0x80)
+                       data->pwm[2][i] = 0;
+
+               reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[i]);
+               data->pwm_weight_temp_sel[i] = reg & 0x1f;
+               /* If weight is disabled, report weight source as 0 */
+               if (j == 1 && !(reg & 0x80))
+                       data->pwm_weight_temp_sel[i] = 0;
+
+               /* Weight temp data */
+               for (j = 0; j < ARRAY_SIZE(data->weight_temp); j++) {
+                       data->weight_temp[j][i]
+                         = nct6775_read_value(data,
+                                              data->REG_WEIGHT_TEMP[j][i]);
+               }
+       }
+}
+
+static void nct6775_update_pwm_limits(struct device *dev)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       int i, j;
+       u8 reg;
+       u16 reg_t;
+
+       for (i = 0; i < data->pwm_num; i++) {
+               if (!(data->has_pwm & (1 << i)))
+                       continue;
+
+               for (j = 0; j < ARRAY_SIZE(data->fan_time); j++) {
+                       data->fan_time[j][i] =
+                         nct6775_read_value(data, data->REG_FAN_TIME[j][i]);
+               }
+
+               reg_t = nct6775_read_value(data, data->REG_TARGET[i]);
+               /* Update only in matching mode or if never updated */
+               if (!data->target_temp[i] ||
+                   data->pwm_enable[i] == thermal_cruise)
+                       data->target_temp[i] = reg_t & data->target_temp_mask;
+               if (!data->target_speed[i] ||
+                   data->pwm_enable[i] == speed_cruise) {
+                       if (data->REG_TOLERANCE_H) {
+                               reg_t |= (nct6775_read_value(data,
+                                       data->REG_TOLERANCE_H[i]) & 0x0f) << 8;
+                       }
+                       data->target_speed[i] = reg_t;
+               }
+
+               for (j = 0; j < data->auto_pwm_num; j++) {
+                       data->auto_pwm[i][j] =
+                         nct6775_read_value(data,
+                                            NCT6775_AUTO_PWM(data, i, j));
+                       data->auto_temp[i][j] =
+                         nct6775_read_value(data,
+                                            NCT6775_AUTO_TEMP(data, i, j));
+               }
+
+               /* critical auto_pwm temperature data */
+               data->auto_temp[i][data->auto_pwm_num] =
+                       nct6775_read_value(data, data->REG_CRITICAL_TEMP[i]);
+
+               switch (data->kind) {
+               case nct6775:
+                       reg = nct6775_read_value(data,
+                                                NCT6775_REG_CRITICAL_ENAB[i]);
+                       data->auto_pwm[i][data->auto_pwm_num] =
+                                               (reg & 0x02) ? 0xff : 0x00;
+                       break;
+               case nct6776:
+                       data->auto_pwm[i][data->auto_pwm_num] = 0xff;
+                       break;
+               case nct6779:
+                       reg = nct6775_read_value(data,
+                                       NCT6779_REG_CRITICAL_PWM_ENABLE[i]);
+                       if (reg & 1)
+                               data->auto_pwm[i][data->auto_pwm_num] =
+                                 nct6775_read_value(data,
+                                       NCT6779_REG_CRITICAL_PWM[i]);
+                       else
+                               data->auto_pwm[i][data->auto_pwm_num] = 0xff;
+                       break;
+               }
+       }
+}
+
+static struct nct6775_data *nct6775_update_device(struct device *dev)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       int i, j;
+
+       mutex_lock(&data->update_lock);
+
+       if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+           || !data->valid) {
+               /* Fan clock dividers */
+               nct6775_update_fan_div_common(data);
+
+               /* Measured voltages and limits */
+               for (i = 0; i < data->in_num; i++) {
+                       if (!(data->have_in & (1 << i)))
+                               continue;
+
+                       data->in[i][0] = nct6775_read_value(data,
+                                                           data->REG_VIN[i]);
+                       data->in[i][1] = nct6775_read_value(data,
+                                         data->REG_IN_MINMAX[0][i]);
+                       data->in[i][2] = nct6775_read_value(data,
+                                         data->REG_IN_MINMAX[1][i]);
+               }
+
+               /* Measured fan speeds and limits */
+               for (i = 0; i < ARRAY_SIZE(data->rpm); i++) {
+                       u16 reg;
+
+                       if (!(data->has_fan & (1 << i)))
+                               continue;
+
+                       reg = nct6775_read_value(data, data->REG_FAN[i]);
+                       data->rpm[i] = data->fan_from_reg(reg,
+                                                         data->fan_div[i]);
+
+                       if (data->has_fan_min & (1 << i))
+                               data->fan_min[i] = nct6775_read_value(data,
+                                          data->REG_FAN_MIN[i]);
+                       data->fan_pulses[i] =
+                         nct6775_read_value(data, data->REG_FAN_PULSES[i]);
+
+                       nct6775_select_fan_div(dev, data, i, reg);
+               }
+
+               nct6775_update_pwm(dev);
+               nct6775_update_pwm_limits(dev);
+
+               /* Measured temperatures and limits */
+               for (i = 0; i < NUM_TEMP; i++) {
+                       if (!(data->have_temp & (1 << i)))
+                               continue;
+                       for (j = 0; j < ARRAY_SIZE(data->reg_temp); j++) {
+                               if (data->reg_temp[j][i])
+                                       data->temp[j][i]
+                                         = nct6775_read_temp(data,
+                                               data->reg_temp[j][i]);
+                       }
+                       if (!(data->have_temp_fixed & (1 << i)))
+                               continue;
+                       data->temp_offset[i]
+                         = nct6775_read_value(data, data->REG_TEMP_OFFSET[i]);
+               }
+
+               data->alarms = 0;
+               for (i = 0; i < NUM_REG_ALARM; i++) {
+                       u8 alarm;
+                       if (!data->REG_ALARM[i])
+                               continue;
+                       alarm = nct6775_read_value(data, data->REG_ALARM[i]);
+                       data->alarms |= ((u64)alarm) << (i << 3);
+               }
+
+               data->last_updated = jiffies;
+               data->valid = true;
+       }
+
+       mutex_unlock(&data->update_lock);
+       return data;
+}
+
+/*
+ * Sysfs callback functions
+ */
+static ssize_t
+show_in_reg(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+       int nr = sattr->nr;
+       int index = sattr->index;
+       return sprintf(buf, "%ld\n", in_from_reg(data->in[nr][index], nr));
+}
+
+static ssize_t
+store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf,
+            size_t count)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+       int nr = sattr->nr;
+       int index = sattr->index;
+       unsigned long val;
+       int err = kstrtoul(buf, 10, &val);
+       if (err < 0)
+               return err;
+       mutex_lock(&data->update_lock);
+       data->in[nr][index] = in_to_reg(val, nr);
+       nct6775_write_value(data, data->REG_IN_MINMAX[index - 1][nr],
+                           data->in[nr][index]);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t
+show_alarm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int nr = data->ALARM_BITS[sattr->index];
+       return sprintf(buf, "%u\n",
+                      (unsigned int)((data->alarms >> nr) & 0x01));
+}
+
+static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in_reg, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in_reg, NULL, 1, 0);
+static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in_reg, NULL, 2, 0);
+static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in_reg, NULL, 3, 0);
+static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in_reg, NULL, 4, 0);
+static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, show_in_reg, NULL, 5, 0);
+static SENSOR_DEVICE_ATTR_2(in6_input, S_IRUGO, show_in_reg, NULL, 6, 0);
+static SENSOR_DEVICE_ATTR_2(in7_input, S_IRUGO, show_in_reg, NULL, 7, 0);
+static SENSOR_DEVICE_ATTR_2(in8_input, S_IRUGO, show_in_reg, NULL, 8, 0);
+static SENSOR_DEVICE_ATTR_2(in9_input, S_IRUGO, show_in_reg, NULL, 9, 0);
+static SENSOR_DEVICE_ATTR_2(in10_input, S_IRUGO, show_in_reg, NULL, 10, 0);
+static SENSOR_DEVICE_ATTR_2(in11_input, S_IRUGO, show_in_reg, NULL, 11, 0);
+static SENSOR_DEVICE_ATTR_2(in12_input, S_IRUGO, show_in_reg, NULL, 12, 0);
+static SENSOR_DEVICE_ATTR_2(in13_input, S_IRUGO, show_in_reg, NULL, 13, 0);
+static SENSOR_DEVICE_ATTR_2(in14_input, S_IRUGO, show_in_reg, NULL, 14, 0);
+
+static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 5);
+static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 7);
+static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 8);
+static SENSOR_DEVICE_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, 9);
+static SENSOR_DEVICE_ATTR(in10_alarm, S_IRUGO, show_alarm, NULL, 10);
+static SENSOR_DEVICE_ATTR(in11_alarm, S_IRUGO, show_alarm, NULL, 11);
+static SENSOR_DEVICE_ATTR(in12_alarm, S_IRUGO, show_alarm, NULL, 12);
+static SENSOR_DEVICE_ATTR(in13_alarm, S_IRUGO, show_alarm, NULL, 13);
+static SENSOR_DEVICE_ATTR(in14_alarm, S_IRUGO, show_alarm, NULL, 14);
+
+static SENSOR_DEVICE_ATTR_2(in0_min, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 0, 1);
+static SENSOR_DEVICE_ATTR_2(in1_min, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 1, 1);
+static SENSOR_DEVICE_ATTR_2(in2_min, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 2, 1);
+static SENSOR_DEVICE_ATTR_2(in3_min, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 3, 1);
+static SENSOR_DEVICE_ATTR_2(in4_min, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 4, 1);
+static SENSOR_DEVICE_ATTR_2(in5_min, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 5, 1);
+static SENSOR_DEVICE_ATTR_2(in6_min, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 6, 1);
+static SENSOR_DEVICE_ATTR_2(in7_min, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 7, 1);
+static SENSOR_DEVICE_ATTR_2(in8_min, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 8, 1);
+static SENSOR_DEVICE_ATTR_2(in9_min, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 9, 1);
+static SENSOR_DEVICE_ATTR_2(in10_min, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 10, 1);
+static SENSOR_DEVICE_ATTR_2(in11_min, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 11, 1);
+static SENSOR_DEVICE_ATTR_2(in12_min, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 12, 1);
+static SENSOR_DEVICE_ATTR_2(in13_min, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 13, 1);
+static SENSOR_DEVICE_ATTR_2(in14_min, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 14, 1);
+
+static SENSOR_DEVICE_ATTR_2(in0_max, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 0, 2);
+static SENSOR_DEVICE_ATTR_2(in1_max, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 1, 2);
+static SENSOR_DEVICE_ATTR_2(in2_max, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 2, 2);
+static SENSOR_DEVICE_ATTR_2(in3_max, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 3, 2);
+static SENSOR_DEVICE_ATTR_2(in4_max, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 4, 2);
+static SENSOR_DEVICE_ATTR_2(in5_max, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 5, 2);
+static SENSOR_DEVICE_ATTR_2(in6_max, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 6, 2);
+static SENSOR_DEVICE_ATTR_2(in7_max, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 7, 2);
+static SENSOR_DEVICE_ATTR_2(in8_max, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 8, 2);
+static SENSOR_DEVICE_ATTR_2(in9_max, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 9, 2);
+static SENSOR_DEVICE_ATTR_2(in10_max, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 10, 2);
+static SENSOR_DEVICE_ATTR_2(in11_max, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 11, 2);
+static SENSOR_DEVICE_ATTR_2(in12_max, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 12, 2);
+static SENSOR_DEVICE_ATTR_2(in13_max, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 13, 2);
+static SENSOR_DEVICE_ATTR_2(in14_max, S_IWUSR | S_IRUGO, show_in_reg,
+                           store_in_reg, 14, 2);
+
+static struct attribute *nct6775_attributes_in[15][5] = {
+       {
+               &sensor_dev_attr_in0_input.dev_attr.attr,
+               &sensor_dev_attr_in0_min.dev_attr.attr,
+               &sensor_dev_attr_in0_max.dev_attr.attr,
+               &sensor_dev_attr_in0_alarm.dev_attr.attr,
+               NULL
+       },
+       {
+               &sensor_dev_attr_in1_input.dev_attr.attr,
+               &sensor_dev_attr_in1_min.dev_attr.attr,
+               &sensor_dev_attr_in1_max.dev_attr.attr,
+               &sensor_dev_attr_in1_alarm.dev_attr.attr,
+               NULL
+       },
+       {
+               &sensor_dev_attr_in2_input.dev_attr.attr,
+               &sensor_dev_attr_in2_min.dev_attr.attr,
+               &sensor_dev_attr_in2_max.dev_attr.attr,
+               &sensor_dev_attr_in2_alarm.dev_attr.attr,
+               NULL
+       },
+       {
+               &sensor_dev_attr_in3_input.dev_attr.attr,
+               &sensor_dev_attr_in3_min.dev_attr.attr,
+               &sensor_dev_attr_in3_max.dev_attr.attr,
+               &sensor_dev_attr_in3_alarm.dev_attr.attr,
+               NULL
+       },
+       {
+               &sensor_dev_attr_in4_input.dev_attr.attr,
+               &sensor_dev_attr_in4_min.dev_attr.attr,
+               &sensor_dev_attr_in4_max.dev_attr.attr,
+               &sensor_dev_attr_in4_alarm.dev_attr.attr,
+               NULL
+       },
+       {
+               &sensor_dev_attr_in5_input.dev_attr.attr,
+               &sensor_dev_attr_in5_min.dev_attr.attr,
+               &sensor_dev_attr_in5_max.dev_attr.attr,
+               &sensor_dev_attr_in5_alarm.dev_attr.attr,
+               NULL
+       },
+       {
+               &sensor_dev_attr_in6_input.dev_attr.attr,
+               &sensor_dev_attr_in6_min.dev_attr.attr,
+               &sensor_dev_attr_in6_max.dev_attr.attr,
+               &sensor_dev_attr_in6_alarm.dev_attr.attr,
+               NULL
+       },
+       {
+               &sensor_dev_attr_in7_input.dev_attr.attr,
+               &sensor_dev_attr_in7_min.dev_attr.attr,
+               &sensor_dev_attr_in7_max.dev_attr.attr,
+               &sensor_dev_attr_in7_alarm.dev_attr.attr,
+               NULL
+       },
+       {
+               &sensor_dev_attr_in8_input.dev_attr.attr,
+               &sensor_dev_attr_in8_min.dev_attr.attr,
+               &sensor_dev_attr_in8_max.dev_attr.attr,
+               &sensor_dev_attr_in8_alarm.dev_attr.attr,
+               NULL
+       },
+       {
+               &sensor_dev_attr_in9_input.dev_attr.attr,
+               &sensor_dev_attr_in9_min.dev_attr.attr,
+               &sensor_dev_attr_in9_max.dev_attr.attr,
+               &sensor_dev_attr_in9_alarm.dev_attr.attr,
+               NULL
+       },
+       {
+               &sensor_dev_attr_in10_input.dev_attr.attr,
+               &sensor_dev_attr_in10_min.dev_attr.attr,
+               &sensor_dev_attr_in10_max.dev_attr.attr,
+               &sensor_dev_attr_in10_alarm.dev_attr.attr,
+               NULL
+       },
+       {
+               &sensor_dev_attr_in11_input.dev_attr.attr,
+               &sensor_dev_attr_in11_min.dev_attr.attr,
+               &sensor_dev_attr_in11_max.dev_attr.attr,
+               &sensor_dev_attr_in11_alarm.dev_attr.attr,
+               NULL
+       },
+       {
+               &sensor_dev_attr_in12_input.dev_attr.attr,
+               &sensor_dev_attr_in12_min.dev_attr.attr,
+               &sensor_dev_attr_in12_max.dev_attr.attr,
+               &sensor_dev_attr_in12_alarm.dev_attr.attr,
+               NULL
+       },
+       {
+               &sensor_dev_attr_in13_input.dev_attr.attr,
+               &sensor_dev_attr_in13_min.dev_attr.attr,
+               &sensor_dev_attr_in13_max.dev_attr.attr,
+               &sensor_dev_attr_in13_alarm.dev_attr.attr,
+               NULL
+       },
+       {
+               &sensor_dev_attr_in14_input.dev_attr.attr,
+               &sensor_dev_attr_in14_min.dev_attr.attr,
+               &sensor_dev_attr_in14_max.dev_attr.attr,
+               &sensor_dev_attr_in14_alarm.dev_attr.attr,
+               NULL
+       },
+};
+
+static const struct attribute_group nct6775_group_in[15] = {
+       { .attrs = nct6775_attributes_in[0] },
+       { .attrs = nct6775_attributes_in[1] },
+       { .attrs = nct6775_attributes_in[2] },
+       { .attrs = nct6775_attributes_in[3] },
+       { .attrs = nct6775_attributes_in[4] },
+       { .attrs = nct6775_attributes_in[5] },
+       { .attrs = nct6775_attributes_in[6] },
+       { .attrs = nct6775_attributes_in[7] },
+       { .attrs = nct6775_attributes_in[8] },
+       { .attrs = nct6775_attributes_in[9] },
+       { .attrs = nct6775_attributes_in[10] },
+       { .attrs = nct6775_attributes_in[11] },
+       { .attrs = nct6775_attributes_in[12] },
+       { .attrs = nct6775_attributes_in[13] },
+       { .attrs = nct6775_attributes_in[14] },
+};
+
+static ssize_t
+show_fan(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int nr = sattr->index;
+       return sprintf(buf, "%d\n", data->rpm[nr]);
+}
+
+static ssize_t
+show_fan_min(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int nr = sattr->index;
+       return sprintf(buf, "%d\n",
+                      data->fan_from_reg_min(data->fan_min[nr],
+                                             data->fan_div[nr]));
+}
+
+static ssize_t
+show_fan_div(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int nr = sattr->index;
+       return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr]));
+}
+
+static ssize_t
+store_fan_min(struct device *dev, struct device_attribute *attr,
+             const char *buf, size_t count)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int nr = sattr->index;
+       unsigned long val;
+       int err;
+       unsigned int reg;
+       u8 new_div;
+
+       err = kstrtoul(buf, 10, &val);
+       if (err < 0)
+               return err;
+
+       mutex_lock(&data->update_lock);
+       if (!data->has_fan_div) {
+               /* NCT6776F or NCT6779D; we know this is a 13 bit register */
+               if (!val) {
+                       val = 0xff1f;
+               } else {
+                       if (val > 1350000U)
+                               val = 135000U;
+                       val = 1350000U / val;
+                       val = (val & 0x1f) | ((val << 3) & 0xff00);
+               }
+               data->fan_min[nr] = val;
+               goto write_min; /* Leave fan divider alone */
+       }
+       if (!val) {
+               /* No min limit, alarm disabled */
+               data->fan_min[nr] = 255;
+               new_div = data->fan_div[nr]; /* No change */
+               dev_info(dev, "fan%u low limit and alarm disabled\n", nr + 1);
+               goto write_div;
+       }
+       reg = 1350000U / val;
+       if (reg >= 128 * 255) {
+               /*
+                * Speed below this value cannot possibly be represented,
+                * even with the highest divider (128)
+                */
+               data->fan_min[nr] = 254;
+               new_div = 7; /* 128 == (1 << 7) */
+               dev_warn(dev,
+                        "fan%u low limit %lu below minimum %u, set to minimum\n",
+                        nr + 1, val, data->fan_from_reg_min(254, 7));
+       } else if (!reg) {
+               /*
+                * Speed above this value cannot possibly be represented,
+                * even with the lowest divider (1)
+                */
+               data->fan_min[nr] = 1;
+               new_div = 0; /* 1 == (1 << 0) */
+               dev_warn(dev,
+                        "fan%u low limit %lu above maximum %u, set to maximum\n",
+                        nr + 1, val, data->fan_from_reg_min(1, 0));
+       } else {
+               /*
+                * Automatically pick the best divider, i.e. the one such
+                * that the min limit will correspond to a register value
+                * in the 96..192 range
+                */
+               new_div = 0;
+               while (reg > 192 && new_div < 7) {
+                       reg >>= 1;
+                       new_div++;
+               }
+               data->fan_min[nr] = reg;
+       }
+
+write_div:
+       /*
+        * Write both the fan clock divider (if it changed) and the new
+        * fan min (unconditionally)
+        */
+       if (new_div != data->fan_div[nr]) {
+               dev_dbg(dev, "fan%u clock divider changed from %u to %u\n",
+                       nr + 1, div_from_reg(data->fan_div[nr]),
+                       div_from_reg(new_div));
+               data->fan_div[nr] = new_div;
+               nct6775_write_fan_div_common(data, nr);
+               /* Give the chip time to sample a new speed value */
+               data->last_updated = jiffies;
+       }
+
+write_min:
+       nct6775_write_value(data, data->REG_FAN_MIN[nr], data->fan_min[nr]);
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t
+show_fan_pulses(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int p = data->fan_pulses[sattr->index];
+
+       return sprintf(buf, "%d\n", p ? : 4);
+}
+
+static ssize_t
+store_fan_pulses(struct device *dev, struct device_attribute *attr,
+                const char *buf, size_t count)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int nr = sattr->index;
+       unsigned long val;
+       int err;
+
+       err = kstrtoul(buf, 10, &val);
+       if (err < 0)
+               return err;
+
+       if (val > 4)
+               return -EINVAL;
+
+       mutex_lock(&data->update_lock);
+       data->fan_pulses[nr] = val & 3;
+       nct6775_write_value(data, data->REG_FAN_PULSES[nr], val & 3);
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static struct sensor_device_attribute sda_fan_input[] = {
+       SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0),
+       SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1),
+       SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2),
+       SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3),
+       SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4),
+};
+
+static struct sensor_device_attribute sda_fan_alarm[] = {
+       SENSOR_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE),
+       SENSOR_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 1),
+       SENSOR_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 2),
+       SENSOR_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 3),
+       SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 4),
+};
+
+static struct sensor_device_attribute sda_fan_min[] = {
+       SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
+                   store_fan_min, 0),
+       SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min,
+                   store_fan_min, 1),
+       SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min,
+                   store_fan_min, 2),
+       SENSOR_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min,
+                   store_fan_min, 3),
+       SENSOR_ATTR(fan5_min, S_IWUSR | S_IRUGO, show_fan_min,
+                   store_fan_min, 4),
+};
+
+static struct sensor_device_attribute sda_fan_pulses[] = {
+       SENSOR_ATTR(fan1_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
+                   store_fan_pulses, 0),
+       SENSOR_ATTR(fan2_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
+                   store_fan_pulses, 1),
+       SENSOR_ATTR(fan3_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
+                   store_fan_pulses, 2),
+       SENSOR_ATTR(fan4_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
+                   store_fan_pulses, 3),
+       SENSOR_ATTR(fan5_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
+                   store_fan_pulses, 4),
+};
+
+static struct sensor_device_attribute sda_fan_div[] = {
+       SENSOR_ATTR(fan1_div, S_IRUGO, show_fan_div, NULL, 0),
+       SENSOR_ATTR(fan2_div, S_IRUGO, show_fan_div, NULL, 1),
+       SENSOR_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2),
+       SENSOR_ATTR(fan4_div, S_IRUGO, show_fan_div, NULL, 3),
+       SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4),
+};
+
+static ssize_t
+show_temp_label(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int nr = sattr->index;
+       return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]);
+}
+
+static ssize_t
+show_temp(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+       int nr = sattr->nr;
+       int index = sattr->index;
+
+       return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->temp[index][nr]));
+}
+
+static ssize_t
+store_temp(struct device *dev, struct device_attribute *attr, const char *buf,
+          size_t count)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+       int nr = sattr->nr;
+       int index = sattr->index;
+       int err;
+       long val;
+
+       err = kstrtol(buf, 10, &val);
+       if (err < 0)
+               return err;
+
+       mutex_lock(&data->update_lock);
+       data->temp[index][nr] = LM75_TEMP_TO_REG(val);
+       nct6775_write_temp(data, data->reg_temp[index][nr],
+                          data->temp[index][nr]);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t
+show_temp_offset(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+
+       return sprintf(buf, "%d\n", data->temp_offset[sattr->index] * 1000);
+}
+
+static ssize_t
+store_temp_offset(struct device *dev, struct device_attribute *attr,
+                 const char *buf, size_t count)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int nr = sattr->index;
+       long val;
+       int err;
+
+       err = kstrtol(buf, 10, &val);
+       if (err < 0)
+               return err;
+
+       val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127);
+
+       mutex_lock(&data->update_lock);
+       data->temp_offset[nr] = val;
+       nct6775_write_value(data, data->REG_TEMP_OFFSET[nr], val);
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t
+show_temp_type(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int nr = sattr->index;
+       return sprintf(buf, "%d\n", (int)data->temp_type[nr]);
+}
+
+static ssize_t
+store_temp_type(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int nr = sattr->index;
+       unsigned long val;
+       int err;
+       u8 vbat, diode, bit;
+
+       err = kstrtoul(buf, 10, &val);
+       if (err < 0)
+               return err;
+
+       if (val != 1 && val != 3 && val != 4)
+               return -EINVAL;
+
+       mutex_lock(&data->update_lock);
+
+       data->temp_type[nr] = val;
+       vbat = nct6775_read_value(data, data->REG_VBAT) & ~(0x02 << nr);
+       diode = nct6775_read_value(data, data->REG_DIODE) & ~(0x02 << nr);
+       bit = 0x02 << nr;
+       switch (val) {
+       case 1: /* CPU diode (diode, current mode) */
+               vbat |= bit;
+               diode |= bit;
+               break;
+       case 3: /* diode, voltage mode */
+               vbat |= bit;
+               break;
+       case 4: /* thermistor */
+               break;
+       }
+       nct6775_write_value(data, data->REG_VBAT, vbat);
+       nct6775_write_value(data, data->REG_DIODE, diode);
+
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static struct sensor_device_attribute_2 sda_temp_input[] = {
+       SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0),
+       SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 1, 0),
+       SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 2, 0),
+       SENSOR_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 3, 0),
+       SENSOR_ATTR_2(temp5_input, S_IRUGO, show_temp, NULL, 4, 0),
+       SENSOR_ATTR_2(temp6_input, S_IRUGO, show_temp, NULL, 5, 0),
+       SENSOR_ATTR_2(temp7_input, S_IRUGO, show_temp, NULL, 6, 0),
+       SENSOR_ATTR_2(temp8_input, S_IRUGO, show_temp, NULL, 7, 0),
+       SENSOR_ATTR_2(temp9_input, S_IRUGO, show_temp, NULL, 8, 0),
+       SENSOR_ATTR_2(temp10_input, S_IRUGO, show_temp, NULL, 9, 0),
+};
+
+static struct sensor_device_attribute sda_temp_label[] = {
+       SENSOR_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL, 0),
+       SENSOR_ATTR(temp2_label, S_IRUGO, show_temp_label, NULL, 1),
+       SENSOR_ATTR(temp3_label, S_IRUGO, show_temp_label, NULL, 2),
+       SENSOR_ATTR(temp4_label, S_IRUGO, show_temp_label, NULL, 3),
+       SENSOR_ATTR(temp5_label, S_IRUGO, show_temp_label, NULL, 4),
+       SENSOR_ATTR(temp6_label, S_IRUGO, show_temp_label, NULL, 5),
+       SENSOR_ATTR(temp7_label, S_IRUGO, show_temp_label, NULL, 6),
+       SENSOR_ATTR(temp8_label, S_IRUGO, show_temp_label, NULL, 7),
+       SENSOR_ATTR(temp9_label, S_IRUGO, show_temp_label, NULL, 8),
+       SENSOR_ATTR(temp10_label, S_IRUGO, show_temp_label, NULL, 9),
+};
+
+static struct sensor_device_attribute_2 sda_temp_max[] = {
+       SENSOR_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     0, 1),
+       SENSOR_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     1, 1),
+       SENSOR_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     2, 1),
+       SENSOR_ATTR_2(temp4_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     3, 1),
+       SENSOR_ATTR_2(temp5_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     4, 1),
+       SENSOR_ATTR_2(temp6_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     5, 1),
+       SENSOR_ATTR_2(temp7_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     6, 1),
+       SENSOR_ATTR_2(temp8_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     7, 1),
+       SENSOR_ATTR_2(temp9_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     8, 1),
+       SENSOR_ATTR_2(temp10_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     9, 1),
+};
+
+static struct sensor_device_attribute_2 sda_temp_max_hyst[] = {
+       SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     0, 2),
+       SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     1, 2),
+       SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     2, 2),
+       SENSOR_ATTR_2(temp4_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     3, 2),
+       SENSOR_ATTR_2(temp5_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     4, 2),
+       SENSOR_ATTR_2(temp6_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     5, 2),
+       SENSOR_ATTR_2(temp7_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     6, 2),
+       SENSOR_ATTR_2(temp8_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     7, 2),
+       SENSOR_ATTR_2(temp9_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     8, 2),
+       SENSOR_ATTR_2(temp10_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     9, 2),
+};
+
+static struct sensor_device_attribute_2 sda_temp_crit[] = {
+       SENSOR_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     0, 3),
+       SENSOR_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     1, 3),
+       SENSOR_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     2, 3),
+       SENSOR_ATTR_2(temp4_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     3, 3),
+       SENSOR_ATTR_2(temp5_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     4, 3),
+       SENSOR_ATTR_2(temp6_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     5, 3),
+       SENSOR_ATTR_2(temp7_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     6, 3),
+       SENSOR_ATTR_2(temp8_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     7, 3),
+       SENSOR_ATTR_2(temp9_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     8, 3),
+       SENSOR_ATTR_2(temp10_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
+                     9, 3),
+};
+
+static struct sensor_device_attribute sda_temp_offset[] = {
+       SENSOR_ATTR(temp1_offset, S_IRUGO | S_IWUSR, show_temp_offset,
+                   store_temp_offset, 0),
+       SENSOR_ATTR(temp2_offset, S_IRUGO | S_IWUSR, show_temp_offset,
+                   store_temp_offset, 1),
+       SENSOR_ATTR(temp3_offset, S_IRUGO | S_IWUSR, show_temp_offset,
+                   store_temp_offset, 2),
+       SENSOR_ATTR(temp4_offset, S_IRUGO | S_IWUSR, show_temp_offset,
+                   store_temp_offset, 3),
+       SENSOR_ATTR(temp5_offset, S_IRUGO | S_IWUSR, show_temp_offset,
+                   store_temp_offset, 4),
+       SENSOR_ATTR(temp6_offset, S_IRUGO | S_IWUSR, show_temp_offset,
+                   store_temp_offset, 5),
+};
+
+static struct sensor_device_attribute sda_temp_type[] = {
+       SENSOR_ATTR(temp1_type, S_IRUGO | S_IWUSR, show_temp_type,
+                   store_temp_type, 0),
+       SENSOR_ATTR(temp2_type, S_IRUGO | S_IWUSR, show_temp_type,
+                   store_temp_type, 1),
+       SENSOR_ATTR(temp3_type, S_IRUGO | S_IWUSR, show_temp_type,
+                   store_temp_type, 2),
+       SENSOR_ATTR(temp4_type, S_IRUGO | S_IWUSR, show_temp_type,
+                   store_temp_type, 3),
+       SENSOR_ATTR(temp5_type, S_IRUGO | S_IWUSR, show_temp_type,
+                   store_temp_type, 4),
+       SENSOR_ATTR(temp6_type, S_IRUGO | S_IWUSR, show_temp_type,
+                   store_temp_type, 5),
+};
+
+static struct sensor_device_attribute sda_temp_alarm[] = {
+       SENSOR_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL,
+                   TEMP_ALARM_BASE),
+       SENSOR_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL,
+                   TEMP_ALARM_BASE + 1),
+       SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL,
+                   TEMP_ALARM_BASE + 2),
+       SENSOR_ATTR(temp4_alarm, S_IRUGO, show_alarm, NULL,
+                   TEMP_ALARM_BASE + 3),
+       SENSOR_ATTR(temp5_alarm, S_IRUGO, show_alarm, NULL,
+                   TEMP_ALARM_BASE + 4),
+       SENSOR_ATTR(temp6_alarm, S_IRUGO, show_alarm, NULL,
+                   TEMP_ALARM_BASE + 5),
+};
+
+#define NUM_TEMP_ALARM ARRAY_SIZE(sda_temp_alarm)
+
+static ssize_t
+show_pwm_mode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+
+       return sprintf(buf, "%d\n", !data->pwm_mode[sattr->index]);
+}
+
+static ssize_t
+store_pwm_mode(struct device *dev, struct device_attribute *attr,
+              const char *buf, size_t count)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int nr = sattr->index;
+       unsigned long val;
+       int err;
+       u8 reg;
+
+       err = kstrtoul(buf, 10, &val);
+       if (err < 0)
+               return err;
+
+       if (val > 1)
+               return -EINVAL;
+
+       /* Setting DC mode is not supported for all chips/channels */
+       if (data->REG_PWM_MODE[nr] == 0) {
+               if (val)
+                       return -EINVAL;
+               return count;
+       }
+
+       mutex_lock(&data->update_lock);
+       data->pwm_mode[nr] = val;
+       reg = nct6775_read_value(data, data->REG_PWM_MODE[nr]);
+       reg &= ~data->PWM_MODE_MASK[nr];
+       if (val)
+               reg |= data->PWM_MODE_MASK[nr];
+       nct6775_write_value(data, data->REG_PWM_MODE[nr], reg);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t
+show_pwm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+       int nr = sattr->nr;
+       int index = sattr->index;
+       int pwm;
+
+       /*
+        * For automatic fan control modes, show current pwm readings.
+        * Otherwise, show the configured value.
+        */
+       if (index == 0 && data->pwm_enable[nr] > manual)
+               pwm = nct6775_read_value(data, data->REG_PWM_READ[nr]);
+       else
+               pwm = data->pwm[index][nr];
+
+       return sprintf(buf, "%d\n", pwm);
+}
+
+static ssize_t
+store_pwm(struct device *dev, struct device_attribute *attr, const char *buf,
+         size_t count)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+       int nr = sattr->nr;
+       int index = sattr->index;
+       unsigned long val;
+       int minval[7] = { 0, 1, 1, data->pwm[2][nr], 0, 0, 0 };
+       int maxval[7]
+         = { 255, 255, data->pwm[3][nr] ? : 255, 255, 255, 255, 255 };
+       int err;
+       u8 reg;
+
+       err = kstrtoul(buf, 10, &val);
+       if (err < 0)
+               return err;
+       val = clamp_val(val, minval[index], maxval[index]);
+
+       mutex_lock(&data->update_lock);
+       data->pwm[index][nr] = val;
+       nct6775_write_value(data, data->REG_PWM[index][nr], val);
+       if (index == 2) { /* floor: disable if val == 0 */
+               reg = nct6775_read_value(data, data->REG_TEMP_SEL[nr]);
+               reg &= 0x7f;
+               if (val)
+                       reg |= 0x80;
+               nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg);
+       }
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+/* Returns 0 if OK, -EINVAL otherwise */
+static int check_trip_points(struct nct6775_data *data, int nr)
+{
+       int i;
+
+       for (i = 0; i < data->auto_pwm_num - 1; i++) {
+               if (data->auto_temp[nr][i] > data->auto_temp[nr][i + 1])
+                       return -EINVAL;
+       }
+       for (i = 0; i < data->auto_pwm_num - 1; i++) {
+               if (data->auto_pwm[nr][i] > data->auto_pwm[nr][i + 1])
+                       return -EINVAL;
+       }
+       /* validate critical temperature and pwm if enabled (pwm > 0) */
+       if (data->auto_pwm[nr][data->auto_pwm_num]) {
+               if (data->auto_temp[nr][data->auto_pwm_num - 1] >
+                               data->auto_temp[nr][data->auto_pwm_num] ||
+                   data->auto_pwm[nr][data->auto_pwm_num - 1] >
+                               data->auto_pwm[nr][data->auto_pwm_num])
+                       return -EINVAL;
+       }
+       return 0;
+}
+
+static void pwm_update_registers(struct nct6775_data *data, int nr)
+{
+       u8 reg;
+
+       switch (data->pwm_enable[nr]) {
+       case off:
+       case manual:
+               break;
+       case speed_cruise:
+               reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]);
+               reg = (reg & ~data->tolerance_mask) |
+                 (data->target_speed_tolerance[nr] & data->tolerance_mask);
+               nct6775_write_value(data, data->REG_FAN_MODE[nr], reg);
+               nct6775_write_value(data, data->REG_TARGET[nr],
+                                   data->target_speed[nr] & 0xff);
+               if (data->REG_TOLERANCE_H) {
+                       reg = (data->target_speed[nr] >> 8) & 0x0f;
+                       reg |= (data->target_speed_tolerance[nr] & 0x38) << 1;
+                       nct6775_write_value(data,
+                                           data->REG_TOLERANCE_H[nr],
+                                           reg);
+               }
+               break;
+       case thermal_cruise:
+               nct6775_write_value(data, data->REG_TARGET[nr],
+                                   data->target_temp[nr]);
+               /* intentional */
+       default:
+               reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]);
+               reg = (reg & ~data->tolerance_mask) |
+                 data->temp_tolerance[0][nr];
+               nct6775_write_value(data, data->REG_FAN_MODE[nr], reg);
+               break;
+       }
+}
+
+static ssize_t
+show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+
+       return sprintf(buf, "%d\n", data->pwm_enable[sattr->index]);
+}
+
+static ssize_t
+store_pwm_enable(struct device *dev, struct device_attribute *attr,
+                const char *buf, size_t count)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int nr = sattr->index;
+       unsigned long val;
+       int err;
+       u16 reg;
+
+       err = kstrtoul(buf, 10, &val);
+       if (err < 0)
+               return err;
+
+       if (val > sf4)
+               return -EINVAL;
+
+       if (val == sf3 && data->kind != nct6775)
+               return -EINVAL;
+
+       if (val == sf4 && check_trip_points(data, nr)) {
+               dev_err(dev, "Inconsistent trip points, not switching to SmartFan IV mode\n");
+               dev_err(dev, "Adjust trip points and try again\n");
+               return -EINVAL;
+       }
+
+       mutex_lock(&data->update_lock);
+       data->pwm_enable[nr] = val;
+       if (val == off) {
+               /*
+                * turn off pwm control: select manual mode, set pwm to maximum
+                */
+               data->pwm[0][nr] = 255;
+               nct6775_write_value(data, data->REG_PWM[0][nr], 255);
+       }
+       pwm_update_registers(data, nr);
+       reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]);
+       reg &= 0x0f;
+       reg |= pwm_enable_to_reg(val) << 4;
+       nct6775_write_value(data, data->REG_FAN_MODE[nr], reg);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t
+show_pwm_temp_sel_common(struct nct6775_data *data, char *buf, int src)
+{
+       int i, sel = 0;
+
+       for (i = 0; i < NUM_TEMP; i++) {
+               if (!(data->have_temp & (1 << i)))
+                       continue;
+               if (src == data->temp_src[i]) {
+                       sel = i + 1;
+                       break;
+               }
+       }
+
+       return sprintf(buf, "%d\n", sel);
+}
+
+static ssize_t
+show_pwm_temp_sel(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int index = sattr->index;
+
+       return show_pwm_temp_sel_common(data, buf, data->pwm_temp_sel[index]);
+}
+
+static ssize_t
+store_pwm_temp_sel(struct device *dev, struct device_attribute *attr,
+                  const char *buf, size_t count)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int nr = sattr->index;
+       unsigned long val;
+       int err, reg, src;
+
+       err = kstrtoul(buf, 10, &val);
+       if (err < 0)
+               return err;
+       if (val == 0 || val > NUM_TEMP)
+               return -EINVAL;
+       if (!(data->have_temp & (1 << (val - 1))) || !data->temp_src[val - 1])
+               return -EINVAL;
+
+       mutex_lock(&data->update_lock);
+       src = data->temp_src[val - 1];
+       data->pwm_temp_sel[nr] = src;
+       reg = nct6775_read_value(data, data->REG_TEMP_SEL[nr]);
+       reg &= 0xe0;
+       reg |= src;
+       nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg);
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t
+show_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int index = sattr->index;
+
+       return show_pwm_temp_sel_common(data, buf,
+                                       data->pwm_weight_temp_sel[index]);
+}
+
+static ssize_t
+store_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int nr = sattr->index;
+       unsigned long val;
+       int err, reg, src;
+
+       err = kstrtoul(buf, 10, &val);
+       if (err < 0)
+               return err;
+       if (val > NUM_TEMP)
+               return -EINVAL;
+       if (val && (!(data->have_temp & (1 << (val - 1))) ||
+                   !data->temp_src[val - 1]))
+               return -EINVAL;
+
+       mutex_lock(&data->update_lock);
+       if (val) {
+               src = data->temp_src[val - 1];
+               data->pwm_weight_temp_sel[nr] = src;
+               reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr]);
+               reg &= 0xe0;
+               reg |= (src | 0x80);
+               nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg);
+       } else {
+               data->pwm_weight_temp_sel[nr] = 0;
+               reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr]);
+               reg &= 0x7f;
+               nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg);
+       }
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t
+show_target_temp(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+
+       return sprintf(buf, "%d\n", data->target_temp[sattr->index] * 1000);
+}
+
+static ssize_t
+store_target_temp(struct device *dev, struct device_attribute *attr,
+                 const char *buf, size_t count)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int nr = sattr->index;
+       unsigned long val;
+       int err;
+
+       err = kstrtoul(buf, 10, &val);
+       if (err < 0)
+               return err;
+
+       val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0,
+                       data->target_temp_mask);
+
+       mutex_lock(&data->update_lock);
+       data->target_temp[nr] = val;
+       pwm_update_registers(data, nr);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t
+show_target_speed(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int nr = sattr->index;
+
+       return sprintf(buf, "%d\n",
+                      fan_from_reg16(data->target_speed[nr],
+                                     data->fan_div[nr]));
+}
+
+static ssize_t
+store_target_speed(struct device *dev, struct device_attribute *attr,
+                  const char *buf, size_t count)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int nr = sattr->index;
+       unsigned long val;
+       int err;
+       u16 speed;
+
+       err = kstrtoul(buf, 10, &val);
+       if (err < 0)
+               return err;
+
+       val = clamp_val(val, 0, 1350000U);
+       speed = fan_to_reg(val, data->fan_div[nr]);
+
+       mutex_lock(&data->update_lock);
+       data->target_speed[nr] = speed;
+       pwm_update_registers(data, nr);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t
+show_temp_tolerance(struct device *dev, struct device_attribute *attr,
+                   char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+       int nr = sattr->nr;
+       int index = sattr->index;
+
+       return sprintf(buf, "%d\n", data->temp_tolerance[index][nr] * 1000);
+}
+
+static ssize_t
+store_temp_tolerance(struct device *dev, struct device_attribute *attr,
+                    const char *buf, size_t count)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+       int nr = sattr->nr;
+       int index = sattr->index;
+       unsigned long val;
+       int err;
+
+       err = kstrtoul(buf, 10, &val);
+       if (err < 0)
+               return err;
+
+       /* Limit tolerance as needed */
+       val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, data->tolerance_mask);
+
+       mutex_lock(&data->update_lock);
+       data->temp_tolerance[index][nr] = val;
+       if (index)
+               pwm_update_registers(data, nr);
+       else
+               nct6775_write_value(data,
+                                   data->REG_CRITICAL_TEMP_TOLERANCE[nr],
+                                   val);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+/*
+ * Fan speed tolerance is a tricky beast, since the associated register is
+ * a tick counter, but the value is reported and configured as rpm.
+ * Compute resulting low and high rpm values and report the difference.
+ */
+static ssize_t
+show_speed_tolerance(struct device *dev, struct device_attribute *attr,
+                    char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int nr = sattr->index;
+       int low = data->target_speed[nr] - data->target_speed_tolerance[nr];
+       int high = data->target_speed[nr] + data->target_speed_tolerance[nr];
+       int tolerance;
+
+       if (low <= 0)
+               low = 1;
+       if (high > 0xffff)
+               high = 0xffff;
+       if (high < low)
+               high = low;
+
+       tolerance = (fan_from_reg16(low, data->fan_div[nr])
+                    - fan_from_reg16(high, data->fan_div[nr])) / 2;
+
+       return sprintf(buf, "%d\n", tolerance);
+}
+
+static ssize_t
+store_speed_tolerance(struct device *dev, struct device_attribute *attr,
+                     const char *buf, size_t count)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+       int nr = sattr->index;
+       unsigned long val;
+       int err;
+       int low, high;
+
+       err = kstrtoul(buf, 10, &val);
+       if (err < 0)
+               return err;
+
+       high = fan_from_reg16(data->target_speed[nr],
+                             data->fan_div[nr]) + val;
+       low = fan_from_reg16(data->target_speed[nr],
+                            data->fan_div[nr]) - val;
+       if (low <= 0)
+               low = 1;
+       if (high < low)
+               high = low;
+
+       val = (fan_to_reg(low, data->fan_div[nr]) -
+              fan_to_reg(high, data->fan_div[nr])) / 2;
+
+       /* Limit tolerance as needed */
+       val = clamp_val(val, 0, data->speed_tolerance_limit);
+
+       mutex_lock(&data->update_lock);
+       data->target_speed_tolerance[nr] = val;
+       pwm_update_registers(data, nr);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static SENSOR_DEVICE_ATTR_2(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 0);
+static SENSOR_DEVICE_ATTR_2(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1, 0);
+static SENSOR_DEVICE_ATTR_2(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2, 0);
+static SENSOR_DEVICE_ATTR_2(pwm4, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3, 0);
+static SENSOR_DEVICE_ATTR_2(pwm5, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 4, 0);
+
+static SENSOR_DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+                         store_pwm_mode, 0);
+static SENSOR_DEVICE_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+                         store_pwm_mode, 1);
+static SENSOR_DEVICE_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+                         store_pwm_mode, 2);
+static SENSOR_DEVICE_ATTR(pwm4_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+                         store_pwm_mode, 3);
+static SENSOR_DEVICE_ATTR(pwm5_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+                         store_pwm_mode, 4);
+
+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+                         store_pwm_enable, 0);
+static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+                         store_pwm_enable, 1);
+static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+                         store_pwm_enable, 2);
+static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+                         store_pwm_enable, 3);
+static SENSOR_DEVICE_ATTR(pwm5_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+                         store_pwm_enable, 4);
+
+static SENSOR_DEVICE_ATTR(pwm1_temp_sel, S_IWUSR | S_IRUGO,
+                           show_pwm_temp_sel, store_pwm_temp_sel, 0);
+static SENSOR_DEVICE_ATTR(pwm2_temp_sel, S_IWUSR | S_IRUGO,
+                           show_pwm_temp_sel, store_pwm_temp_sel, 1);
+static SENSOR_DEVICE_ATTR(pwm3_temp_sel, S_IWUSR | S_IRUGO,
+                           show_pwm_temp_sel, store_pwm_temp_sel, 2);
+static SENSOR_DEVICE_ATTR(pwm4_temp_sel, S_IWUSR | S_IRUGO,
+                           show_pwm_temp_sel, store_pwm_temp_sel, 3);
+static SENSOR_DEVICE_ATTR(pwm5_temp_sel, S_IWUSR | S_IRUGO,
+                           show_pwm_temp_sel, store_pwm_temp_sel, 4);
+
+static SENSOR_DEVICE_ATTR(pwm1_target_temp, S_IWUSR | S_IRUGO, show_target_temp,
+                         store_target_temp, 0);
+static SENSOR_DEVICE_ATTR(pwm2_target_temp, S_IWUSR | S_IRUGO, show_target_temp,
+                         store_target_temp, 1);
+static SENSOR_DEVICE_ATTR(pwm3_target_temp, S_IWUSR | S_IRUGO, show_target_temp,
+                         store_target_temp, 2);
+static SENSOR_DEVICE_ATTR(pwm4_target_temp, S_IWUSR | S_IRUGO, show_target_temp,
+                         store_target_temp, 3);
+static SENSOR_DEVICE_ATTR(pwm5_target_temp, S_IWUSR | S_IRUGO, show_target_temp,
+                         store_target_temp, 4);
+
+static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, show_target_speed,
+                         store_target_speed, 0);
+static SENSOR_DEVICE_ATTR(fan2_target, S_IWUSR | S_IRUGO, show_target_speed,
+                         store_target_speed, 1);
+static SENSOR_DEVICE_ATTR(fan3_target, S_IWUSR | S_IRUGO, show_target_speed,
+                         store_target_speed, 2);
+static SENSOR_DEVICE_ATTR(fan4_target, S_IWUSR | S_IRUGO, show_target_speed,
+                         store_target_speed, 3);
+static SENSOR_DEVICE_ATTR(fan5_target, S_IWUSR | S_IRUGO, show_target_speed,
+                         store_target_speed, 4);
+
+static SENSOR_DEVICE_ATTR(fan1_tolerance, S_IWUSR | S_IRUGO,
+                           show_speed_tolerance, store_speed_tolerance, 0);
+static SENSOR_DEVICE_ATTR(fan2_tolerance, S_IWUSR | S_IRUGO,
+                           show_speed_tolerance, store_speed_tolerance, 1);
+static SENSOR_DEVICE_ATTR(fan3_tolerance, S_IWUSR | S_IRUGO,
+                           show_speed_tolerance, store_speed_tolerance, 2);
+static SENSOR_DEVICE_ATTR(fan4_tolerance, S_IWUSR | S_IRUGO,
+                           show_speed_tolerance, store_speed_tolerance, 3);
+static SENSOR_DEVICE_ATTR(fan5_tolerance, S_IWUSR | S_IRUGO,
+                           show_speed_tolerance, store_speed_tolerance, 4);
+
+/* Smart Fan registers */
+
+static ssize_t
+show_weight_temp(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+       int nr = sattr->nr;
+       int index = sattr->index;
+
+       return sprintf(buf, "%d\n", data->weight_temp[index][nr] * 1000);
+}
+
+static ssize_t
+store_weight_temp(struct device *dev, struct device_attribute *attr,
+                 const char *buf, size_t count)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+       int nr = sattr->nr;
+       int index = sattr->index;
+       unsigned long val;
+       int err;
+
+       err = kstrtoul(buf, 10, &val);
+       if (err < 0)
+               return err;
+
+       val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 255);
+
+       mutex_lock(&data->update_lock);
+       data->weight_temp[index][nr] = val;
+       nct6775_write_value(data, data->REG_WEIGHT_TEMP[index][nr], val);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static SENSOR_DEVICE_ATTR(pwm1_weight_temp_sel, S_IWUSR | S_IRUGO,
+                           show_pwm_weight_temp_sel, store_pwm_weight_temp_sel,
+                           0);
+static SENSOR_DEVICE_ATTR(pwm2_weight_temp_sel, S_IWUSR | S_IRUGO,
+                           show_pwm_weight_temp_sel, store_pwm_weight_temp_sel,
+                           1);
+static SENSOR_DEVICE_ATTR(pwm3_weight_temp_sel, S_IWUSR | S_IRUGO,
+                           show_pwm_weight_temp_sel, store_pwm_weight_temp_sel,
+                           2);
+static SENSOR_DEVICE_ATTR(pwm4_weight_temp_sel, S_IWUSR | S_IRUGO,
+                           show_pwm_weight_temp_sel, store_pwm_weight_temp_sel,
+                           3);
+static SENSOR_DEVICE_ATTR(pwm5_weight_temp_sel, S_IWUSR | S_IRUGO,
+                           show_pwm_weight_temp_sel, store_pwm_weight_temp_sel,
+                           4);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_weight_temp_step, S_IWUSR | S_IRUGO,
+                           show_weight_temp, store_weight_temp, 0, 0);
+static SENSOR_DEVICE_ATTR_2(pwm2_weight_temp_step, S_IWUSR | S_IRUGO,
+                           show_weight_temp, store_weight_temp, 1, 0);
+static SENSOR_DEVICE_ATTR_2(pwm3_weight_temp_step, S_IWUSR | S_IRUGO,
+                           show_weight_temp, store_weight_temp, 2, 0);
+static SENSOR_DEVICE_ATTR_2(pwm4_weight_temp_step, S_IWUSR | S_IRUGO,
+                           show_weight_temp, store_weight_temp, 3, 0);
+static SENSOR_DEVICE_ATTR_2(pwm5_weight_temp_step, S_IWUSR | S_IRUGO,
+                           show_weight_temp, store_weight_temp, 4, 0);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_weight_temp_step_tol, S_IWUSR | S_IRUGO,
+                           show_weight_temp, store_weight_temp, 0, 1);
+static SENSOR_DEVICE_ATTR_2(pwm2_weight_temp_step_tol, S_IWUSR | S_IRUGO,
+                           show_weight_temp, store_weight_temp, 1, 1);
+static SENSOR_DEVICE_ATTR_2(pwm3_weight_temp_step_tol, S_IWUSR | S_IRUGO,
+                           show_weight_temp, store_weight_temp, 2, 1);
+static SENSOR_DEVICE_ATTR_2(pwm4_weight_temp_step_tol, S_IWUSR | S_IRUGO,
+                           show_weight_temp, store_weight_temp, 3, 1);
+static SENSOR_DEVICE_ATTR_2(pwm5_weight_temp_step_tol, S_IWUSR | S_IRUGO,
+                           show_weight_temp, store_weight_temp, 4, 1);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_weight_temp_step_base, S_IWUSR | S_IRUGO,
+                           show_weight_temp, store_weight_temp, 0, 2);
+static SENSOR_DEVICE_ATTR_2(pwm2_weight_temp_step_base, S_IWUSR | S_IRUGO,
+                           show_weight_temp, store_weight_temp, 1, 2);
+static SENSOR_DEVICE_ATTR_2(pwm3_weight_temp_step_base, S_IWUSR | S_IRUGO,
+                           show_weight_temp, store_weight_temp, 2, 2);
+static SENSOR_DEVICE_ATTR_2(pwm4_weight_temp_step_base, S_IWUSR | S_IRUGO,
+                           show_weight_temp, store_weight_temp, 3, 2);
+static SENSOR_DEVICE_ATTR_2(pwm5_weight_temp_step_base, S_IWUSR | S_IRUGO,
+                           show_weight_temp, store_weight_temp, 4, 2);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_weight_duty_step, S_IWUSR | S_IRUGO,
+                           show_pwm, store_pwm, 0, 5);
+static SENSOR_DEVICE_ATTR_2(pwm2_weight_duty_step, S_IWUSR | S_IRUGO,
+                           show_pwm, store_pwm, 1, 5);
+static SENSOR_DEVICE_ATTR_2(pwm3_weight_duty_step, S_IWUSR | S_IRUGO,
+                           show_pwm, store_pwm, 2, 5);
+static SENSOR_DEVICE_ATTR_2(pwm4_weight_duty_step, S_IWUSR | S_IRUGO,
+                           show_pwm, store_pwm, 3, 5);
+static SENSOR_DEVICE_ATTR_2(pwm5_weight_duty_step, S_IWUSR | S_IRUGO,
+                           show_pwm, store_pwm, 4, 5);
+
+/* duty_base is not supported on all chips */
+static struct sensor_device_attribute_2 sda_weight_duty_base[] = {
+       SENSOR_ATTR_2(pwm1_weight_duty_base, S_IWUSR | S_IRUGO,
+                     show_pwm, store_pwm, 0, 6),
+       SENSOR_ATTR_2(pwm2_weight_duty_base, S_IWUSR | S_IRUGO,
+                     show_pwm, store_pwm, 1, 6),
+       SENSOR_ATTR_2(pwm3_weight_duty_base, S_IWUSR | S_IRUGO,
+                     show_pwm, store_pwm, 2, 6),
+       SENSOR_ATTR_2(pwm4_weight_duty_base, S_IWUSR | S_IRUGO,
+                     show_pwm, store_pwm, 3, 6),
+       SENSOR_ATTR_2(pwm5_weight_duty_base, S_IWUSR | S_IRUGO,
+                     show_pwm, store_pwm, 4, 6),
+};
+
+static ssize_t
+show_fan_time(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+       int nr = sattr->nr;
+       int index = sattr->index;
+
+       return sprintf(buf, "%d\n",
+                      step_time_from_reg(data->fan_time[index][nr],
+                                         data->pwm_mode[nr]));
+}
+
+static ssize_t
+store_fan_time(struct device *dev, struct device_attribute *attr,
+              const char *buf, size_t count)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+       int nr = sattr->nr;
+       int index = sattr->index;
+       unsigned long val;
+       int err;
+
+       err = kstrtoul(buf, 10, &val);
+       if (err < 0)
+               return err;
+
+       val = step_time_to_reg(val, data->pwm_mode[nr]);
+       mutex_lock(&data->update_lock);
+       data->fan_time[index][nr] = val;
+       nct6775_write_value(data, data->REG_FAN_TIME[index][nr], val);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t
+show_name(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%s\n", data->name);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_time,
+                           store_fan_time, 0, 0);
+static SENSOR_DEVICE_ATTR_2(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_time,
+                           store_fan_time, 1, 0);
+static SENSOR_DEVICE_ATTR_2(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_time,
+                           store_fan_time, 2, 0);
+static SENSOR_DEVICE_ATTR_2(pwm4_stop_time, S_IWUSR | S_IRUGO, show_fan_time,
+                           store_fan_time, 3, 0);
+static SENSOR_DEVICE_ATTR_2(pwm5_stop_time, S_IWUSR | S_IRUGO, show_fan_time,
+                           store_fan_time, 4, 0);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_step_up_time, S_IWUSR | S_IRUGO, show_fan_time,
+                           store_fan_time, 0, 1);
+static SENSOR_DEVICE_ATTR_2(pwm2_step_up_time, S_IWUSR | S_IRUGO, show_fan_time,
+                           store_fan_time, 1, 1);
+static SENSOR_DEVICE_ATTR_2(pwm3_step_up_time, S_IWUSR | S_IRUGO, show_fan_time,
+                           store_fan_time, 2, 1);
+static SENSOR_DEVICE_ATTR_2(pwm4_step_up_time, S_IWUSR | S_IRUGO, show_fan_time,
+                           store_fan_time, 3, 1);
+static SENSOR_DEVICE_ATTR_2(pwm5_step_up_time, S_IWUSR | S_IRUGO, show_fan_time,
+                           store_fan_time, 4, 1);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_step_down_time, S_IWUSR | S_IRUGO,
+                           show_fan_time, store_fan_time, 0, 2);
+static SENSOR_DEVICE_ATTR_2(pwm2_step_down_time, S_IWUSR | S_IRUGO,
+                           show_fan_time, store_fan_time, 1, 2);
+static SENSOR_DEVICE_ATTR_2(pwm3_step_down_time, S_IWUSR | S_IRUGO,
+                           show_fan_time, store_fan_time, 2, 2);
+static SENSOR_DEVICE_ATTR_2(pwm4_step_down_time, S_IWUSR | S_IRUGO,
+                           show_fan_time, store_fan_time, 3, 2);
+static SENSOR_DEVICE_ATTR_2(pwm5_step_down_time, S_IWUSR | S_IRUGO,
+                           show_fan_time, store_fan_time, 4, 2);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_start, S_IWUSR | S_IRUGO, show_pwm,
+                           store_pwm, 0, 1);
+static SENSOR_DEVICE_ATTR_2(pwm2_start, S_IWUSR | S_IRUGO, show_pwm,
+                           store_pwm, 1, 1);
+static SENSOR_DEVICE_ATTR_2(pwm3_start, S_IWUSR | S_IRUGO, show_pwm,
+                           store_pwm, 2, 1);
+static SENSOR_DEVICE_ATTR_2(pwm4_start, S_IWUSR | S_IRUGO, show_pwm,
+                           store_pwm, 3, 1);
+static SENSOR_DEVICE_ATTR_2(pwm5_start, S_IWUSR | S_IRUGO, show_pwm,
+                           store_pwm, 4, 1);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_floor, S_IWUSR | S_IRUGO, show_pwm,
+                           store_pwm, 0, 2);
+static SENSOR_DEVICE_ATTR_2(pwm2_floor, S_IWUSR | S_IRUGO, show_pwm,
+                           store_pwm, 1, 2);
+static SENSOR_DEVICE_ATTR_2(pwm3_floor, S_IWUSR | S_IRUGO, show_pwm,
+                           store_pwm, 2, 2);
+static SENSOR_DEVICE_ATTR_2(pwm4_floor, S_IWUSR | S_IRUGO, show_pwm,
+                           store_pwm, 3, 2);
+static SENSOR_DEVICE_ATTR_2(pwm5_floor, S_IWUSR | S_IRUGO, show_pwm,
+                           store_pwm, 4, 2);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_temp_tolerance, S_IWUSR | S_IRUGO,
+                           show_temp_tolerance, store_temp_tolerance, 0, 0);
+static SENSOR_DEVICE_ATTR_2(pwm2_temp_tolerance, S_IWUSR | S_IRUGO,
+                           show_temp_tolerance, store_temp_tolerance, 1, 0);
+static SENSOR_DEVICE_ATTR_2(pwm3_temp_tolerance, S_IWUSR | S_IRUGO,
+                           show_temp_tolerance, store_temp_tolerance, 2, 0);
+static SENSOR_DEVICE_ATTR_2(pwm4_temp_tolerance, S_IWUSR | S_IRUGO,
+                           show_temp_tolerance, store_temp_tolerance, 3, 0);
+static SENSOR_DEVICE_ATTR_2(pwm5_temp_tolerance, S_IWUSR | S_IRUGO,
+                           show_temp_tolerance, store_temp_tolerance, 4, 0);
+
+static SENSOR_DEVICE_ATTR_2(pwm1_crit_temp_tolerance, S_IWUSR | S_IRUGO,
+                           show_temp_tolerance, store_temp_tolerance, 0, 1);
+static SENSOR_DEVICE_ATTR_2(pwm2_crit_temp_tolerance, S_IWUSR | S_IRUGO,
+                           show_temp_tolerance, store_temp_tolerance, 1, 1);
+static SENSOR_DEVICE_ATTR_2(pwm3_crit_temp_tolerance, S_IWUSR | S_IRUGO,
+                           show_temp_tolerance, store_temp_tolerance, 2, 1);
+static SENSOR_DEVICE_ATTR_2(pwm4_crit_temp_tolerance, S_IWUSR | S_IRUGO,
+                           show_temp_tolerance, store_temp_tolerance, 3, 1);
+static SENSOR_DEVICE_ATTR_2(pwm5_crit_temp_tolerance, S_IWUSR | S_IRUGO,
+                           show_temp_tolerance, store_temp_tolerance, 4, 1);
+
+/* pwm_max is not supported on all chips */
+static struct sensor_device_attribute_2 sda_pwm_max[] = {
+       SENSOR_ATTR_2(pwm1_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm,
+                     0, 3),
+       SENSOR_ATTR_2(pwm2_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm,
+                     1, 3),
+       SENSOR_ATTR_2(pwm3_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm,
+                     2, 3),
+       SENSOR_ATTR_2(pwm4_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm,
+                     3, 3),
+       SENSOR_ATTR_2(pwm5_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm,
+                     4, 3),
+};
+
+/* pwm_step is not supported on all chips */
+static struct sensor_device_attribute_2 sda_pwm_step[] = {
+       SENSOR_ATTR_2(pwm1_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 4),
+       SENSOR_ATTR_2(pwm2_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1, 4),
+       SENSOR_ATTR_2(pwm3_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2, 4),
+       SENSOR_ATTR_2(pwm4_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3, 4),
+       SENSOR_ATTR_2(pwm5_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 4, 4),
+};
+
+static struct attribute *nct6775_attributes_pwm[5][20] = {
+       {
+               &sensor_dev_attr_pwm1.dev_attr.attr,
+               &sensor_dev_attr_pwm1_mode.dev_attr.attr,
+               &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+               &sensor_dev_attr_pwm1_temp_sel.dev_attr.attr,
+               &sensor_dev_attr_pwm1_temp_tolerance.dev_attr.attr,
+               &sensor_dev_attr_pwm1_crit_temp_tolerance.dev_attr.attr,
+               &sensor_dev_attr_pwm1_target_temp.dev_attr.attr,
+               &sensor_dev_attr_fan1_target.dev_attr.attr,
+               &sensor_dev_attr_fan1_tolerance.dev_attr.attr,
+               &sensor_dev_attr_pwm1_stop_time.dev_attr.attr,
+               &sensor_dev_attr_pwm1_step_up_time.dev_attr.attr,
+               &sensor_dev_attr_pwm1_step_down_time.dev_attr.attr,
+               &sensor_dev_attr_pwm1_start.dev_attr.attr,
+               &sensor_dev_attr_pwm1_floor.dev_attr.attr,
+               &sensor_dev_attr_pwm1_weight_temp_sel.dev_attr.attr,
+               &sensor_dev_attr_pwm1_weight_temp_step.dev_attr.attr,
+               &sensor_dev_attr_pwm1_weight_temp_step_tol.dev_attr.attr,
+               &sensor_dev_attr_pwm1_weight_temp_step_base.dev_attr.attr,
+               &sensor_dev_attr_pwm1_weight_duty_step.dev_attr.attr,
+               NULL
+       },
+       {
+               &sensor_dev_attr_pwm2.dev_attr.attr,
+               &sensor_dev_attr_pwm2_mode.dev_attr.attr,
+               &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+               &sensor_dev_attr_pwm2_temp_sel.dev_attr.attr,
+               &sensor_dev_attr_pwm2_temp_tolerance.dev_attr.attr,
+               &sensor_dev_attr_pwm2_crit_temp_tolerance.dev_attr.attr,
+               &sensor_dev_attr_pwm2_target_temp.dev_attr.attr,
+               &sensor_dev_attr_fan2_target.dev_attr.attr,
+               &sensor_dev_attr_fan2_tolerance.dev_attr.attr,
+               &sensor_dev_attr_pwm2_stop_time.dev_attr.attr,
+               &sensor_dev_attr_pwm2_step_up_time.dev_attr.attr,
+               &sensor_dev_attr_pwm2_step_down_time.dev_attr.attr,
+               &sensor_dev_attr_pwm2_start.dev_attr.attr,
+               &sensor_dev_attr_pwm2_floor.dev_attr.attr,
+               &sensor_dev_attr_pwm2_weight_temp_sel.dev_attr.attr,
+               &sensor_dev_attr_pwm2_weight_temp_step.dev_attr.attr,
+               &sensor_dev_attr_pwm2_weight_temp_step_tol.dev_attr.attr,
+               &sensor_dev_attr_pwm2_weight_temp_step_base.dev_attr.attr,
+               &sensor_dev_attr_pwm2_weight_duty_step.dev_attr.attr,
+               NULL
+       },
+       {
+               &sensor_dev_attr_pwm3.dev_attr.attr,
+               &sensor_dev_attr_pwm3_mode.dev_attr.attr,
+               &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+               &sensor_dev_attr_pwm3_temp_sel.dev_attr.attr,
+               &sensor_dev_attr_pwm3_temp_tolerance.dev_attr.attr,
+               &sensor_dev_attr_pwm3_crit_temp_tolerance.dev_attr.attr,
+               &sensor_dev_attr_pwm3_target_temp.dev_attr.attr,
+               &sensor_dev_attr_fan3_target.dev_attr.attr,
+               &sensor_dev_attr_fan3_tolerance.dev_attr.attr,
+               &sensor_dev_attr_pwm3_stop_time.dev_attr.attr,
+               &sensor_dev_attr_pwm3_step_up_time.dev_attr.attr,
+               &sensor_dev_attr_pwm3_step_down_time.dev_attr.attr,
+               &sensor_dev_attr_pwm3_start.dev_attr.attr,
+               &sensor_dev_attr_pwm3_floor.dev_attr.attr,
+               &sensor_dev_attr_pwm3_weight_temp_sel.dev_attr.attr,
+               &sensor_dev_attr_pwm3_weight_temp_step.dev_attr.attr,
+               &sensor_dev_attr_pwm3_weight_temp_step_tol.dev_attr.attr,
+               &sensor_dev_attr_pwm3_weight_temp_step_base.dev_attr.attr,
+               &sensor_dev_attr_pwm3_weight_duty_step.dev_attr.attr,
+               NULL
+       },
+       {
+               &sensor_dev_attr_pwm4.dev_attr.attr,
+               &sensor_dev_attr_pwm4_mode.dev_attr.attr,
+               &sensor_dev_attr_pwm4_enable.dev_attr.attr,
+               &sensor_dev_attr_pwm4_temp_sel.dev_attr.attr,
+               &sensor_dev_attr_pwm4_temp_tolerance.dev_attr.attr,
+               &sensor_dev_attr_pwm4_crit_temp_tolerance.dev_attr.attr,
+               &sensor_dev_attr_pwm4_target_temp.dev_attr.attr,
+               &sensor_dev_attr_fan4_target.dev_attr.attr,
+               &sensor_dev_attr_fan4_tolerance.dev_attr.attr,
+               &sensor_dev_attr_pwm4_stop_time.dev_attr.attr,
+               &sensor_dev_attr_pwm4_step_up_time.dev_attr.attr,
+               &sensor_dev_attr_pwm4_step_down_time.dev_attr.attr,
+               &sensor_dev_attr_pwm4_start.dev_attr.attr,
+               &sensor_dev_attr_pwm4_floor.dev_attr.attr,
+               &sensor_dev_attr_pwm4_weight_temp_sel.dev_attr.attr,
+               &sensor_dev_attr_pwm4_weight_temp_step.dev_attr.attr,
+               &sensor_dev_attr_pwm4_weight_temp_step_tol.dev_attr.attr,
+               &sensor_dev_attr_pwm4_weight_temp_step_base.dev_attr.attr,
+               &sensor_dev_attr_pwm4_weight_duty_step.dev_attr.attr,
+               NULL
+       },
+       {
+               &sensor_dev_attr_pwm5.dev_attr.attr,
+               &sensor_dev_attr_pwm5_mode.dev_attr.attr,
+               &sensor_dev_attr_pwm5_enable.dev_attr.attr,
+               &sensor_dev_attr_pwm5_temp_sel.dev_attr.attr,
+               &sensor_dev_attr_pwm5_temp_tolerance.dev_attr.attr,
+               &sensor_dev_attr_pwm5_crit_temp_tolerance.dev_attr.attr,
+               &sensor_dev_attr_pwm5_target_temp.dev_attr.attr,
+               &sensor_dev_attr_fan5_target.dev_attr.attr,
+               &sensor_dev_attr_fan5_tolerance.dev_attr.attr,
+               &sensor_dev_attr_pwm5_stop_time.dev_attr.attr,
+               &sensor_dev_attr_pwm5_step_up_time.dev_attr.attr,
+               &sensor_dev_attr_pwm5_step_down_time.dev_attr.attr,
+               &sensor_dev_attr_pwm5_start.dev_attr.attr,
+               &sensor_dev_attr_pwm5_floor.dev_attr.attr,
+               &sensor_dev_attr_pwm5_weight_temp_sel.dev_attr.attr,
+               &sensor_dev_attr_pwm5_weight_temp_step.dev_attr.attr,
+               &sensor_dev_attr_pwm5_weight_temp_step_tol.dev_attr.attr,
+               &sensor_dev_attr_pwm5_weight_temp_step_base.dev_attr.attr,
+               &sensor_dev_attr_pwm5_weight_duty_step.dev_attr.attr,
+               NULL
+       },
+};
+
+static const struct attribute_group nct6775_group_pwm[5] = {
+       { .attrs = nct6775_attributes_pwm[0] },
+       { .attrs = nct6775_attributes_pwm[1] },
+       { .attrs = nct6775_attributes_pwm[2] },
+       { .attrs = nct6775_attributes_pwm[3] },
+       { .attrs = nct6775_attributes_pwm[4] },
+};
+
+static ssize_t
+show_auto_pwm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+
+       return sprintf(buf, "%d\n", data->auto_pwm[sattr->nr][sattr->index]);
+}
+
+static ssize_t
+store_auto_pwm(struct device *dev, struct device_attribute *attr,
+              const char *buf, size_t count)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+       int nr = sattr->nr;
+       int point = sattr->index;
+       unsigned long val;
+       int err;
+       u8 reg;
+
+       err = kstrtoul(buf, 10, &val);
+       if (err < 0)
+               return err;
+       if (val > 255)
+               return -EINVAL;
+
+       if (point == data->auto_pwm_num) {
+               if (data->kind != nct6775 && !val)
+                       return -EINVAL;
+               if (data->kind != nct6779 && val)
+                       val = 0xff;
+       }
+
+       mutex_lock(&data->update_lock);
+       data->auto_pwm[nr][point] = val;
+       if (point < data->auto_pwm_num) {
+               nct6775_write_value(data,
+                                   NCT6775_AUTO_PWM(data, nr, point),
+                                   data->auto_pwm[nr][point]);
+       } else {
+               switch (data->kind) {
+               case nct6775:
+                       /* disable if needed (pwm == 0) */
+                       reg = nct6775_read_value(data,
+                                                NCT6775_REG_CRITICAL_ENAB[nr]);
+                       if (val)
+                               reg |= 0x02;
+                       else
+                               reg &= ~0x02;
+                       nct6775_write_value(data, NCT6775_REG_CRITICAL_ENAB[nr],
+                                           reg);
+                       break;
+               case nct6776:
+                       break; /* always enabled, nothing to do */
+               case nct6779:
+                       nct6775_write_value(data, NCT6779_REG_CRITICAL_PWM[nr],
+                                           val);
+                       reg = nct6775_read_value(data,
+                                       NCT6779_REG_CRITICAL_PWM_ENABLE[nr]);
+                       if (val == 255)
+                               reg &= ~0x01;
+                       else
+                               reg |= 0x01;
+                       nct6775_write_value(data,
+                                           NCT6779_REG_CRITICAL_PWM_ENABLE[nr],
+                                           reg);
+                       break;
+               }
+       }
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t
+show_auto_temp(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+       int nr = sattr->nr;
+       int point = sattr->index;
+
+       /*
+        * We don't know for sure if the temperature is signed or unsigned.
+        * Assume it is unsigned.
+        */
+       return sprintf(buf, "%d\n", data->auto_temp[nr][point] * 1000);
+}
+
+static ssize_t
+store_auto_temp(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+       int nr = sattr->nr;
+       int point = sattr->index;
+       unsigned long val;
+       int err;
+
+       err = kstrtoul(buf, 10, &val);
+       if (err)
+               return err;
+       if (val > 255000)
+               return -EINVAL;
+
+       mutex_lock(&data->update_lock);
+       data->auto_temp[nr][point] = DIV_ROUND_CLOSEST(val, 1000);
+       if (point < data->auto_pwm_num) {
+               nct6775_write_value(data,
+                                   NCT6775_AUTO_TEMP(data, nr, point),
+                                   data->auto_temp[nr][point]);
+       } else {
+               nct6775_write_value(data, data->REG_CRITICAL_TEMP[nr],
+                                   data->auto_temp[nr][point]);
+       }
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+/*
+ * The number of auto-point trip points is chip dependent.
+ * Need to check support while generating/removing attribute files.
+ */
+static struct sensor_device_attribute_2 sda_auto_pwm_arrays[] = {
+       SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 0, 0),
+       SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 0, 0),
+       SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 0, 1),
+       SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 0, 1),
+       SENSOR_ATTR_2(pwm1_auto_point3_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 0, 2),
+       SENSOR_ATTR_2(pwm1_auto_point3_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 0, 2),
+       SENSOR_ATTR_2(pwm1_auto_point4_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 0, 3),
+       SENSOR_ATTR_2(pwm1_auto_point4_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 0, 3),
+       SENSOR_ATTR_2(pwm1_auto_point5_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 0, 4),
+       SENSOR_ATTR_2(pwm1_auto_point5_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 0, 4),
+       SENSOR_ATTR_2(pwm1_auto_point6_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 0, 5),
+       SENSOR_ATTR_2(pwm1_auto_point6_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 0, 5),
+       SENSOR_ATTR_2(pwm1_auto_point7_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 0, 6),
+       SENSOR_ATTR_2(pwm1_auto_point7_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 0, 6),
+
+       SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 1, 0),
+       SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 1, 0),
+       SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 1, 1),
+       SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 1, 1),
+       SENSOR_ATTR_2(pwm2_auto_point3_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 1, 2),
+       SENSOR_ATTR_2(pwm2_auto_point3_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 1, 2),
+       SENSOR_ATTR_2(pwm2_auto_point4_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 1, 3),
+       SENSOR_ATTR_2(pwm2_auto_point4_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 1, 3),
+       SENSOR_ATTR_2(pwm2_auto_point5_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 1, 4),
+       SENSOR_ATTR_2(pwm2_auto_point5_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 1, 4),
+       SENSOR_ATTR_2(pwm2_auto_point6_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 1, 5),
+       SENSOR_ATTR_2(pwm2_auto_point6_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 1, 5),
+       SENSOR_ATTR_2(pwm2_auto_point7_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 1, 6),
+       SENSOR_ATTR_2(pwm2_auto_point7_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 1, 6),
+
+       SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 2, 0),
+       SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 2, 0),
+       SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 2, 1),
+       SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 2, 1),
+       SENSOR_ATTR_2(pwm3_auto_point3_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 2, 2),
+       SENSOR_ATTR_2(pwm3_auto_point3_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 2, 2),
+       SENSOR_ATTR_2(pwm3_auto_point4_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 2, 3),
+       SENSOR_ATTR_2(pwm3_auto_point4_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 2, 3),
+       SENSOR_ATTR_2(pwm3_auto_point5_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 2, 4),
+       SENSOR_ATTR_2(pwm3_auto_point5_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 2, 4),
+       SENSOR_ATTR_2(pwm3_auto_point6_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 2, 5),
+       SENSOR_ATTR_2(pwm3_auto_point6_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 2, 5),
+       SENSOR_ATTR_2(pwm3_auto_point7_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 2, 6),
+       SENSOR_ATTR_2(pwm3_auto_point7_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 2, 6),
+
+       SENSOR_ATTR_2(pwm4_auto_point1_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 3, 0),
+       SENSOR_ATTR_2(pwm4_auto_point1_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 3, 0),
+       SENSOR_ATTR_2(pwm4_auto_point2_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 3, 1),
+       SENSOR_ATTR_2(pwm4_auto_point2_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 3, 1),
+       SENSOR_ATTR_2(pwm4_auto_point3_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 3, 2),
+       SENSOR_ATTR_2(pwm4_auto_point3_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 3, 2),
+       SENSOR_ATTR_2(pwm4_auto_point4_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 3, 3),
+       SENSOR_ATTR_2(pwm4_auto_point4_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 3, 3),
+       SENSOR_ATTR_2(pwm4_auto_point5_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 3, 4),
+       SENSOR_ATTR_2(pwm4_auto_point5_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 3, 4),
+       SENSOR_ATTR_2(pwm4_auto_point6_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 3, 5),
+       SENSOR_ATTR_2(pwm4_auto_point6_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 3, 5),
+       SENSOR_ATTR_2(pwm4_auto_point7_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 3, 6),
+       SENSOR_ATTR_2(pwm4_auto_point7_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 3, 6),
+
+       SENSOR_ATTR_2(pwm5_auto_point1_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 4, 0),
+       SENSOR_ATTR_2(pwm5_auto_point1_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 4, 0),
+       SENSOR_ATTR_2(pwm5_auto_point2_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 4, 1),
+       SENSOR_ATTR_2(pwm5_auto_point2_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 4, 1),
+       SENSOR_ATTR_2(pwm5_auto_point3_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 4, 2),
+       SENSOR_ATTR_2(pwm5_auto_point3_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 4, 2),
+       SENSOR_ATTR_2(pwm5_auto_point4_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 4, 3),
+       SENSOR_ATTR_2(pwm5_auto_point4_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 4, 3),
+       SENSOR_ATTR_2(pwm5_auto_point5_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 4, 4),
+       SENSOR_ATTR_2(pwm5_auto_point5_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 4, 4),
+       SENSOR_ATTR_2(pwm5_auto_point6_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 4, 5),
+       SENSOR_ATTR_2(pwm5_auto_point6_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 4, 5),
+       SENSOR_ATTR_2(pwm5_auto_point7_pwm, S_IWUSR | S_IRUGO,
+                     show_auto_pwm, store_auto_pwm, 4, 6),
+       SENSOR_ATTR_2(pwm5_auto_point7_temp, S_IWUSR | S_IRUGO,
+                     show_auto_temp, store_auto_temp, 4, 6),
+};
+
+static ssize_t
+show_vid(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
+}
+
+static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+
+/* Case open detection */
+
+static ssize_t
+clear_caseopen(struct device *dev, struct device_attribute *attr,
+              const char *buf, size_t count)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       struct nct6775_sio_data *sio_data = dev->platform_data;
+       int nr = to_sensor_dev_attr(attr)->index - INTRUSION_ALARM_BASE;
+       unsigned long val;
+       u8 reg;
+       int ret;
+
+       if (kstrtoul(buf, 10, &val) || val != 0)
+               return -EINVAL;
+
+       mutex_lock(&data->update_lock);
+
+       /*
+        * Use CR registers to clear caseopen status.
+        * The CR registers are the same for all chips, and not all chips
+        * support clearing the caseopen status through "regular" registers.
+        */
+       ret = superio_enter(sio_data->sioreg);
+       if (ret) {
+               count = ret;
+               goto error;
+       }
+
+       superio_select(sio_data->sioreg, NCT6775_LD_ACPI);
+       reg = superio_inb(sio_data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr]);
+       reg |= NCT6775_CR_CASEOPEN_CLR_MASK[nr];
+       superio_outb(sio_data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg);
+       reg &= ~NCT6775_CR_CASEOPEN_CLR_MASK[nr];
+       superio_outb(sio_data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg);
+       superio_exit(sio_data->sioreg);
+
+       data->valid = false;    /* Force cache refresh */
+error:
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static struct sensor_device_attribute sda_caseopen[] = {
+       SENSOR_ATTR(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm,
+                   clear_caseopen, INTRUSION_ALARM_BASE),
+       SENSOR_ATTR(intrusion1_alarm, S_IWUSR | S_IRUGO, show_alarm,
+                   clear_caseopen, INTRUSION_ALARM_BASE + 1),
+};
+
+/*
+ * Driver and device management
+ */
+
+static void nct6775_device_remove_files(struct device *dev)
+{
+       /*
+        * some entries in the following arrays may not have been used in
+        * device_create_file(), but device_remove_file() will ignore them
+        */
+       int i;
+       struct nct6775_data *data = dev_get_drvdata(dev);
+
+       for (i = 0; i < data->pwm_num; i++)
+               sysfs_remove_group(&dev->kobj, &nct6775_group_pwm[i]);
+
+       for (i = 0; i < ARRAY_SIZE(sda_pwm_max); i++)
+               device_remove_file(dev, &sda_pwm_max[i].dev_attr);
+
+       for (i = 0; i < ARRAY_SIZE(sda_pwm_step); i++)
+               device_remove_file(dev, &sda_pwm_step[i].dev_attr);
+
+       for (i = 0; i < ARRAY_SIZE(sda_weight_duty_base); i++)
+               device_remove_file(dev, &sda_weight_duty_base[i].dev_attr);
+
+       for (i = 0; i < ARRAY_SIZE(sda_auto_pwm_arrays); i++)
+               device_remove_file(dev, &sda_auto_pwm_arrays[i].dev_attr);
+
+       for (i = 0; i < data->in_num; i++)
+               sysfs_remove_group(&dev->kobj, &nct6775_group_in[i]);
+
+       for (i = 0; i < 5; i++) {
+               device_remove_file(dev, &sda_fan_input[i].dev_attr);
+               device_remove_file(dev, &sda_fan_alarm[i].dev_attr);
+               device_remove_file(dev, &sda_fan_div[i].dev_attr);
+               device_remove_file(dev, &sda_fan_min[i].dev_attr);
+               device_remove_file(dev, &sda_fan_pulses[i].dev_attr);
+       }
+       for (i = 0; i < NUM_TEMP; i++) {
+               if (!(data->have_temp & (1 << i)))
+                       continue;
+               device_remove_file(dev, &sda_temp_input[i].dev_attr);
+               device_remove_file(dev, &sda_temp_label[i].dev_attr);
+               device_remove_file(dev, &sda_temp_max[i].dev_attr);
+               device_remove_file(dev, &sda_temp_max_hyst[i].dev_attr);
+               device_remove_file(dev, &sda_temp_crit[i].dev_attr);
+               if (!(data->have_temp_fixed & (1 << i)))
+                       continue;
+               device_remove_file(dev, &sda_temp_type[i].dev_attr);
+               device_remove_file(dev, &sda_temp_offset[i].dev_attr);
+               if (i >= NUM_TEMP_ALARM)
+                       continue;
+               device_remove_file(dev, &sda_temp_alarm[i].dev_attr);
+       }
+
+       device_remove_file(dev, &sda_caseopen[0].dev_attr);
+       device_remove_file(dev, &sda_caseopen[1].dev_attr);
+
+       device_remove_file(dev, &dev_attr_name);
+       device_remove_file(dev, &dev_attr_cpu0_vid);
+}
+
+/* Get the monitoring functions started */
+static inline void nct6775_init_device(struct nct6775_data *data)
+{
+       int i;
+       u8 tmp, diode;
+
+       /* Start monitoring if needed */
+       if (data->REG_CONFIG) {
+               tmp = nct6775_read_value(data, data->REG_CONFIG);
+               if (!(tmp & 0x01))
+                       nct6775_write_value(data, data->REG_CONFIG, tmp | 0x01);
+       }
+
+       /* Enable temperature sensors if needed */
+       for (i = 0; i < NUM_TEMP; i++) {
+               if (!(data->have_temp & (1 << i)))
+                       continue;
+               if (!data->reg_temp_config[i])
+                       continue;
+               tmp = nct6775_read_value(data, data->reg_temp_config[i]);
+               if (tmp & 0x01)
+                       nct6775_write_value(data, data->reg_temp_config[i],
+                                           tmp & 0xfe);
+       }
+
+       /* Enable VBAT monitoring if needed */
+       tmp = nct6775_read_value(data, data->REG_VBAT);
+       if (!(tmp & 0x01))
+               nct6775_write_value(data, data->REG_VBAT, tmp | 0x01);
+
+       diode = nct6775_read_value(data, data->REG_DIODE);
+
+       for (i = 0; i < data->temp_fixed_num; i++) {
+               if (!(data->have_temp_fixed & (1 << i)))
+                       continue;
+               if ((tmp & (0x02 << i)))        /* diode */
+                       data->temp_type[i] = 3 - ((diode >> i) & 0x02);
+               else                            /* thermistor */
+                       data->temp_type[i] = 4;
+       }
+}
+
+static int
+nct6775_check_fan_inputs(const struct nct6775_sio_data *sio_data,
+                        struct nct6775_data *data)
+{
+       int regval;
+       bool fan3pin, fan3min, fan4pin, fan4min, fan5pin;
+       bool pwm3pin, pwm4pin, pwm5pin;
+       int ret;
+
+       ret = superio_enter(sio_data->sioreg);
+       if (ret)
+               return ret;
+
+       /* fan4 and fan5 share some pins with the GPIO and serial flash */
+       if (data->kind == nct6775) {
+               regval = superio_inb(sio_data->sioreg, 0x2c);
+
+               fan3pin = regval & (1 << 6);
+               fan3min = fan3pin;
+               pwm3pin = regval & (1 << 7);
+
+               /* On NCT6775, fan4 shares pins with the fdc interface */
+               fan4pin = !(superio_inb(sio_data->sioreg, 0x2A) & 0x80);
+               fan4min = 0;
+               fan5pin = 0;
+               pwm4pin = 0;
+               pwm5pin = 0;
+       } else if (data->kind == nct6776) {
+               bool gpok = superio_inb(sio_data->sioreg, 0x27) & 0x80;
+
+               superio_select(sio_data->sioreg, NCT6775_LD_HWM);
+               regval = superio_inb(sio_data->sioreg, SIO_REG_ENABLE);
+
+               if (regval & 0x80)
+                       fan3pin = gpok;
+               else
+                       fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40);
+
+               if (regval & 0x40)
+                       fan4pin = gpok;
+               else
+                       fan4pin = superio_inb(sio_data->sioreg, 0x1C) & 0x01;
+
+               if (regval & 0x20)
+                       fan5pin = gpok;
+               else
+                       fan5pin = superio_inb(sio_data->sioreg, 0x1C) & 0x02;
+
+               fan4min = fan4pin;
+               fan3min = fan3pin;
+               pwm3pin = fan3pin;
+               pwm4pin = 0;
+               pwm5pin = 0;
+       } else {        /* NCT6779D */
+               regval = superio_inb(sio_data->sioreg, 0x1c);
+
+               fan3pin = !(regval & (1 << 5));
+               fan4pin = !(regval & (1 << 6));
+               fan5pin = !(regval & (1 << 7));
+
+               pwm3pin = !(regval & (1 << 0));
+               pwm4pin = !(regval & (1 << 1));
+               pwm5pin = !(regval & (1 << 2));
+
+               fan3min = fan3pin;
+               fan4min = fan4pin;
+       }
+
+       superio_exit(sio_data->sioreg);
+
+       data->has_fan = data->has_fan_min = 0x03; /* fan1 and fan2 */
+       data->has_fan |= fan3pin << 2;
+       data->has_fan_min |= fan3min << 2;
+
+       data->has_fan |= (fan4pin << 3) | (fan5pin << 4);
+       data->has_fan_min |= (fan4min << 3) | (fan5pin << 4);
+
+       data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) | (pwm5pin << 4);
+
+       return 0;
+}
+
+static void add_temp_sensors(struct nct6775_data *data, const u16 *regp,
+                            int *available, int *mask)
+{
+       int i;
+       u8 src;
+
+       for (i = 0; i < data->pwm_num && *available; i++) {
+               int index;
+
+               if (!regp[i])
+                       continue;
+               src = nct6775_read_value(data, regp[i]);
+               src &= 0x1f;
+               if (!src || (*mask & (1 << src)))
+                       continue;
+               if (src >= data->temp_label_num ||
+                   !strlen(data->temp_label[src]))
+                       continue;
+
+               index = __ffs(*available);
+               nct6775_write_value(data, data->REG_TEMP_SOURCE[index], src);
+               *available &= ~(1 << index);
+               *mask |= 1 << src;
+       }
+}
+
+static int nct6775_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct nct6775_sio_data *sio_data = dev->platform_data;
+       struct nct6775_data *data;
+       struct resource *res;
+       int i, s, err = 0;
+       int src, mask, available;
+       const u16 *reg_temp, *reg_temp_over, *reg_temp_hyst, *reg_temp_config;
+       const u16 *reg_temp_alternate, *reg_temp_crit;
+       int num_reg_temp;
+       bool have_vid = false;
+       u8 cr2a;
+
+       res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+       if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH,
+                                DRVNAME))
+               return -EBUSY;
+
+       data = devm_kzalloc(&pdev->dev, sizeof(struct nct6775_data),
+                           GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->kind = sio_data->kind;
+       data->addr = res->start;
+       mutex_init(&data->update_lock);
+       data->name = nct6775_device_names[data->kind];
+       data->bank = 0xff;              /* Force initial bank selection */
+       platform_set_drvdata(pdev, data);
+
+       switch (data->kind) {
+       case nct6775:
+               data->in_num = 9;
+               data->pwm_num = 3;
+               data->auto_pwm_num = 6;
+               data->has_fan_div = true;
+               data->temp_fixed_num = 3;
+
+               data->ALARM_BITS = NCT6775_ALARM_BITS;
+
+               data->fan_from_reg = fan_from_reg16;
+               data->fan_from_reg_min = fan_from_reg8;
+               data->target_temp_mask = 0x7f;
+               data->tolerance_mask = 0x0f;
+               data->speed_tolerance_limit = 15;
+
+               data->temp_label = nct6775_temp_label;
+               data->temp_label_num = ARRAY_SIZE(nct6775_temp_label);
+
+               data->REG_CONFIG = NCT6775_REG_CONFIG;
+               data->REG_VBAT = NCT6775_REG_VBAT;
+               data->REG_DIODE = NCT6775_REG_DIODE;
+               data->REG_VIN = NCT6775_REG_IN;
+               data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN;
+               data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX;
+               data->REG_TARGET = NCT6775_REG_TARGET;
+               data->REG_FAN = NCT6775_REG_FAN;
+               data->REG_FAN_MODE = NCT6775_REG_FAN_MODE;
+               data->REG_FAN_MIN = NCT6775_REG_FAN_MIN;
+               data->REG_FAN_PULSES = NCT6775_REG_FAN_PULSES;
+               data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME;
+               data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME;
+               data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME;
+               data->REG_PWM[0] = NCT6775_REG_PWM;
+               data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT;
+               data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT;
+               data->REG_PWM[3] = NCT6775_REG_FAN_MAX_OUTPUT;
+               data->REG_PWM[4] = NCT6775_REG_FAN_STEP_OUTPUT;
+               data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP;
+               data->REG_PWM_READ = NCT6775_REG_PWM_READ;
+               data->REG_PWM_MODE = NCT6775_REG_PWM_MODE;
+               data->PWM_MODE_MASK = NCT6775_PWM_MODE_MASK;
+               data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP;
+               data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM;
+               data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP;
+               data->REG_CRITICAL_TEMP_TOLERANCE
+                 = NCT6775_REG_CRITICAL_TEMP_TOLERANCE;
+               data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET;
+               data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE;
+               data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL;
+               data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL;
+               data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP;
+               data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL;
+               data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE;
+               data->REG_ALARM = NCT6775_REG_ALARM;
+
+               reg_temp = NCT6775_REG_TEMP;
+               num_reg_temp = ARRAY_SIZE(NCT6775_REG_TEMP);
+               reg_temp_over = NCT6775_REG_TEMP_OVER;
+               reg_temp_hyst = NCT6775_REG_TEMP_HYST;
+               reg_temp_config = NCT6775_REG_TEMP_CONFIG;
+               reg_temp_alternate = NCT6775_REG_TEMP_ALTERNATE;
+               reg_temp_crit = NCT6775_REG_TEMP_CRIT;
+
+               break;
+       case nct6776:
+               data->in_num = 9;
+               data->pwm_num = 3;
+               data->auto_pwm_num = 4;
+               data->has_fan_div = false;
+               data->temp_fixed_num = 3;
+
+               data->ALARM_BITS = NCT6776_ALARM_BITS;
+
+               data->fan_from_reg = fan_from_reg13;
+               data->fan_from_reg_min = fan_from_reg13;
+               data->target_temp_mask = 0xff;
+               data->tolerance_mask = 0x07;
+               data->speed_tolerance_limit = 63;
+
+               data->temp_label = nct6776_temp_label;
+               data->temp_label_num = ARRAY_SIZE(nct6776_temp_label);
+
+               data->REG_CONFIG = NCT6775_REG_CONFIG;
+               data->REG_VBAT = NCT6775_REG_VBAT;
+               data->REG_DIODE = NCT6775_REG_DIODE;
+               data->REG_VIN = NCT6775_REG_IN;
+               data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN;
+               data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX;
+               data->REG_TARGET = NCT6775_REG_TARGET;
+               data->REG_FAN = NCT6775_REG_FAN;
+               data->REG_FAN_MODE = NCT6775_REG_FAN_MODE;
+               data->REG_FAN_MIN = NCT6776_REG_FAN_MIN;
+               data->REG_FAN_PULSES = NCT6776_REG_FAN_PULSES;
+               data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME;
+               data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME;
+               data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME;
+               data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H;
+               data->REG_PWM[0] = NCT6775_REG_PWM;
+               data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT;
+               data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT;
+               data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP;
+               data->REG_PWM[6] = NCT6776_REG_WEIGHT_DUTY_BASE;
+               data->REG_PWM_READ = NCT6775_REG_PWM_READ;
+               data->REG_PWM_MODE = NCT6776_REG_PWM_MODE;
+               data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK;
+               data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP;
+               data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM;
+               data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP;
+               data->REG_CRITICAL_TEMP_TOLERANCE
+                 = NCT6775_REG_CRITICAL_TEMP_TOLERANCE;
+               data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET;
+               data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE;
+               data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL;
+               data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL;
+               data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP;
+               data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL;
+               data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE;
+               data->REG_ALARM = NCT6775_REG_ALARM;
+
+               reg_temp = NCT6775_REG_TEMP;
+               num_reg_temp = ARRAY_SIZE(NCT6775_REG_TEMP);
+               reg_temp_over = NCT6775_REG_TEMP_OVER;
+               reg_temp_hyst = NCT6775_REG_TEMP_HYST;
+               reg_temp_config = NCT6776_REG_TEMP_CONFIG;
+               reg_temp_alternate = NCT6776_REG_TEMP_ALTERNATE;
+               reg_temp_crit = NCT6776_REG_TEMP_CRIT;
+
+               break;
+       case nct6779:
+               data->in_num = 15;
+               data->pwm_num = 5;
+               data->auto_pwm_num = 4;
+               data->has_fan_div = false;
+               data->temp_fixed_num = 6;
+
+               data->ALARM_BITS = NCT6779_ALARM_BITS;
+
+               data->fan_from_reg = fan_from_reg13;
+               data->fan_from_reg_min = fan_from_reg13;
+               data->target_temp_mask = 0xff;
+               data->tolerance_mask = 0x07;
+               data->speed_tolerance_limit = 63;
+
+               data->temp_label = nct6779_temp_label;
+               data->temp_label_num = ARRAY_SIZE(nct6779_temp_label);
+
+               data->REG_CONFIG = NCT6775_REG_CONFIG;
+               data->REG_VBAT = NCT6775_REG_VBAT;
+               data->REG_DIODE = NCT6775_REG_DIODE;
+               data->REG_VIN = NCT6779_REG_IN;
+               data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN;
+               data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX;
+               data->REG_TARGET = NCT6775_REG_TARGET;
+               data->REG_FAN = NCT6779_REG_FAN;
+               data->REG_FAN_MODE = NCT6775_REG_FAN_MODE;
+               data->REG_FAN_MIN = NCT6776_REG_FAN_MIN;
+               data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES;
+               data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME;
+               data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME;
+               data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME;
+               data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H;
+               data->REG_PWM[0] = NCT6775_REG_PWM;
+               data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT;
+               data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT;
+               data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP;
+               data->REG_PWM[6] = NCT6776_REG_WEIGHT_DUTY_BASE;
+               data->REG_PWM_READ = NCT6775_REG_PWM_READ;
+               data->REG_PWM_MODE = NCT6776_REG_PWM_MODE;
+               data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK;
+               data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP;
+               data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM;
+               data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP;
+               data->REG_CRITICAL_TEMP_TOLERANCE
+                 = NCT6775_REG_CRITICAL_TEMP_TOLERANCE;
+               data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET;
+               data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE;
+               data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL;
+               data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL;
+               data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP;
+               data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL;
+               data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE;
+               data->REG_ALARM = NCT6779_REG_ALARM;
+
+               reg_temp = NCT6779_REG_TEMP;
+               num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP);
+               reg_temp_over = NCT6779_REG_TEMP_OVER;
+               reg_temp_hyst = NCT6779_REG_TEMP_HYST;
+               reg_temp_config = NCT6779_REG_TEMP_CONFIG;
+               reg_temp_alternate = NCT6779_REG_TEMP_ALTERNATE;
+               reg_temp_crit = NCT6779_REG_TEMP_CRIT;
+
+               break;
+       default:
+               return -ENODEV;
+       }
+       data->have_in = (1 << data->in_num) - 1;
+       data->have_temp = 0;
+
+       /*
+        * On some boards, not all available temperature sources are monitored,
+        * even though some of the monitoring registers are unused.
+        * Get list of unused monitoring registers, then detect if any fan
+        * controls are configured to use unmonitored temperature sources.
+        * If so, assign the unmonitored temperature sources to available
+        * monitoring registers.
+        */
+       mask = 0;
+       available = 0;
+       for (i = 0; i < num_reg_temp; i++) {
+               if (reg_temp[i] == 0)
+                       continue;
+
+               src = nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f;
+               if (!src || (mask & (1 << src)))
+                       available |= 1 << i;
+
+               mask |= 1 << src;
+       }
+
+       /*
+        * Now find unmonitored temperature registers and enable monitoring
+        * if additional monitoring registers are available.
+        */
+       add_temp_sensors(data, data->REG_TEMP_SEL, &available, &mask);
+       add_temp_sensors(data, data->REG_WEIGHT_TEMP_SEL, &available, &mask);
+
+       mask = 0;
+       s = NUM_TEMP_FIXED;     /* First dynamic temperature attribute */
+       for (i = 0; i < num_reg_temp; i++) {
+               if (reg_temp[i] == 0)
+                       continue;
+
+               src = nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f;
+               if (!src || (mask & (1 << src)))
+                       continue;
+
+               if (src >= data->temp_label_num ||
+                   !strlen(data->temp_label[src])) {
+                       dev_info(dev,
+                                "Invalid temperature source %d at index %d, source register 0x%x, temp register 0x%x\n",
+                                src, i, data->REG_TEMP_SOURCE[i], reg_temp[i]);
+                       continue;
+               }
+
+               mask |= 1 << src;
+
+               /* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */
+               if (src <= data->temp_fixed_num) {
+                       data->have_temp |= 1 << (src - 1);
+                       data->have_temp_fixed |= 1 << (src - 1);
+                       data->reg_temp[0][src - 1] = reg_temp[i];
+                       data->reg_temp[1][src - 1] = reg_temp_over[i];
+                       data->reg_temp[2][src - 1] = reg_temp_hyst[i];
+                       data->reg_temp_config[src - 1] = reg_temp_config[i];
+                       data->temp_src[src - 1] = src;
+                       continue;
+               }
+
+               if (s >= NUM_TEMP)
+                       continue;
+
+               /* Use dynamic index for other sources */
+               data->have_temp |= 1 << s;
+               data->reg_temp[0][s] = reg_temp[i];
+               data->reg_temp[1][s] = reg_temp_over[i];
+               data->reg_temp[2][s] = reg_temp_hyst[i];
+               data->reg_temp_config[s] = reg_temp_config[i];
+               if (reg_temp_crit[src - 1])
+                       data->reg_temp[3][s] = reg_temp_crit[src - 1];
+
+               data->temp_src[s] = src;
+               s++;
+       }
+
+#ifdef USE_ALTERNATE
+       /*
+        * Go through the list of alternate temp registers and enable
+        * if possible.
+        * The temperature is already monitored if the respective bit in <mask>
+        * is set.
+        */
+       for (i = 0; i < data->temp_label_num - 1; i++) {
+               if (!reg_temp_alternate[i])
+                       continue;
+               if (mask & (1 << (i + 1)))
+                       continue;
+               if (i < data->temp_fixed_num) {
+                       if (data->have_temp & (1 << i))
+                               continue;
+                       data->have_temp |= 1 << i;
+                       data->have_temp_fixed |= 1 << i;
+                       data->reg_temp[0][i] = reg_temp_alternate[i];
+                       data->reg_temp[1][i] = reg_temp_over[i];
+                       data->reg_temp[2][i] = reg_temp_hyst[i];
+                       data->temp_src[i] = i + 1;
+                       continue;
+               }
+
+               if (s >= NUM_TEMP)      /* Abort if no more space */
+                       break;
+
+               data->have_temp |= 1 << s;
+               data->reg_temp[0][s] = reg_temp_alternate[i];
+               data->temp_src[s] = i + 1;
+               s++;
+       }
+#endif /* USE_ALTERNATE */
+
+       /* Initialize the chip */
+       nct6775_init_device(data);
+
+       err = superio_enter(sio_data->sioreg);
+       if (err)
+               return err;
+
+       cr2a = superio_inb(sio_data->sioreg, 0x2a);
+       switch (data->kind) {
+       case nct6775:
+               have_vid = (cr2a & 0x40);
+               break;
+       case nct6776:
+               have_vid = (cr2a & 0x60) == 0x40;
+               break;
+       case nct6779:
+               break;
+       }
+
+       /*
+        * Read VID value
+        * We can get the VID input values directly at logical device D 0xe3.
+        */
+       if (have_vid) {
+               superio_select(sio_data->sioreg, NCT6775_LD_VID);
+               data->vid = superio_inb(sio_data->sioreg, 0xe3);
+               data->vrm = vid_which_vrm();
+       }
+
+       if (fan_debounce) {
+               u8 tmp;
+
+               superio_select(sio_data->sioreg, NCT6775_LD_HWM);
+               tmp = superio_inb(sio_data->sioreg,
+                                 NCT6775_REG_CR_FAN_DEBOUNCE);
+               switch (data->kind) {
+               case nct6775:
+                       tmp |= 0x1e;
+                       break;
+               case nct6776:
+               case nct6779:
+                       tmp |= 0x3e;
+                       break;
+               }
+               superio_outb(sio_data->sioreg, NCT6775_REG_CR_FAN_DEBOUNCE,
+                            tmp);
+               dev_info(&pdev->dev, "Enabled fan debounce for chip %s\n",
+                        data->name);
+       }
+
+       superio_exit(sio_data->sioreg);
+
+       if (have_vid) {
+               err = device_create_file(dev, &dev_attr_cpu0_vid);
+               if (err)
+                       return err;
+       }
+
+       err = nct6775_check_fan_inputs(sio_data, data);
+       if (err)
+               goto exit_remove;
+
+       /* Read fan clock dividers immediately */
+       nct6775_init_fan_common(dev, data);
+
+       /* Register sysfs hooks */
+       for (i = 0; i < data->pwm_num; i++) {
+               if (!(data->has_pwm & (1 << i)))
+                       continue;
+
+               err = sysfs_create_group(&dev->kobj, &nct6775_group_pwm[i]);
+               if (err)
+                       goto exit_remove;
+
+               if (data->REG_PWM[3]) {
+                       err = device_create_file(dev,
+                                       &sda_pwm_max[i].dev_attr);
+                       if (err)
+                               goto exit_remove;
+               }
+               if (data->REG_PWM[4]) {
+                       err = device_create_file(dev,
+                                       &sda_pwm_step[i].dev_attr);
+                       if (err)
+                               goto exit_remove;
+               }
+               if (data->REG_PWM[6]) {
+                       err = device_create_file(dev,
+                                       &sda_weight_duty_base[i].dev_attr);
+                       if (err)
+                               goto exit_remove;
+               }
+       }
+       for (i = 0; i < ARRAY_SIZE(sda_auto_pwm_arrays); i++) {
+               struct sensor_device_attribute_2 *attr =
+                       &sda_auto_pwm_arrays[i];
+
+               if (!(data->has_pwm & (1 << attr->nr)))
+                       continue;
+               if (attr->index > data->auto_pwm_num)
+                       continue;
+               err = device_create_file(dev, &attr->dev_attr);
+               if (err)
+                       goto exit_remove;
+       }
+
+       for (i = 0; i < data->in_num; i++) {
+               if (!(data->have_in & (1 << i)))
+                       continue;
+               err = sysfs_create_group(&dev->kobj, &nct6775_group_in[i]);
+               if (err)
+                       goto exit_remove;
+       }
+
+       for (i = 0; i < 5; i++) {
+               if (data->has_fan & (1 << i)) {
+                       err = device_create_file(dev,
+                                                &sda_fan_input[i].dev_attr);
+                       if (err)
+                               goto exit_remove;
+                       err = device_create_file(dev,
+                                                &sda_fan_alarm[i].dev_attr);
+                       if (err)
+                               goto exit_remove;
+                       if (data->kind != nct6776 &&
+                           data->kind != nct6779) {
+                               err = device_create_file(dev,
+                                               &sda_fan_div[i].dev_attr);
+                               if (err)
+                                       goto exit_remove;
+                       }
+                       if (data->has_fan_min & (1 << i)) {
+                               err = device_create_file(dev,
+                                               &sda_fan_min[i].dev_attr);
+                               if (err)
+                                       goto exit_remove;
+                       }
+                       err = device_create_file(dev,
+                                               &sda_fan_pulses[i].dev_attr);
+                       if (err)
+                               goto exit_remove;
+               }
+       }
+
+       for (i = 0; i < NUM_TEMP; i++) {
+               if (!(data->have_temp & (1 << i)))
+                       continue;
+               err = device_create_file(dev, &sda_temp_input[i].dev_attr);
+               if (err)
+                       goto exit_remove;
+               if (data->temp_label) {
+                       err = device_create_file(dev,
+                                                &sda_temp_label[i].dev_attr);
+                       if (err)
+                               goto exit_remove;
+               }
+               if (data->reg_temp[1][i]) {
+                       err = device_create_file(dev,
+                                                &sda_temp_max[i].dev_attr);
+                       if (err)
+                               goto exit_remove;
+               }
+               if (data->reg_temp[2][i]) {
+                       err = device_create_file(dev,
+                                       &sda_temp_max_hyst[i].dev_attr);
+                       if (err)
+                               goto exit_remove;
+               }
+               if (data->reg_temp[3][i]) {
+                       err = device_create_file(dev,
+                                                &sda_temp_crit[i].dev_attr);
+                       if (err)
+                               goto exit_remove;
+               }
+               if (!(data->have_temp_fixed & (1 << i)))
+                       continue;
+               err = device_create_file(dev, &sda_temp_type[i].dev_attr);
+               if (err)
+                       goto exit_remove;
+               err = device_create_file(dev, &sda_temp_offset[i].dev_attr);
+               if (err)
+                       goto exit_remove;
+               if (i >= NUM_TEMP_ALARM ||
+                   data->ALARM_BITS[TEMP_ALARM_BASE + i] < 0)
+                       continue;
+               err = device_create_file(dev, &sda_temp_alarm[i].dev_attr);
+               if (err)
+                       goto exit_remove;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(sda_caseopen); i++) {
+               if (data->ALARM_BITS[INTRUSION_ALARM_BASE + i] < 0)
+                       continue;
+               err = device_create_file(dev, &sda_caseopen[i].dev_attr);
+               if (err)
+                       goto exit_remove;
+       }
+
+       err = device_create_file(dev, &dev_attr_name);
+       if (err)
+               goto exit_remove;
+
+       data->hwmon_dev = hwmon_device_register(dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
+               goto exit_remove;
+       }
+
+       return 0;
+
+exit_remove:
+       nct6775_device_remove_files(dev);
+       return err;
+}
+
+static int nct6775_remove(struct platform_device *pdev)
+{
+       struct nct6775_data *data = platform_get_drvdata(pdev);
+
+       hwmon_device_unregister(data->hwmon_dev);
+       nct6775_device_remove_files(&pdev->dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int nct6775_suspend(struct device *dev)
+{
+       struct nct6775_data *data = nct6775_update_device(dev);
+       struct nct6775_sio_data *sio_data = dev->platform_data;
+
+       mutex_lock(&data->update_lock);
+       data->vbat = nct6775_read_value(data, data->REG_VBAT);
+       if (sio_data->kind == nct6775) {
+               data->fandiv1 = nct6775_read_value(data, NCT6775_REG_FANDIV1);
+               data->fandiv2 = nct6775_read_value(data, NCT6775_REG_FANDIV2);
+       }
+       mutex_unlock(&data->update_lock);
+
+       return 0;
+}
+
+static int nct6775_resume(struct device *dev)
+{
+       struct nct6775_data *data = dev_get_drvdata(dev);
+       struct nct6775_sio_data *sio_data = dev->platform_data;
+       int i, j;
+
+       mutex_lock(&data->update_lock);
+       data->bank = 0xff;              /* Force initial bank selection */
+
+       /* Restore limits */
+       for (i = 0; i < data->in_num; i++) {
+               if (!(data->have_in & (1 << i)))
+                       continue;
+
+               nct6775_write_value(data, data->REG_IN_MINMAX[0][i],
+                                   data->in[i][1]);
+               nct6775_write_value(data, data->REG_IN_MINMAX[1][i],
+                                   data->in[i][2]);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) {
+               if (!(data->has_fan_min & (1 << i)))
+                       continue;
+
+               nct6775_write_value(data, data->REG_FAN_MIN[i],
+                                   data->fan_min[i]);
+       }
+
+       for (i = 0; i < NUM_TEMP; i++) {
+               if (!(data->have_temp & (1 << i)))
+                       continue;
+
+               for (j = 1; j < ARRAY_SIZE(data->reg_temp); j++)
+                       if (data->reg_temp[j][i])
+                               nct6775_write_temp(data, data->reg_temp[j][i],
+                                                  data->temp[j][i]);
+       }
+
+       /* Restore other settings */
+       nct6775_write_value(data, data->REG_VBAT, data->vbat);
+       if (sio_data->kind == nct6775) {
+               nct6775_write_value(data, NCT6775_REG_FANDIV1, data->fandiv1);
+               nct6775_write_value(data, NCT6775_REG_FANDIV2, data->fandiv2);
+       }
+
+       /* Force re-reading all values */
+       data->valid = false;
+       mutex_unlock(&data->update_lock);
+
+       return 0;
+}
+
+static const struct dev_pm_ops nct6775_dev_pm_ops = {
+       .suspend = nct6775_suspend,
+       .resume = nct6775_resume,
+};
+
+#define NCT6775_DEV_PM_OPS     (&nct6775_dev_pm_ops)
+#else
+#define NCT6775_DEV_PM_OPS     NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver nct6775_driver = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = DRVNAME,
+               .pm     = NCT6775_DEV_PM_OPS,
+       },
+       .probe          = nct6775_probe,
+       .remove         = nct6775_remove,
+};
+
+static const char * const nct6775_sio_names[] __initconst = {
+       "NCT6775F",
+       "NCT6776D/F",
+       "NCT6779D",
+};
+
+/* nct6775_find() looks for a '627 in the Super-I/O config space */
+static int __init nct6775_find(int sioaddr, unsigned short *addr,
+                              struct nct6775_sio_data *sio_data)
+{
+       u16 val;
+       int err;
+
+       err = superio_enter(sioaddr);
+       if (err)
+               return err;
+
+       if (force_id)
+               val = force_id;
+       else
+               val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8)
+                   | superio_inb(sioaddr, SIO_REG_DEVID + 1);
+       switch (val & SIO_ID_MASK) {
+       case SIO_NCT6775_ID:
+               sio_data->kind = nct6775;
+               break;
+       case SIO_NCT6776_ID:
+               sio_data->kind = nct6776;
+               break;
+       case SIO_NCT6779_ID:
+               sio_data->kind = nct6779;
+               break;
+       default:
+               if (val != 0xffff)
+                       pr_debug("unsupported chip ID: 0x%04x\n", val);
+               superio_exit(sioaddr);
+               return -ENODEV;
+       }
+
+       /* We have a known chip, find the HWM I/O address */
+       superio_select(sioaddr, NCT6775_LD_HWM);
+       val = (superio_inb(sioaddr, SIO_REG_ADDR) << 8)
+           | superio_inb(sioaddr, SIO_REG_ADDR + 1);
+       *addr = val & IOREGION_ALIGNMENT;
+       if (*addr == 0) {
+               pr_err("Refusing to enable a Super-I/O device with a base I/O port 0\n");
+               superio_exit(sioaddr);
+               return -ENODEV;
+       }
+
+       /* Activate logical device if needed */
+       val = superio_inb(sioaddr, SIO_REG_ENABLE);
+       if (!(val & 0x01)) {
+               pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n");
+               superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01);
+       }
+
+       superio_exit(sioaddr);
+       pr_info("Found %s or compatible chip at %#x\n",
+               nct6775_sio_names[sio_data->kind], *addr);
+       sio_data->sioreg = sioaddr;
+
+       return 0;
+}
+
+/*
+ * when Super-I/O functions move to a separate file, the Super-I/O
+ * bus will manage the lifetime of the device and this module will only keep
+ * track of the nct6775 driver. But since we platform_device_alloc(), we
+ * must keep track of the device
+ */
+static struct platform_device *pdev;
+
+static int __init sensors_nct6775_init(void)
+{
+       int err;
+       unsigned short address;
+       struct resource res;
+       struct nct6775_sio_data sio_data;
+
+       /*
+        * initialize sio_data->kind and sio_data->sioreg.
+        *
+        * when Super-I/O functions move to a separate file, the Super-I/O
+        * driver will probe 0x2e and 0x4e and auto-detect the presence of a
+        * nct6775 hardware monitor, and call probe()
+        */
+       if (nct6775_find(0x2e, &address, &sio_data) &&
+           nct6775_find(0x4e, &address, &sio_data))
+               return -ENODEV;
+
+       err = platform_driver_register(&nct6775_driver);
+       if (err)
+               goto exit;
+
+       pdev = platform_device_alloc(DRVNAME, address);
+       if (!pdev) {
+               err = -ENOMEM;
+               pr_err("Device allocation failed\n");
+               goto exit_unregister;
+       }
+
+       err = platform_device_add_data(pdev, &sio_data,
+                                      sizeof(struct nct6775_sio_data));
+       if (err) {
+               pr_err("Platform data allocation failed\n");
+               goto exit_device_put;
+       }
+
+       memset(&res, 0, sizeof(res));
+       res.name = DRVNAME;
+       res.start = address + IOREGION_OFFSET;
+       res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1;
+       res.flags = IORESOURCE_IO;
+
+       err = acpi_check_resource_conflict(&res);
+       if (err)
+               goto exit_device_put;
+
+       err = platform_device_add_resources(pdev, &res, 1);
+       if (err) {
+               pr_err("Device resource addition failed (%d)\n", err);
+               goto exit_device_put;
+       }
+
+       /* platform_device_add calls probe() */
+       err = platform_device_add(pdev);
+       if (err) {
+               pr_err("Device addition failed (%d)\n", err);
+               goto exit_device_put;
+       }
+
+       return 0;
+
+exit_device_put:
+       platform_device_put(pdev);
+exit_unregister:
+       platform_driver_unregister(&nct6775_driver);
+exit:
+       return err;
+}
+
+static void __exit sensors_nct6775_exit(void)
+{
+       platform_device_unregister(pdev);
+       platform_driver_unregister(&nct6775_driver);
+}
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("NCT6775F/NCT6776F/NCT6779D driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_nct6775_init);
+module_exit(sensors_nct6775_exit);
index b5f63f9c0ce1754f6c1b6a614e7c402f24382b81..d6d640a733d5425730cbd68cc15fbcfe1c229891 100644 (file)
 #include <linux/math64.h>
 #include <linux/platform_device.h>
 #include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 
 #include <linux/platform_data/ntc_thermistor.h>
 
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/consumer.h>
+
 #include <linux/hwmon.h>
 #include <linux/hwmon-sysfs.h>
 
 struct ntc_compensation {
-       int             temp_C;
+       int             temp_c;
        unsigned int    ohm;
 };
 
+static const struct platform_device_id ntc_thermistor_id[] = {
+       { "ncp15wb473", TYPE_NCPXXWB473 },
+       { "ncp18wb473", TYPE_NCPXXWB473 },
+       { "ncp21wb473", TYPE_NCPXXWB473 },
+       { "ncp03wb473", TYPE_NCPXXWB473 },
+       { "ncp15wl333", TYPE_NCPXXWL333 },
+       { },
+};
+
 /*
  * A compensation table should be sorted by the values of .ohm
  * in descending order.
@@ -44,76 +60,76 @@ struct ntc_compensation {
  * Thermistors Datasheet
  */
 static const struct ntc_compensation ncpXXwb473[] = {
-       { .temp_C       = -40, .ohm     = 1747920 },
-       { .temp_C       = -35, .ohm     = 1245428 },
-       { .temp_C       = -30, .ohm     = 898485 },
-       { .temp_C       = -25, .ohm     = 655802 },
-       { .temp_C       = -20, .ohm     = 483954 },
-       { .temp_C       = -15, .ohm     = 360850 },
-       { .temp_C       = -10, .ohm     = 271697 },
-       { .temp_C       = -5, .ohm      = 206463 },
-       { .temp_C       = 0, .ohm       = 158214 },
-       { .temp_C       = 5, .ohm       = 122259 },
-       { .temp_C       = 10, .ohm      = 95227 },
-       { .temp_C       = 15, .ohm      = 74730 },
-       { .temp_C       = 20, .ohm      = 59065 },
-       { .temp_C       = 25, .ohm      = 47000 },
-       { .temp_C       = 30, .ohm      = 37643 },
-       { .temp_C       = 35, .ohm      = 30334 },
-       { .temp_C       = 40, .ohm      = 24591 },
-       { .temp_C       = 45, .ohm      = 20048 },
-       { .temp_C       = 50, .ohm      = 16433 },
-       { .temp_C       = 55, .ohm      = 13539 },
-       { .temp_C       = 60, .ohm      = 11209 },
-       { .temp_C       = 65, .ohm      = 9328 },
-       { .temp_C       = 70, .ohm      = 7798 },
-       { .temp_C       = 75, .ohm      = 6544 },
-       { .temp_C       = 80, .ohm      = 5518 },
-       { .temp_C       = 85, .ohm      = 4674 },
-       { .temp_C       = 90, .ohm      = 3972 },
-       { .temp_C       = 95, .ohm      = 3388 },
-       { .temp_C       = 100, .ohm     = 2902 },
-       { .temp_C       = 105, .ohm     = 2494 },
-       { .temp_C       = 110, .ohm     = 2150 },
-       { .temp_C       = 115, .ohm     = 1860 },
-       { .temp_C       = 120, .ohm     = 1615 },
-       { .temp_C       = 125, .ohm     = 1406 },
+       { .temp_c       = -40, .ohm     = 1747920 },
+       { .temp_c       = -35, .ohm     = 1245428 },
+       { .temp_c       = -30, .ohm     = 898485 },
+       { .temp_c       = -25, .ohm     = 655802 },
+       { .temp_c       = -20, .ohm     = 483954 },
+       { .temp_c       = -15, .ohm     = 360850 },
+       { .temp_c       = -10, .ohm     = 271697 },
+       { .temp_c       = -5, .ohm      = 206463 },
+       { .temp_c       = 0, .ohm       = 158214 },
+       { .temp_c       = 5, .ohm       = 122259 },
+       { .temp_c       = 10, .ohm      = 95227 },
+       { .temp_c       = 15, .ohm      = 74730 },
+       { .temp_c       = 20, .ohm      = 59065 },
+       { .temp_c       = 25, .ohm      = 47000 },
+       { .temp_c       = 30, .ohm      = 37643 },
+       { .temp_c       = 35, .ohm      = 30334 },
+       { .temp_c       = 40, .ohm      = 24591 },
+       { .temp_c       = 45, .ohm      = 20048 },
+       { .temp_c       = 50, .ohm      = 16433 },
+       { .temp_c       = 55, .ohm      = 13539 },
+       { .temp_c       = 60, .ohm      = 11209 },
+       { .temp_c       = 65, .ohm      = 9328 },
+       { .temp_c       = 70, .ohm      = 7798 },
+       { .temp_c       = 75, .ohm      = 6544 },
+       { .temp_c       = 80, .ohm      = 5518 },
+       { .temp_c       = 85, .ohm      = 4674 },
+       { .temp_c       = 90, .ohm      = 3972 },
+       { .temp_c       = 95, .ohm      = 3388 },
+       { .temp_c       = 100, .ohm     = 2902 },
+       { .temp_c       = 105, .ohm     = 2494 },
+       { .temp_c       = 110, .ohm     = 2150 },
+       { .temp_c       = 115, .ohm     = 1860 },
+       { .temp_c       = 120, .ohm     = 1615 },
+       { .temp_c       = 125, .ohm     = 1406 },
 };
 static const struct ntc_compensation ncpXXwl333[] = {
-       { .temp_C       = -40, .ohm     = 1610154 },
-       { .temp_C       = -35, .ohm     = 1130850 },
-       { .temp_C       = -30, .ohm     = 802609 },
-       { .temp_C       = -25, .ohm     = 575385 },
-       { .temp_C       = -20, .ohm     = 416464 },
-       { .temp_C       = -15, .ohm     = 304219 },
-       { .temp_C       = -10, .ohm     = 224193 },
-       { .temp_C       = -5, .ohm      = 166623 },
-       { .temp_C       = 0, .ohm       = 124850 },
-       { .temp_C       = 5, .ohm       = 94287 },
-       { .temp_C       = 10, .ohm      = 71747 },
-       { .temp_C       = 15, .ohm      = 54996 },
-       { .temp_C       = 20, .ohm      = 42455 },
-       { .temp_C       = 25, .ohm      = 33000 },
-       { .temp_C       = 30, .ohm      = 25822 },
-       { .temp_C       = 35, .ohm      = 20335 },
-       { .temp_C       = 40, .ohm      = 16115 },
-       { .temp_C       = 45, .ohm      = 12849 },
-       { .temp_C       = 50, .ohm      = 10306 },
-       { .temp_C       = 55, .ohm      = 8314 },
-       { .temp_C       = 60, .ohm      = 6746 },
-       { .temp_C       = 65, .ohm      = 5503 },
-       { .temp_C       = 70, .ohm      = 4513 },
-       { .temp_C       = 75, .ohm      = 3721 },
-       { .temp_C       = 80, .ohm      = 3084 },
-       { .temp_C       = 85, .ohm      = 2569 },
-       { .temp_C       = 90, .ohm      = 2151 },
-       { .temp_C       = 95, .ohm      = 1809 },
-       { .temp_C       = 100, .ohm     = 1529 },
-       { .temp_C       = 105, .ohm     = 1299 },
-       { .temp_C       = 110, .ohm     = 1108 },
-       { .temp_C       = 115, .ohm     = 949 },
-       { .temp_C       = 120, .ohm     = 817 },
-       { .temp_C       = 125, .ohm     = 707 },
+       { .temp_c       = -40, .ohm     = 1610154 },
+       { .temp_c       = -35, .ohm     = 1130850 },
+       { .temp_c       = -30, .ohm     = 802609 },
+       { .temp_c       = -25, .ohm     = 575385 },
+       { .temp_c       = -20, .ohm     = 416464 },
+       { .temp_c       = -15, .ohm     = 304219 },
+       { .temp_c       = -10, .ohm     = 224193 },
+       { .temp_c       = -5, .ohm      = 166623 },
+       { .temp_c       = 0, .ohm       = 124850 },
+       { .temp_c       = 5, .ohm       = 94287 },
+       { .temp_c       = 10, .ohm      = 71747 },
+       { .temp_c       = 15, .ohm      = 54996 },
+       { .temp_c       = 20, .ohm      = 42455 },
+       { .temp_c       = 25, .ohm      = 33000 },
+       { .temp_c       = 30, .ohm      = 25822 },
+       { .temp_c       = 35, .ohm      = 20335 },
+       { .temp_c       = 40, .ohm      = 16115 },
+       { .temp_c       = 45, .ohm      = 12849 },
+       { .temp_c       = 50, .ohm      = 10306 },
+       { .temp_c       = 55, .ohm      = 8314 },
+       { .temp_c       = 60, .ohm      = 6746 },
+       { .temp_c       = 65, .ohm      = 5503 },
+       { .temp_c       = 70, .ohm      = 4513 },
+       { .temp_c       = 75, .ohm      = 3721 },
+       { .temp_c       = 80, .ohm      = 3084 },
+       { .temp_c       = 85, .ohm      = 2569 },
+       { .temp_c       = 90, .ohm      = 2151 },
+       { .temp_c       = 95, .ohm      = 1809 },
+       { .temp_c       = 100, .ohm     = 1529 },
+       { .temp_c       = 105, .ohm     = 1299 },
+       { .temp_c       = 110, .ohm     = 1108 },
+       { .temp_c       = 115, .ohm     = 949 },
+       { .temp_c       = 120, .ohm     = 817 },
+       { .temp_c       = 125, .ohm     = 707 },
 };
 
 struct ntc_data {
@@ -125,6 +141,92 @@ struct ntc_data {
        char name[PLATFORM_NAME_SIZE];
 };
 
+#ifdef CONFIG_OF
+static int ntc_adc_iio_read(struct ntc_thermistor_platform_data *pdata)
+{
+       struct iio_channel *channel = pdata->chan;
+       unsigned int result;
+       int val, ret;
+
+       ret = iio_read_channel_raw(channel, &val);
+       if (ret < 0) {
+               pr_err("read channel() error: %d\n", ret);
+               return ret;
+       }
+
+       /* unit: mV */
+       result = pdata->pullup_uv * val;
+       result >>= 12;
+
+       return result;
+}
+
+static const struct of_device_id ntc_match[] = {
+       { .compatible = "ntc,ncp15wb473",
+               .data = &ntc_thermistor_id[TYPE_NCPXXWB473] },
+       { .compatible = "ntc,ncp18wb473",
+               .data = &ntc_thermistor_id[TYPE_NCPXXWB473] },
+       { .compatible = "ntc,ncp21wb473",
+               .data = &ntc_thermistor_id[TYPE_NCPXXWB473] },
+       { .compatible = "ntc,ncp03wb473",
+               .data = &ntc_thermistor_id[TYPE_NCPXXWB473] },
+       { .compatible = "ntc,ncp15wl333",
+               .data = &ntc_thermistor_id[TYPE_NCPXXWL333] },
+       { },
+};
+MODULE_DEVICE_TABLE(of, ntc_match);
+
+static struct ntc_thermistor_platform_data *
+ntc_thermistor_parse_dt(struct platform_device *pdev)
+{
+       struct iio_channel *chan;
+       struct device_node *np = pdev->dev.of_node;
+       struct ntc_thermistor_platform_data *pdata;
+
+       if (!np)
+               return NULL;
+
+       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return ERR_PTR(-ENOMEM);
+
+       chan = iio_channel_get(&pdev->dev, NULL);
+       if (IS_ERR(chan))
+               return ERR_CAST(chan);
+
+       if (of_property_read_u32(np, "pullup-uv", &pdata->pullup_uv))
+               return ERR_PTR(-ENODEV);
+       if (of_property_read_u32(np, "pullup-ohm", &pdata->pullup_ohm))
+               return ERR_PTR(-ENODEV);
+       if (of_property_read_u32(np, "pulldown-ohm", &pdata->pulldown_ohm))
+               return ERR_PTR(-ENODEV);
+
+       if (of_find_property(np, "connected-positive", NULL))
+               pdata->connect = NTC_CONNECTED_POSITIVE;
+       else /* status change should be possible if not always on. */
+               pdata->connect = NTC_CONNECTED_GROUND;
+
+       pdata->chan = chan;
+       pdata->read_uv = ntc_adc_iio_read;
+
+       return pdata;
+}
+static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata)
+{
+       if (pdata->chan)
+               iio_channel_release(pdata->chan);
+}
+#else
+static struct ntc_thermistor_platform_data *
+ntc_thermistor_parse_dt(struct platform_device *pdev)
+{
+       return NULL;
+}
+
+static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata)
+{ }
+#endif
+
 static inline u64 div64_u64_safe(u64 dividend, u64 divisor)
 {
        if (divisor == 0 && dividend == 0)
@@ -134,37 +236,37 @@ static inline u64 div64_u64_safe(u64 dividend, u64 divisor)
        return div64_u64(dividend, divisor);
 }
 
-static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uV)
+static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uv)
 {
        struct ntc_thermistor_platform_data *pdata = data->pdata;
-       u64 mV = uV / 1000;
-       u64 pmV = pdata->pullup_uV / 1000;
-       u64 N, puO, pdO;
-       puO = pdata->pullup_ohm;
-       pdO = pdata->pulldown_ohm;
+       u64 mv = uv / 1000;
+       u64 pmv = pdata->pullup_uv / 1000;
+       u64 n, puo, pdo;
+       puo = pdata->pullup_ohm;
+       pdo = pdata->pulldown_ohm;
 
-       if (mV == 0) {
+       if (mv == 0) {
                if (pdata->connect == NTC_CONNECTED_POSITIVE)
                        return INT_MAX;
                return 0;
        }
-       if (mV >= pmV)
+       if (mv >= pmv)
                return (pdata->connect == NTC_CONNECTED_POSITIVE) ?
                        0 : INT_MAX;
 
-       if (pdata->connect == NTC_CONNECTED_POSITIVE && puO == 0)
-               N = div64_u64_safe(pdO * (pmV - mV), mV);
-       else if (pdata->connect == NTC_CONNECTED_GROUND && pdO == 0)
-               N = div64_u64_safe(puO * mV, pmV - mV);
+       if (pdata->connect == NTC_CONNECTED_POSITIVE && puo == 0)
+               n = div64_u64_safe(pdo * (pmv - mv), mv);
+       else if (pdata->connect == NTC_CONNECTED_GROUND && pdo == 0)
+               n = div64_u64_safe(puo * mv, pmv - mv);
        else if (pdata->connect == NTC_CONNECTED_POSITIVE)
-               N = div64_u64_safe(pdO * puO * (pmV - mV),
-                               puO * mV - pdO * (pmV - mV));
+               n = div64_u64_safe(pdo * puo * (pmv - mv),
+                               puo * mv - pdo * (pmv - mv));
        else
-               N = div64_u64_safe(pdO * puO * mV, pdO * (pmV - mV) - puO * mV);
+               n = div64_u64_safe(pdo * puo * mv, pdo * (pmv - mv) - puo * mv);
 
-       if (N > INT_MAX)
-               N = INT_MAX;
-       return N;
+       if (n > INT_MAX)
+               n = INT_MAX;
+       return n;
 }
 
 static void lookup_comp(struct ntc_data *data, unsigned int ohm,
@@ -233,7 +335,7 @@ static void lookup_comp(struct ntc_data *data, unsigned int ohm,
                *i_high = end - 1;
 }
 
-static int get_temp_mC(struct ntc_data *data, unsigned int ohm)
+static int get_temp_mc(struct ntc_data *data, unsigned int ohm)
 {
        int low, high;
        int temp;
@@ -241,10 +343,10 @@ static int get_temp_mC(struct ntc_data *data, unsigned int ohm)
        lookup_comp(data, ohm, &low, &high);
        if (low == high) {
                /* Unable to use linear approximation */
-               temp = data->comp[low].temp_C * 1000;
+               temp = data->comp[low].temp_c * 1000;
        } else {
-               temp = data->comp[low].temp_C * 1000 +
-                       ((data->comp[high].temp_C - data->comp[low].temp_C) *
+               temp = data->comp[low].temp_c * 1000 +
+                       ((data->comp[high].temp_c - data->comp[low].temp_c) *
                         1000 * ((int)ohm - (int)data->comp[low].ohm)) /
                        ((int)data->comp[high].ohm - (int)data->comp[low].ohm);
        }
@@ -253,16 +355,16 @@ static int get_temp_mC(struct ntc_data *data, unsigned int ohm)
 
 static int ntc_thermistor_get_ohm(struct ntc_data *data)
 {
-       int read_uV;
+       int read_uv;
 
        if (data->pdata->read_ohm)
                return data->pdata->read_ohm();
 
-       if (data->pdata->read_uV) {
-               read_uV = data->pdata->read_uV();
-               if (read_uV < 0)
-                       return read_uV;
-               return get_ohm_of_thermistor(data, read_uV);
+       if (data->pdata->read_uv) {
+               read_uv = data->pdata->read_uv(data->pdata);
+               if (read_uv < 0)
+                       return read_uv;
+               return get_ohm_of_thermistor(data, read_uv);
        }
        return -EINVAL;
 }
@@ -291,7 +393,7 @@ static ssize_t ntc_show_temp(struct device *dev,
        if (ohm < 0)
                return ohm;
 
-       return sprintf(buf, "%d\n", get_temp_mC(data, ohm));
+       return sprintf(buf, "%d\n", get_temp_mc(data, ohm));
 }
 
 static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0);
@@ -311,9 +413,18 @@ static const struct attribute_group ntc_attr_group = {
 
 static int ntc_thermistor_probe(struct platform_device *pdev)
 {
+       const struct of_device_id *of_id =
+                       of_match_device(of_match_ptr(ntc_match), &pdev->dev);
+       const struct platform_device_id *pdev_id;
+       struct ntc_thermistor_platform_data *pdata;
        struct ntc_data *data;
-       struct ntc_thermistor_platform_data *pdata = pdev->dev.platform_data;
-       int ret = 0;
+       int ret;
+
+       pdata = ntc_thermistor_parse_dt(pdev);
+       if (IS_ERR(pdata))
+               return PTR_ERR(pdata);
+       else if (pdata == NULL)
+               pdata = pdev->dev.platform_data;
 
        if (!pdata) {
                dev_err(&pdev->dev, "No platform init data supplied.\n");
@@ -321,19 +432,19 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
        }
 
        /* Either one of the two is required. */
-       if (!pdata->read_uV && !pdata->read_ohm) {
+       if (!pdata->read_uv && !pdata->read_ohm) {
                dev_err(&pdev->dev,
-                       "Both read_uV and read_ohm missing. Need either one of the two.\n");
+                       "Both read_uv and read_ohm missing. Need either one of the two.\n");
                return -EINVAL;
        }
 
-       if (pdata->read_uV && pdata->read_ohm) {
+       if (pdata->read_uv && pdata->read_ohm) {
                dev_warn(&pdev->dev,
-                        "Only one of read_uV and read_ohm is needed; ignoring read_uV.\n");
-               pdata->read_uV = NULL;
+                        "Only one of read_uv and read_ohm is needed; ignoring read_uv.\n");
+               pdata->read_uv = NULL;
        }
 
-       if (pdata->read_uV && (pdata->pullup_uV == 0 ||
+       if (pdata->read_uv && (pdata->pullup_uv == 0 ||
                                (pdata->pullup_ohm == 0 && pdata->connect ==
                                 NTC_CONNECTED_GROUND) ||
                                (pdata->pulldown_ohm == 0 && pdata->connect ==
@@ -341,7 +452,7 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
                                (pdata->connect != NTC_CONNECTED_POSITIVE &&
                                 pdata->connect != NTC_CONNECTED_GROUND))) {
                dev_err(&pdev->dev,
-                       "Required data to use read_uV not supplied.\n");
+                       "Required data to use read_uv not supplied.\n");
                return -EINVAL;
        }
 
@@ -349,11 +460,13 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
        if (!data)
                return -ENOMEM;
 
+       pdev_id = of_id ? of_id->data : platform_get_device_id(pdev);
+
        data->dev = &pdev->dev;
        data->pdata = pdata;
-       strlcpy(data->name, pdev->id_entry->name, sizeof(data->name));
+       strlcpy(data->name, pdev_id->name, sizeof(data->name));
 
-       switch (pdev->id_entry->driver_data) {
+       switch (pdev_id->driver_data) {
        case TYPE_NCPXXWB473:
                data->comp = ncpXXwb473;
                data->n_comp = ARRAY_SIZE(ncpXXwb473);
@@ -364,8 +477,7 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
                break;
        default:
                dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n",
-                               pdev->id_entry->driver_data,
-                               pdev->id_entry->name);
+                               pdev_id->driver_data, pdev_id->name);
                return -EINVAL;
        }
 
@@ -384,39 +496,34 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
                goto err_after_sysfs;
        }
 
-       dev_info(&pdev->dev, "Thermistor %s:%d (type: %s/%lu) successfully probed.\n",
-                       pdev->name, pdev->id, pdev->id_entry->name,
-                       pdev->id_entry->driver_data);
+       dev_info(&pdev->dev, "Thermistor type: %s successfully probed.\n",
+                                                               pdev->name);
+
        return 0;
 err_after_sysfs:
        sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
+       ntc_iio_channel_release(pdata);
        return ret;
 }
 
 static int ntc_thermistor_remove(struct platform_device *pdev)
 {
        struct ntc_data *data = platform_get_drvdata(pdev);
+       struct ntc_thermistor_platform_data *pdata = data->pdata;
 
        hwmon_device_unregister(data->hwmon_dev);
        sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
+       ntc_iio_channel_release(pdata);
        platform_set_drvdata(pdev, NULL);
 
        return 0;
 }
 
-static const struct platform_device_id ntc_thermistor_id[] = {
-       { "ncp15wb473", TYPE_NCPXXWB473 },
-       { "ncp18wb473", TYPE_NCPXXWB473 },
-       { "ncp21wb473", TYPE_NCPXXWB473 },
-       { "ncp03wb473", TYPE_NCPXXWB473 },
-       { "ncp15wl333", TYPE_NCPXXWL333 },
-       { },
-};
-
 static struct platform_driver ntc_thermistor_driver = {
        .driver = {
                .name = "ntc-thermistor",
                .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(ntc_match),
        },
        .probe = ntc_thermistor_probe,
        .remove = ntc_thermistor_remove,
index e35856bb79b4d69096a452360e3f7973c7b9c1f3..aa615ba73d4bf70f9f627a29a7279d545dae22bb 100644 (file)
@@ -1190,8 +1190,7 @@ static int __init pc87360_find(int sioaddr, u8 *devid,
                                confreg[3] = superio_inb(sioaddr, 0x25);
 
                                if (confreg[2] & 0x40) {
-                                       pr_info("Using thermistors for "
-                                               "temperature monitoring\n");
+                                       pr_info("Using thermistors for temperature monitoring\n");
                                }
                                if (confreg[3] & 0xE0) {
                                        pr_info("VID inputs routed (mode %u)\n",
@@ -1271,9 +1270,9 @@ static int pc87360_probe(struct platform_device *pdev)
                if (data->address[i]
                 && !devm_request_region(dev, extra_isa[i], PC87360_EXTENT,
                                         pc87360_driver.driver.name)) {
-                       dev_err(dev, "Region 0x%x-0x%x already "
-                               "in use!\n", extra_isa[i],
-                               extra_isa[i]+PC87360_EXTENT-1);
+                       dev_err(dev,
+                               "Region 0x%x-0x%x already in use!\n",
+                               extra_isa[i], extra_isa[i]+PC87360_EXTENT-1);
                        return -EBUSY;
                }
        }
@@ -1435,8 +1434,8 @@ static void pc87360_init_device(struct platform_device *pdev,
        if (init >= 2 && data->innr) {
                reg = pc87360_read_value(data, LD_IN, NO_BANK,
                                         PC87365_REG_IN_CONVRATE);
-               dev_info(&pdev->dev, "VLM conversion set to "
-                        "1s period, 160us delay\n");
+               dev_info(&pdev->dev,
+                        "VLM conversion set to 1s period, 160us delay\n");
                pc87360_write_value(data, LD_IN, NO_BANK,
                                    PC87365_REG_IN_CONVRATE,
                                    (reg & 0xC0) | 0x11);
@@ -1450,8 +1449,8 @@ static void pc87360_init_device(struct platform_device *pdev,
                if (init >= init_in[i]) {
                        /* Forcibly enable voltage channel */
                        if (!(reg & CHAN_ENA)) {
-                               dev_dbg(&pdev->dev, "Forcibly "
-                                       "enabling in%d\n", i);
+                               dev_dbg(&pdev->dev, "Forcibly enabling in%d\n",
+                                       i);
                                pc87360_write_value(data, LD_IN, i,
                                                    PC87365_REG_IN_STATUS,
                                                    (reg & 0x68) | 0x87);
@@ -1575,8 +1574,8 @@ static void pc87360_autodiv(struct device *dev, int nr)
                        data->fan_status[nr] += 0x20;
                        data->fan_min[nr] >>= 1;
                        data->fan[nr] >>= 1;
-                       dev_dbg(dev, "Increasing "
-                               "clock divider to %d for fan %d\n",
+                       dev_dbg(dev,
+                               "Increasing clock divider to %d for fan %d\n",
                                FAN_DIV_FROM_REG(data->fan_status[nr]), nr + 1);
                }
        } else {
@@ -1587,8 +1586,8 @@ static void pc87360_autodiv(struct device *dev, int nr)
                        data->fan_status[nr] -= 0x20;
                        data->fan_min[nr] <<= 1;
                        data->fan[nr] <<= 1;
-                       dev_dbg(dev, "Decreasing "
-                               "clock divider to %d for fan %d\n",
+                       dev_dbg(dev,
+                               "Decreasing clock divider to %d for fan %d\n",
                                FAN_DIV_FROM_REG(data->fan_status[nr]),
                                nr + 1);
                }
index 6086ad039d7d3e4968a26943315ebf6683e10a51..ea606860d2b203b8a9f679c62bc3d2cbfbfedbc0 100644 (file)
@@ -627,8 +627,9 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute
        pc87427_readall_pwm(data, nr);
        mode = data->pwm_enable[nr] & PWM_ENABLE_MODE_MASK;
        if (mode != PWM_MODE_MANUAL && mode != PWM_MODE_OFF) {
-               dev_notice(dev, "Can't set PWM%d duty cycle while not in "
-                          "manual mode\n", nr + 1);
+               dev_notice(dev,
+                          "Can't set PWM%d duty cycle while not in manual mode\n",
+                          nr + 1);
                mutex_unlock(&data->lock);
                return -EPERM;
        }
@@ -1245,16 +1246,16 @@ static int __init pc87427_find(int sioaddr, struct pc87427_sio_data *sio_data)
 
                val = superio_inb(sioaddr, SIOREG_MAP);
                if (val & 0x01) {
-                       pr_warn("Logical device 0x%02x is memory-mapped, "
-                               "can't use\n", logdev[i]);
+                       pr_warn("Logical device 0x%02x is memory-mapped, can't use\n",
+                               logdev[i]);
                        continue;
                }
 
                val = (superio_inb(sioaddr, SIOREG_IOBASE) << 8)
                    | superio_inb(sioaddr, SIOREG_IOBASE + 1);
                if (!val) {
-                       pr_info("I/O base address not set for logical device "
-                               "0x%02x\n", logdev[i]);
+                       pr_info("I/O base address not set for logical device 0x%02x\n",
+                               logdev[i]);
                        continue;
                }
                sio_data->address[i] = val;
index 4f9eb0af5229b0e47dbbc7ad27d51d792de9bbb7..39cc63edfbb02d92dac8d00e2b1dfee876e9dd7a 100644 (file)
@@ -42,17 +42,17 @@ config SENSORS_LM25066
        default n
        help
          If you say yes here you get hardware monitoring support for National
-         Semiconductor LM25066, LM5064, and LM5066.
+         Semiconductor LM25056, LM25066, LM5064, and LM5066.
 
          This driver can also be built as a module. If so, the module will
          be called lm25066.
 
 config SENSORS_LTC2978
-       tristate "Linear Technologies LTC2978 and LTC3880"
+       tristate "Linear Technologies LTC2974, LTC2978, LTC3880, and LTC3883"
        default n
        help
          If you say yes here you get hardware monitoring support for Linear
-         Technology LTC2978 and LTC3880.
+         Technology LTC2974, LTC2978, LTC3880, and LTC3883.
 
          This driver can also be built as a module. If so, the module will
          be called ltc2978.
index c299392716af2c02dd503a797112e03a01f489c7..6a9d6edaacb3947a6080162d2ce9b8ddc0c9d59c 100644 (file)
@@ -1,7 +1,8 @@
 /*
- * Hardware monitoring driver for LM25066 / LM5064 / LM5066
+ * Hardware monitoring driver for LM25056 / LM25066 / LM5064 / LM5066
  *
  * Copyright (c) 2011 Ericsson AB.
+ * Copyright (c) 2013 Guenter Roeck
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -26,7 +27,7 @@
 #include <linux/i2c.h>
 #include "pmbus.h"
 
-enum chips { lm25066, lm5064, lm5066 };
+enum chips { lm25056, lm25066, lm5064, lm5066 };
 
 #define LM25066_READ_VAUX              0xd0
 #define LM25066_MFR_READ_IIN           0xd1
@@ -43,6 +44,138 @@ enum chips { lm25066, lm5064, lm5066 };
 
 #define LM25066_DEV_SETUP_CL           (1 << 4)        /* Current limit */
 
+/* LM25056 only */
+
+#define LM25056_VAUX_OV_WARN_LIMIT     0xe3
+#define LM25056_VAUX_UV_WARN_LIMIT     0xe4
+
+#define LM25056_MFR_STS_VAUX_OV_WARN   (1 << 1)
+#define LM25056_MFR_STS_VAUX_UV_WARN   (1 << 0)
+
+struct __coeff {
+       short m, b, R;
+};
+
+#define PSC_CURRENT_IN_L       (PSC_NUM_CLASSES)
+#define PSC_POWER_L            (PSC_NUM_CLASSES + 1)
+
+static struct __coeff lm25066_coeff[4][PSC_NUM_CLASSES + 2] = {
+       [lm25056] = {
+               [PSC_VOLTAGE_IN] = {
+                       .m = 16296,
+                       .R = -2,
+               },
+               [PSC_CURRENT_IN] = {
+                       .m = 13797,
+                       .R = -2,
+               },
+               [PSC_CURRENT_IN_L] = {
+                       .m = 6726,
+                       .R = -2,
+               },
+               [PSC_POWER] = {
+                       .m = 5501,
+                       .R = -3,
+               },
+               [PSC_POWER_L] = {
+                       .m = 26882,
+                       .R = -4,
+               },
+               [PSC_TEMPERATURE] = {
+                       .m = 1580,
+                       .b = -14500,
+                       .R = -2,
+               },
+       },
+       [lm25066] = {
+               [PSC_VOLTAGE_IN] = {
+                       .m = 22070,
+                       .R = -2,
+               },
+               [PSC_VOLTAGE_OUT] = {
+                       .m = 22070,
+                       .R = -2,
+               },
+               [PSC_CURRENT_IN] = {
+                       .m = 13661,
+                       .R = -2,
+               },
+               [PSC_CURRENT_IN_L] = {
+                       .m = 6852,
+                       .R = -2,
+               },
+               [PSC_POWER] = {
+                       .m = 736,
+                       .R = -2,
+               },
+               [PSC_POWER_L] = {
+                       .m = 369,
+                       .R = -2,
+               },
+               [PSC_TEMPERATURE] = {
+                       .m = 16,
+               },
+       },
+       [lm5064] = {
+               [PSC_VOLTAGE_IN] = {
+                       .m = 4611,
+                       .R = -2,
+               },
+               [PSC_VOLTAGE_OUT] = {
+                       .m = 4621,
+                       .R = -2,
+               },
+               [PSC_CURRENT_IN] = {
+                       .m = 10742,
+                       .R = -2,
+               },
+               [PSC_CURRENT_IN_L] = {
+                       .m = 5456,
+                       .R = -2,
+               },
+               [PSC_POWER] = {
+                       .m = 1204,
+                       .R = -3,
+               },
+               [PSC_POWER_L] = {
+                       .m = 612,
+                       .R = -3,
+               },
+               [PSC_TEMPERATURE] = {
+                       .m = 16,
+               },
+       },
+       [lm5066] = {
+               [PSC_VOLTAGE_IN] = {
+                       .m = 4587,
+                       .R = -2,
+               },
+               [PSC_VOLTAGE_OUT] = {
+                       .m = 4587,
+                       .R = -2,
+               },
+               [PSC_CURRENT_IN] = {
+                       .m = 10753,
+                       .R = -2,
+               },
+               [PSC_CURRENT_IN_L] = {
+                       .m = 5405,
+                       .R = -2,
+               },
+               [PSC_POWER] = {
+                       .m = 1204,
+                       .R = -3,
+               },
+               [PSC_POWER_L] = {
+                       .m = 605,
+                       .R = -3,
+               },
+               [PSC_TEMPERATURE] = {
+                       .m = 16,
+               },
+       },
+};
+
 struct lm25066_data {
        int id;
        struct pmbus_driver_info info;
@@ -56,42 +189,31 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg)
        const struct lm25066_data *data = to_lm25066_data(info);
        int ret;
 
-       if (page > 1)
-               return -ENXIO;
-
-       /* Map READ_VAUX into READ_VOUT register on page 1 */
-       if (page == 1) {
-               switch (reg) {
-               case PMBUS_READ_VOUT:
-                       ret = pmbus_read_word_data(client, 0,
-                                                  LM25066_READ_VAUX);
-                       if (ret < 0)
-                               break;
-                       /* Adjust returned value to match VOUT coefficients */
-                       switch (data->id) {
-                       case lm25066:
-                               /* VOUT: 4.54 mV VAUX: 283.2 uV LSB */
-                               ret = DIV_ROUND_CLOSEST(ret * 2832, 45400);
-                               break;
-                       case lm5064:
-                               /* VOUT: 4.53 mV VAUX: 700 uV LSB */
-                               ret = DIV_ROUND_CLOSEST(ret * 70, 453);
-                               break;
-                       case lm5066:
-                               /* VOUT: 2.18 mV VAUX: 725 uV LSB */
-                               ret = DIV_ROUND_CLOSEST(ret * 725, 2180);
-                               break;
-                       }
+       switch (reg) {
+       case PMBUS_VIRT_READ_VMON:
+               ret = pmbus_read_word_data(client, 0, LM25066_READ_VAUX);
+               if (ret < 0)
+                       break;
+               /* Adjust returned value to match VIN coefficients */
+               switch (data->id) {
+               case lm25056:
+                       /* VIN: 6.14 mV VAUX: 293 uV LSB */
+                       ret = DIV_ROUND_CLOSEST(ret * 293, 6140);
                        break;
-               default:
-                       /* No other valid registers on page 1 */
-                       ret = -ENXIO;
+               case lm25066:
+                       /* VIN: 4.54 mV VAUX: 283.2 uV LSB */
+                       ret = DIV_ROUND_CLOSEST(ret * 2832, 45400);
+                       break;
+               case lm5064:
+                       /* VIN: 4.53 mV VAUX: 700 uV LSB */
+                       ret = DIV_ROUND_CLOSEST(ret * 70, 453);
+                       break;
+               case lm5066:
+                       /* VIN: 2.18 mV VAUX: 725 uV LSB */
+                       ret = DIV_ROUND_CLOSEST(ret * 725, 2180);
                        break;
                }
-               goto done;
-       }
-
-       switch (reg) {
+               break;
        case PMBUS_READ_IIN:
                ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_IIN);
                break;
@@ -128,7 +250,58 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg)
                ret = -ENODATA;
                break;
        }
-done:
+       return ret;
+}
+
+static int lm25056_read_word_data(struct i2c_client *client, int page, int reg)
+{
+       int ret;
+
+       switch (reg) {
+       case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
+               ret = pmbus_read_word_data(client, 0,
+                                          LM25056_VAUX_UV_WARN_LIMIT);
+               if (ret < 0)
+                       break;
+               /* Adjust returned value to match VIN coefficients */
+               ret = DIV_ROUND_CLOSEST(ret * 293, 6140);
+               break;
+       case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
+               ret = pmbus_read_word_data(client, 0,
+                                          LM25056_VAUX_OV_WARN_LIMIT);
+               if (ret < 0)
+                       break;
+               /* Adjust returned value to match VIN coefficients */
+               ret = DIV_ROUND_CLOSEST(ret * 293, 6140);
+               break;
+       default:
+               ret = lm25066_read_word_data(client, page, reg);
+               break;
+       }
+       return ret;
+}
+
+static int lm25056_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+       int ret, s;
+
+       switch (reg) {
+       case PMBUS_VIRT_STATUS_VMON:
+               ret = pmbus_read_byte_data(client, 0,
+                                          PMBUS_STATUS_MFR_SPECIFIC);
+               if (ret < 0)
+                       break;
+               s = 0;
+               if (ret & LM25056_MFR_STS_VAUX_UV_WARN)
+                       s |= PB_VOLTAGE_UV_WARNING;
+               if (ret & LM25056_MFR_STS_VAUX_OV_WARN)
+                       s |= PB_VOLTAGE_OV_WARNING;
+               ret = s;
+               break;
+       default:
+               ret = -ENODATA;
+               break;
+       }
        return ret;
 }
 
@@ -137,19 +310,45 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg,
 {
        int ret;
 
-       if (page > 1)
-               return -ENXIO;
-
        switch (reg) {
+       case PMBUS_VOUT_UV_WARN_LIMIT:
+       case PMBUS_OT_FAULT_LIMIT:
+       case PMBUS_OT_WARN_LIMIT:
+       case PMBUS_VIN_UV_WARN_LIMIT:
+       case PMBUS_VIN_OV_WARN_LIMIT:
+               word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff);
+               ret = pmbus_write_word_data(client, 0, reg, word);
+               pmbus_clear_cache(client);
+               break;
        case PMBUS_IIN_OC_WARN_LIMIT:
+               word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff);
                ret = pmbus_write_word_data(client, 0,
                                            LM25066_MFR_IIN_OC_WARN_LIMIT,
                                            word);
+               pmbus_clear_cache(client);
                break;
        case PMBUS_PIN_OP_WARN_LIMIT:
+               word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff);
                ret = pmbus_write_word_data(client, 0,
                                            LM25066_MFR_PIN_OP_WARN_LIMIT,
                                            word);
+               pmbus_clear_cache(client);
+               break;
+       case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
+               /* Adjust from VIN coefficients (for LM25056) */
+               word = DIV_ROUND_CLOSEST((int)word * 6140, 293);
+               word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff);
+               ret = pmbus_write_word_data(client, 0,
+                                           LM25056_VAUX_UV_WARN_LIMIT, word);
+               pmbus_clear_cache(client);
+               break;
+       case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
+               /* Adjust from VIN coefficients (for LM25056) */
+               word = DIV_ROUND_CLOSEST((int)word * 6140, 293);
+               word = ((s16)word < 0) ? 0 : clamp_val(word, 0, 0x0fff);
+               ret = pmbus_write_word_data(client, 0,
+                                           LM25056_VAUX_OV_WARN_LIMIT, word);
+               pmbus_clear_cache(client);
                break;
        case PMBUS_VIRT_RESET_PIN_HISTORY:
                ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK);
@@ -161,23 +360,13 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg,
        return ret;
 }
 
-static int lm25066_write_byte(struct i2c_client *client, int page, u8 value)
-{
-       if (page > 1)
-               return -ENXIO;
-
-       if (page <= 0)
-               return pmbus_write_byte(client, page, value);
-
-       return 0;
-}
-
 static int lm25066_probe(struct i2c_client *client,
                          const struct i2c_device_id *id)
 {
        int config;
        struct lm25066_data *data;
        struct pmbus_driver_info *info;
+       struct __coeff *coeff;
 
        if (!i2c_check_functionality(client->adapter,
                                     I2C_FUNC_SMBUS_READ_BYTE_DATA))
@@ -195,107 +384,54 @@ static int lm25066_probe(struct i2c_client *client,
        data->id = id->driver_data;
        info = &data->info;
 
-       info->pages = 2;
+       info->pages = 1;
        info->format[PSC_VOLTAGE_IN] = direct;
        info->format[PSC_VOLTAGE_OUT] = direct;
        info->format[PSC_CURRENT_IN] = direct;
        info->format[PSC_TEMPERATURE] = direct;
        info->format[PSC_POWER] = direct;
 
-       info->m[PSC_TEMPERATURE] = 16;
-       info->b[PSC_TEMPERATURE] = 0;
-       info->R[PSC_TEMPERATURE] = 0;
-
-       info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT
-         | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_PIN | PMBUS_HAVE_IIN
-         | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
-       info->func[1] = PMBUS_HAVE_VOUT;
-
-       info->read_word_data = lm25066_read_word_data;
+       info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VMON
+         | PMBUS_HAVE_PIN | PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT
+         | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+
+       if (data->id == lm25056) {
+               info->func[0] |= PMBUS_HAVE_STATUS_VMON;
+               info->read_word_data = lm25056_read_word_data;
+               info->read_byte_data = lm25056_read_byte_data;
+       } else {
+               info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
+               info->read_word_data = lm25066_read_word_data;
+       }
        info->write_word_data = lm25066_write_word_data;
-       info->write_byte = lm25066_write_byte;
-
-       switch (id->driver_data) {
-       case lm25066:
-               info->m[PSC_VOLTAGE_IN] = 22070;
-               info->b[PSC_VOLTAGE_IN] = 0;
-               info->R[PSC_VOLTAGE_IN] = -2;
-               info->m[PSC_VOLTAGE_OUT] = 22070;
-               info->b[PSC_VOLTAGE_OUT] = 0;
-               info->R[PSC_VOLTAGE_OUT] = -2;
-
-               if (config & LM25066_DEV_SETUP_CL) {
-                       info->m[PSC_CURRENT_IN] = 6852;
-                       info->b[PSC_CURRENT_IN] = 0;
-                       info->R[PSC_CURRENT_IN] = -2;
-                       info->m[PSC_POWER] = 369;
-                       info->b[PSC_POWER] = 0;
-                       info->R[PSC_POWER] = -2;
-               } else {
-                       info->m[PSC_CURRENT_IN] = 13661;
-                       info->b[PSC_CURRENT_IN] = 0;
-                       info->R[PSC_CURRENT_IN] = -2;
-                       info->m[PSC_POWER] = 736;
-                       info->b[PSC_POWER] = 0;
-                       info->R[PSC_POWER] = -2;
-               }
-               break;
-       case lm5064:
-               info->m[PSC_VOLTAGE_IN] = 22075;
-               info->b[PSC_VOLTAGE_IN] = 0;
-               info->R[PSC_VOLTAGE_IN] = -2;
-               info->m[PSC_VOLTAGE_OUT] = 22075;
-               info->b[PSC_VOLTAGE_OUT] = 0;
-               info->R[PSC_VOLTAGE_OUT] = -2;
-
-               if (config & LM25066_DEV_SETUP_CL) {
-                       info->m[PSC_CURRENT_IN] = 6713;
-                       info->b[PSC_CURRENT_IN] = 0;
-                       info->R[PSC_CURRENT_IN] = -2;
-                       info->m[PSC_POWER] = 3619;
-                       info->b[PSC_POWER] = 0;
-                       info->R[PSC_POWER] = -3;
-               } else {
-                       info->m[PSC_CURRENT_IN] = 13426;
-                       info->b[PSC_CURRENT_IN] = 0;
-                       info->R[PSC_CURRENT_IN] = -2;
-                       info->m[PSC_POWER] = 7238;
-                       info->b[PSC_POWER] = 0;
-                       info->R[PSC_POWER] = -3;
-               }
-               break;
-       case lm5066:
-               info->m[PSC_VOLTAGE_IN] = 4587;
-               info->b[PSC_VOLTAGE_IN] = 0;
-               info->R[PSC_VOLTAGE_IN] = -2;
-               info->m[PSC_VOLTAGE_OUT] = 4587;
-               info->b[PSC_VOLTAGE_OUT] = 0;
-               info->R[PSC_VOLTAGE_OUT] = -2;
-
-               if (config & LM25066_DEV_SETUP_CL) {
-                       info->m[PSC_CURRENT_IN] = 10753;
-                       info->b[PSC_CURRENT_IN] = 0;
-                       info->R[PSC_CURRENT_IN] = -2;
-                       info->m[PSC_POWER] = 1204;
-                       info->b[PSC_POWER] = 0;
-                       info->R[PSC_POWER] = -3;
-               } else {
-                       info->m[PSC_CURRENT_IN] = 5405;
-                       info->b[PSC_CURRENT_IN] = 0;
-                       info->R[PSC_CURRENT_IN] = -2;
-                       info->m[PSC_POWER] = 605;
-                       info->b[PSC_POWER] = 0;
-                       info->R[PSC_POWER] = -3;
-               }
-               break;
-       default:
-               return -ENODEV;
+
+       coeff = &lm25066_coeff[data->id][0];
+       info->m[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].m;
+       info->b[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].b;
+       info->R[PSC_TEMPERATURE] = coeff[PSC_TEMPERATURE].R;
+       info->m[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].m;
+       info->b[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].b;
+       info->R[PSC_VOLTAGE_IN] = coeff[PSC_VOLTAGE_IN].R;
+       info->m[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].m;
+       info->b[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].b;
+       info->R[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].R;
+       info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].b;
+       info->R[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].R;
+       info->b[PSC_POWER] = coeff[PSC_POWER].b;
+       info->R[PSC_POWER] = coeff[PSC_POWER].R;
+       if (config & LM25066_DEV_SETUP_CL) {
+               info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].m;
+               info->m[PSC_POWER] = coeff[PSC_POWER_L].m;
+       } else {
+               info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].m;
+               info->m[PSC_POWER] = coeff[PSC_POWER].m;
        }
 
        return pmbus_do_probe(client, id, info);
 }
 
 static const struct i2c_device_id lm25066_id[] = {
+       {"lm25056", lm25056},
        {"lm25066", lm25066},
        {"lm5064", lm5064},
        {"lm5066", lm5066},
@@ -317,5 +453,5 @@ static struct i2c_driver lm25066_driver = {
 module_i2c_driver(lm25066_driver);
 
 MODULE_AUTHOR("Guenter Roeck");
-MODULE_DESCRIPTION("PMBus driver for LM25066/LM5064/LM5066");
+MODULE_DESCRIPTION("PMBus driver for LM25056/LM25066/LM5064/LM5066");
 MODULE_LICENSE("GPL");
index 6d6130752f942380b6fe475504c9a2f75b422f44..586a89ef9e0f4f28a6264fd48e00273fad2a1c5a 100644 (file)
@@ -1,7 +1,8 @@
 /*
- * Hardware monitoring driver for LTC2978 and LTC3880
+ * Hardware monitoring driver for LTC2974, LTC2978, LTC3880, and LTC3883
  *
  * Copyright (c) 2011 Ericsson AB.
+ * Copyright (c) 2013 Guenter Roeck
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <linux/i2c.h>
 #include "pmbus.h"
 
-enum chips { ltc2978, ltc3880 };
+enum chips { ltc2974, ltc2978, ltc3880, ltc3883 };
 
-/* LTC2978 and LTC3880 */
+/* Common for all chips */
 #define LTC2978_MFR_VOUT_PEAK          0xdd
 #define LTC2978_MFR_VIN_PEAK           0xde
 #define LTC2978_MFR_TEMPERATURE_PEAK   0xdf
 #define LTC2978_MFR_SPECIAL_ID         0xe7
 
-/* LTC2978 only */
+/* LTC2974 and LTC2978 */
 #define LTC2978_MFR_VOUT_MIN           0xfb
 #define LTC2978_MFR_VIN_MIN            0xfc
 #define LTC2978_MFR_TEMPERATURE_MIN    0xfd
 
-/* LTC3880 only */
+/* LTC2974 only */
+#define LTC2974_MFR_IOUT_PEAK          0xd7
+#define LTC2974_MFR_IOUT_MIN           0xd8
+
+/* LTC3880 and LTC3883 */
 #define LTC3880_MFR_IOUT_PEAK          0xd7
 #define LTC3880_MFR_CLEAR_PEAKS                0xe3
 #define LTC3880_MFR_TEMPERATURE2_PEAK  0xf4
 
+/* LTC3883 only */
+#define LTC3883_MFR_IIN_PEAK           0xe1
+
+#define LTC2974_ID                     0x0212
 #define LTC2978_ID_REV1                        0x0121
 #define LTC2978_ID_REV2                        0x0122
 #define LTC3880_ID                     0x4000
 #define LTC3880_ID_MASK                        0xff00
+#define LTC3883_ID                     0x4300
+#define LTC3883_ID_MASK                        0xff00
+
+#define LTC2974_NUM_PAGES              4
+#define LTC2978_NUM_PAGES              8
+#define LTC3880_NUM_PAGES              2
+#define LTC3883_NUM_PAGES              1
 
 /*
  * LTC2978 clears peak data whenever the CLEAR_FAULTS command is executed, which
@@ -56,13 +72,15 @@ enum chips { ltc2978, ltc3880 };
  * internal cache of measured peak data, which is only cleared if an explicit
  * "clear peak" command is executed for the sensor in question.
  */
+
 struct ltc2978_data {
        enum chips id;
-       int vin_min, vin_max;
-       int temp_min, temp_max[2];
-       int vout_min[8], vout_max[8];
-       int iout_max[2];
-       int temp2_max;
+       u16 vin_min, vin_max;
+       u16 temp_min[LTC2974_NUM_PAGES], temp_max[LTC2974_NUM_PAGES];
+       u16 vout_min[LTC2978_NUM_PAGES], vout_max[LTC2978_NUM_PAGES];
+       u16 iout_min[LTC2974_NUM_PAGES], iout_max[LTC2974_NUM_PAGES];
+       u16 iin_max;
+       u16 temp2_max;
        struct pmbus_driver_info info;
 };
 
@@ -167,9 +185,9 @@ static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg)
                                           LTC2978_MFR_TEMPERATURE_MIN);
                if (ret >= 0) {
                        if (lin11_to_val(ret)
-                           < lin11_to_val(data->temp_min))
-                               data->temp_min = ret;
-                       ret = data->temp_min;
+                           < lin11_to_val(data->temp_min[page]))
+                               data->temp_min[page] = ret;
+                       ret = data->temp_min[page];
                }
                break;
        case PMBUS_VIRT_READ_IOUT_MAX:
@@ -185,6 +203,41 @@ static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg)
        return ret;
 }
 
+static int ltc2974_read_word_data(struct i2c_client *client, int page, int reg)
+{
+       const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+       struct ltc2978_data *data = to_ltc2978_data(info);
+       int ret;
+
+       switch (reg) {
+       case PMBUS_VIRT_READ_IOUT_MAX:
+               ret = pmbus_read_word_data(client, page, LTC2974_MFR_IOUT_PEAK);
+               if (ret >= 0) {
+                       if (lin11_to_val(ret)
+                           > lin11_to_val(data->iout_max[page]))
+                               data->iout_max[page] = ret;
+                       ret = data->iout_max[page];
+               }
+               break;
+       case PMBUS_VIRT_READ_IOUT_MIN:
+               ret = pmbus_read_word_data(client, page, LTC2974_MFR_IOUT_MIN);
+               if (ret >= 0) {
+                       if (lin11_to_val(ret)
+                           < lin11_to_val(data->iout_min[page]))
+                               data->iout_min[page] = ret;
+                       ret = data->iout_min[page];
+               }
+               break;
+       case PMBUS_VIRT_RESET_IOUT_HISTORY:
+               ret = 0;
+               break;
+       default:
+               ret = ltc2978_read_word_data(client, page, reg);
+               break;
+       }
+       return ret;
+}
+
 static int ltc3880_read_word_data(struct i2c_client *client, int page, int reg)
 {
        const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
@@ -226,15 +279,41 @@ static int ltc3880_read_word_data(struct i2c_client *client, int page, int reg)
        return ret;
 }
 
+static int ltc3883_read_word_data(struct i2c_client *client, int page, int reg)
+{
+       const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+       struct ltc2978_data *data = to_ltc2978_data(info);
+       int ret;
+
+       switch (reg) {
+       case PMBUS_VIRT_READ_IIN_MAX:
+               ret = pmbus_read_word_data(client, page, LTC3883_MFR_IIN_PEAK);
+               if (ret >= 0) {
+                       if (lin11_to_val(ret)
+                           > lin11_to_val(data->iin_max))
+                               data->iin_max = ret;
+                       ret = data->iin_max;
+               }
+               break;
+       case PMBUS_VIRT_RESET_IIN_HISTORY:
+               ret = 0;
+               break;
+       default:
+               ret = ltc3880_read_word_data(client, page, reg);
+               break;
+       }
+       return ret;
+}
+
 static int ltc2978_clear_peaks(struct i2c_client *client, int page,
                               enum chips id)
 {
        int ret;
 
-       if (id == ltc2978)
-               ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS);
-       else
+       if (id == ltc3880 || id == ltc3883)
                ret = pmbus_write_byte(client, 0, LTC3880_MFR_CLEAR_PEAKS);
+       else
+               ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS);
 
        return ret;
 }
@@ -247,8 +326,13 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page,
        int ret;
 
        switch (reg) {
+       case PMBUS_VIRT_RESET_IIN_HISTORY:
+               data->iin_max = 0x7c00;
+               ret = ltc2978_clear_peaks(client, page, data->id);
+               break;
        case PMBUS_VIRT_RESET_IOUT_HISTORY:
                data->iout_max[page] = 0x7c00;
+               data->iout_min[page] = 0xfbff;
                ret = ltc2978_clear_peaks(client, page, data->id);
                break;
        case PMBUS_VIRT_RESET_TEMP2_HISTORY:
@@ -266,7 +350,7 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page,
                ret = ltc2978_clear_peaks(client, page, data->id);
                break;
        case PMBUS_VIRT_RESET_TEMP_HISTORY:
-               data->temp_min = 0x7bff;
+               data->temp_min[page] = 0x7bff;
                data->temp_max[page] = 0x7c00;
                ret = ltc2978_clear_peaks(client, page, data->id);
                break;
@@ -278,8 +362,10 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page,
 }
 
 static const struct i2c_device_id ltc2978_id[] = {
+       {"ltc2974", ltc2974},
        {"ltc2978", ltc2978},
        {"ltc3880", ltc3880},
+       {"ltc3883", ltc3883},
        {}
 };
 MODULE_DEVICE_TABLE(i2c, ltc2978_id);
@@ -304,10 +390,14 @@ static int ltc2978_probe(struct i2c_client *client,
        if (chip_id < 0)
                return chip_id;
 
-       if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2) {
+       if (chip_id == LTC2974_ID) {
+               data->id = ltc2974;
+       } else if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2) {
                data->id = ltc2978;
        } else if ((chip_id & LTC3880_ID_MASK) == LTC3880_ID) {
                data->id = ltc3880;
+       } else if ((chip_id & LTC3883_ID_MASK) == LTC3883_ID) {
+               data->id = ltc3883;
        } else {
                dev_err(&client->dev, "Unsupported chip ID 0x%x\n", chip_id);
                return -ENODEV;
@@ -323,26 +413,45 @@ static int ltc2978_probe(struct i2c_client *client,
 
        data->vin_min = 0x7bff;
        data->vin_max = 0x7c00;
-       data->temp_min = 0x7bff;
+       for (i = 0; i < ARRAY_SIZE(data->vout_min); i++)
+               data->vout_min[i] = 0xffff;
+       for (i = 0; i < ARRAY_SIZE(data->iout_min); i++)
+               data->iout_min[i] = 0xfbff;
+       for (i = 0; i < ARRAY_SIZE(data->iout_max); i++)
+               data->iout_max[i] = 0x7c00;
+       for (i = 0; i < ARRAY_SIZE(data->temp_min); i++)
+               data->temp_min[i] = 0x7bff;
        for (i = 0; i < ARRAY_SIZE(data->temp_max); i++)
                data->temp_max[i] = 0x7c00;
        data->temp2_max = 0x7c00;
 
        switch (data->id) {
+       case ltc2974:
+               info->read_word_data = ltc2974_read_word_data;
+               info->pages = LTC2974_NUM_PAGES;
+               info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
+                 | PMBUS_HAVE_TEMP2;
+               for (i = 0; i < info->pages; i++) {
+                       info->func[i] |= PMBUS_HAVE_VOUT
+                         | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_POUT
+                         | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP
+                         | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
+               }
+               break;
        case ltc2978:
                info->read_word_data = ltc2978_read_word_data;
-               info->pages = 8;
+               info->pages = LTC2978_NUM_PAGES;
                info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
                  | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
                  | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
-               for (i = 1; i < 8; i++) {
+               for (i = 1; i < LTC2978_NUM_PAGES; i++) {
                        info->func[i] = PMBUS_HAVE_VOUT
                          | PMBUS_HAVE_STATUS_VOUT;
                }
                break;
        case ltc3880:
                info->read_word_data = ltc3880_read_word_data;
-               info->pages = 2;
+               info->pages = LTC3880_NUM_PAGES;
                info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN
                  | PMBUS_HAVE_STATUS_INPUT
                  | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
@@ -353,15 +462,20 @@ static int ltc2978_probe(struct i2c_client *client,
                  | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
                  | PMBUS_HAVE_POUT
                  | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
-               data->iout_max[0] = 0x7c00;
-               data->iout_max[1] = 0x7c00;
+               break;
+       case ltc3883:
+               info->read_word_data = ltc3883_read_word_data;
+               info->pages = LTC3883_NUM_PAGES;
+               info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN
+                 | PMBUS_HAVE_STATUS_INPUT
+                 | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+                 | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
+                 | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
+                 | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
                break;
        default:
                return -ENODEV;
        }
-       for (i = 0; i < info->pages; i++)
-               data->vout_min[i] = 0xffff;
-
        return pmbus_do_probe(client, id, info);
 }
 
@@ -378,5 +492,5 @@ static struct i2c_driver ltc2978_driver = {
 module_i2c_driver(ltc2978_driver);
 
 MODULE_AUTHOR("Guenter Roeck");
-MODULE_DESCRIPTION("PMBus driver for LTC2978 and LTC3880");
+MODULE_DESCRIPTION("PMBus driver for LTC2974, LTC2978, LTC3880, and LTC3883");
 MODULE_LICENSE("GPL");
index ff2ae0252a48b048d9e4844efa5182c53b1e6188..a9f7e804f1e4f6c3d580ff6d444b3ff7506d589a 100644 (file)
@@ -107,17 +107,14 @@ static ssize_t s3c_hwmon_show_raw(struct device *dev,
        return  (ret < 0) ? ret : snprintf(buf, PAGE_SIZE, "%d\n", ret);
 }
 
-#define DEF_ADC_ATTR(x)        \
-       static SENSOR_DEVICE_ATTR(adc##x##_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, x)
-
-DEF_ADC_ATTR(0);
-DEF_ADC_ATTR(1);
-DEF_ADC_ATTR(2);
-DEF_ADC_ATTR(3);
-DEF_ADC_ATTR(4);
-DEF_ADC_ATTR(5);
-DEF_ADC_ATTR(6);
-DEF_ADC_ATTR(7);
+static SENSOR_DEVICE_ATTR(adc0_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 0);
+static SENSOR_DEVICE_ATTR(adc1_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 1);
+static SENSOR_DEVICE_ATTR(adc2_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 2);
+static SENSOR_DEVICE_ATTR(adc3_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 3);
+static SENSOR_DEVICE_ATTR(adc4_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 4);
+static SENSOR_DEVICE_ATTR(adc5_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 5);
+static SENSOR_DEVICE_ATTR(adc6_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 6);
+static SENSOR_DEVICE_ATTR(adc7_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, 7);
 
 static struct attribute *s3c_hwmon_attrs[9] = {
        &sensor_dev_attr_adc0_raw.dev_attr.attr,
index d00b30adc34b1c50cb617ec4d6088c7093fc40a1..7386819832842f867ead2c0a4e13fc4487038d7e 100644 (file)
@@ -161,8 +161,8 @@ static int sch56xx_send_cmd(u16 addr, u8 cmd, u16 reg, u8 v)
                        break;
        }
        if (i == max_busy_polls + max_lazy_polls) {
-               pr_err("Max retries exceeded reading virtual "
-                      "register 0x%04hx (%d)\n", reg, 1);
+               pr_err("Max retries exceeded reading virtual register 0x%04hx (%d)\n",
+                      reg, 1);
                return -EIO;
        }
 
@@ -178,12 +178,12 @@ static int sch56xx_send_cmd(u16 addr, u8 cmd, u16 reg, u8 v)
                        break;
 
                if (i == 0)
-                       pr_warn("EC reports: 0x%02x reading virtual register "
-                               "0x%04hx\n", (unsigned int)val, reg);
+                       pr_warn("EC reports: 0x%02x reading virtual register 0x%04hx\n",
+                               (unsigned int)val, reg);
        }
        if (i == max_busy_polls) {
-               pr_err("Max retries exceeded reading virtual "
-                      "register 0x%04hx (%d)\n", reg, 2);
+               pr_err("Max retries exceeded reading virtual register 0x%04hx (%d)\n",
+                      reg, 2);
                return -EIO;
        }
 
index c35847a1a0a32c27c8d2aa1d87c6c179ed13f91e..1404e6319deb3cf918c1874f1be8b09b85902d65 100644 (file)
@@ -456,8 +456,9 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *da,
                data->fan_div[nr] = 3;
                break;
        default:
-               dev_err(dev, "fan_div value %ld not "
-                       "supported. Choose one of 1, 2, 4 or 8!\n", val);
+               dev_err(dev,
+                       "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n",
+                       val);
                mutex_unlock(&data->update_lock);
                return -EINVAL;
        }
index 4b59eb53b18ac38ccaeb949f9feab2ba3fdc097e..db288db7d3e9995840ae6d995d0995fed9936bf4 100644 (file)
@@ -41,8 +41,8 @@ enum chips { thmc50, adm1022 };
 static unsigned short adm1022_temp3[16];
 static unsigned int adm1022_temp3_num;
 module_param_array(adm1022_temp3, ushort, &adm1022_temp3_num, 0);
-MODULE_PARM_DESC(adm1022_temp3, "List of adapter,address pairs "
-                       "to enable 3rd temperature (ADM1022 only)");
+MODULE_PARM_DESC(adm1022_temp3,
+                "List of adapter,address pairs to enable 3rd temperature (ADM1022 only)");
 
 /* Many THMC50 constants specified below */
 
@@ -312,8 +312,7 @@ static int thmc50_detect(struct i2c_client *client,
        const char *type_name;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
-               pr_debug("thmc50: detect failed, "
-                        "smbus byte data not supported!\n");
+               pr_debug("thmc50: detect failed, smbus byte data not supported!\n");
                return -ENODEV;
        }
 
index 523dd89ba498c3dff27cbe87677fb8d2095c39c0..d7b47abf37fe88c259da4f3d7dd73a766af7b9f7 100644 (file)
@@ -155,8 +155,8 @@ static int tmp102_probe(struct i2c_client *client,
 
        if (!i2c_check_functionality(client->adapter,
                                     I2C_FUNC_SMBUS_WORD_DATA)) {
-               dev_err(&client->dev, "adapter doesn't support SMBus word "
-                       "transactions\n");
+               dev_err(&client->dev,
+                       "adapter doesn't support SMBus word transactions\n");
                return -ENODEV;
        }
 
index c85f6967ccc34534da241368af7a440d267bf6bf..a478454f690fbc318aefa16b8952040944692149 100644 (file)
@@ -5,6 +5,9 @@
  * Gabriel Konat, Sander Leget, Wouter Willems
  * Copyright (C) 2009 Andre Prendel <andre.prendel@gmx.de>
  *
+ * Cleanup and support for TMP431 and TMP432 by Guenter Roeck
+ * Copyright (c) 2013 Guenter Roeck <linux@roeck-us.net>
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -30,6 +33,7 @@
 
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/bitops.h>
 #include <linux/slab.h>
 #include <linux/jiffies.h>
 #include <linux/i2c.h>
@@ -40,9 +44,9 @@
 #include <linux/sysfs.h>
 
 /* Addresses to scan */
-static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END };
+static const unsigned short normal_i2c[] = { 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
 
-enum chips { tmp401, tmp411 };
+enum chips { tmp401, tmp411, tmp431, tmp432 };
 
 /*
  * The TMP401 registers, note some registers have different addresses for
@@ -54,42 +58,84 @@ enum chips { tmp401, tmp411 };
 #define TMP401_CONVERSION_RATE_READ            0x04
 #define TMP401_CONVERSION_RATE_WRITE           0x0A
 #define TMP401_TEMP_CRIT_HYST                  0x21
-#define TMP401_CONSECUTIVE_ALERT               0x22
 #define TMP401_MANUFACTURER_ID_REG             0xFE
 #define TMP401_DEVICE_ID_REG                   0xFF
-#define TMP411_N_FACTOR_REG                    0x18
-
-static const u8 TMP401_TEMP_MSB[2]                     = { 0x00, 0x01 };
-static const u8 TMP401_TEMP_LSB[2]                     = { 0x15, 0x10 };
-static const u8 TMP401_TEMP_LOW_LIMIT_MSB_READ[2]      = { 0x06, 0x08 };
-static const u8 TMP401_TEMP_LOW_LIMIT_MSB_WRITE[2]     = { 0x0C, 0x0E };
-static const u8 TMP401_TEMP_LOW_LIMIT_LSB[2]           = { 0x17, 0x14 };
-static const u8 TMP401_TEMP_HIGH_LIMIT_MSB_READ[2]     = { 0x05, 0x07 };
-static const u8 TMP401_TEMP_HIGH_LIMIT_MSB_WRITE[2]    = { 0x0B, 0x0D };
-static const u8 TMP401_TEMP_HIGH_LIMIT_LSB[2]          = { 0x16, 0x13 };
-/* These are called the THERM limit / hysteresis / mask in the datasheet */
-static const u8 TMP401_TEMP_CRIT_LIMIT[2]              = { 0x20, 0x19 };
-
-static const u8 TMP411_TEMP_LOWEST_MSB[2]              = { 0x30, 0x34 };
-static const u8 TMP411_TEMP_LOWEST_LSB[2]              = { 0x31, 0x35 };
-static const u8 TMP411_TEMP_HIGHEST_MSB[2]             = { 0x32, 0x36 };
-static const u8 TMP411_TEMP_HIGHEST_LSB[2]             = { 0x33, 0x37 };
+
+static const u8 TMP401_TEMP_MSB_READ[6][2] = {
+       { 0x00, 0x01 }, /* temp */
+       { 0x06, 0x08 }, /* low limit */
+       { 0x05, 0x07 }, /* high limit */
+       { 0x20, 0x19 }, /* therm (crit) limit */
+       { 0x30, 0x34 }, /* lowest */
+       { 0x32, 0x36 }, /* highest */
+};
+
+static const u8 TMP401_TEMP_MSB_WRITE[6][2] = {
+       { 0, 0 },       /* temp (unused) */
+       { 0x0C, 0x0E }, /* low limit */
+       { 0x0B, 0x0D }, /* high limit */
+       { 0x20, 0x19 }, /* therm (crit) limit */
+       { 0x30, 0x34 }, /* lowest */
+       { 0x32, 0x36 }, /* highest */
+};
+
+static const u8 TMP401_TEMP_LSB[6][2] = {
+       { 0x15, 0x10 }, /* temp */
+       { 0x17, 0x14 }, /* low limit */
+       { 0x16, 0x13 }, /* high limit */
+       { 0, 0 },       /* therm (crit) limit (unused) */
+       { 0x31, 0x35 }, /* lowest */
+       { 0x33, 0x37 }, /* highest */
+};
+
+static const u8 TMP432_TEMP_MSB_READ[4][3] = {
+       { 0x00, 0x01, 0x23 },   /* temp */
+       { 0x06, 0x08, 0x16 },   /* low limit */
+       { 0x05, 0x07, 0x15 },   /* high limit */
+       { 0x20, 0x19, 0x1A },   /* therm (crit) limit */
+};
+
+static const u8 TMP432_TEMP_MSB_WRITE[4][3] = {
+       { 0, 0, 0 },            /* temp  - unused */
+       { 0x0C, 0x0E, 0x16 },   /* low limit */
+       { 0x0B, 0x0D, 0x15 },   /* high limit */
+       { 0x20, 0x19, 0x1A },   /* therm (crit) limit */
+};
+
+static const u8 TMP432_TEMP_LSB[3][3] = {
+       { 0x29, 0x10, 0x24 },   /* temp */
+       { 0x3E, 0x14, 0x18 },   /* low limit */
+       { 0x3D, 0x13, 0x17 },   /* high limit */
+};
+
+/* [0] = fault, [1] = low, [2] = high, [3] = therm/crit */
+static const u8 TMP432_STATUS_REG[] = {
+       0x1b, 0x36, 0x35, 0x37 };
 
 /* Flags */
-#define TMP401_CONFIG_RANGE            0x04
-#define TMP401_CONFIG_SHUTDOWN         0x40
-#define TMP401_STATUS_LOCAL_CRIT               0x01
-#define TMP401_STATUS_REMOTE_CRIT              0x02
-#define TMP401_STATUS_REMOTE_OPEN              0x04
-#define TMP401_STATUS_REMOTE_LOW               0x08
-#define TMP401_STATUS_REMOTE_HIGH              0x10
-#define TMP401_STATUS_LOCAL_LOW                0x20
-#define TMP401_STATUS_LOCAL_HIGH               0x40
+#define TMP401_CONFIG_RANGE                    BIT(2)
+#define TMP401_CONFIG_SHUTDOWN                 BIT(6)
+#define TMP401_STATUS_LOCAL_CRIT               BIT(0)
+#define TMP401_STATUS_REMOTE_CRIT              BIT(1)
+#define TMP401_STATUS_REMOTE_OPEN              BIT(2)
+#define TMP401_STATUS_REMOTE_LOW               BIT(3)
+#define TMP401_STATUS_REMOTE_HIGH              BIT(4)
+#define TMP401_STATUS_LOCAL_LOW                        BIT(5)
+#define TMP401_STATUS_LOCAL_HIGH               BIT(6)
+
+/* On TMP432, each status has its own register */
+#define TMP432_STATUS_LOCAL                    BIT(0)
+#define TMP432_STATUS_REMOTE1                  BIT(1)
+#define TMP432_STATUS_REMOTE2                  BIT(2)
 
 /* Manufacturer / Device ID's */
 #define TMP401_MANUFACTURER_ID                 0x55
 #define TMP401_DEVICE_ID                       0x11
-#define TMP411_DEVICE_ID                       0x12
+#define TMP411A_DEVICE_ID                      0x12
+#define TMP411B_DEVICE_ID                      0x13
+#define TMP411C_DEVICE_ID                      0x10
+#define TMP431_DEVICE_ID                       0x31
+#define TMP432_DEVICE_ID                       0x32
 
 /*
  * Driver data (common to all clients)
@@ -98,6 +144,8 @@ static const u8 TMP411_TEMP_HIGHEST_LSB[2]           = { 0x33, 0x37 };
 static const struct i2c_device_id tmp401_id[] = {
        { "tmp401", tmp401 },
        { "tmp411", tmp411 },
+       { "tmp431", tmp431 },
+       { "tmp432", tmp432 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, tmp401_id);
@@ -113,16 +161,13 @@ struct tmp401_data {
        unsigned long last_updated; /* in jiffies */
        enum chips kind;
 
+       unsigned int update_interval;   /* in milliseconds */
+
        /* register values */
-       u8 status;
+       u8 status[4];
        u8 config;
-       u16 temp[2];
-       u16 temp_low[2];
-       u16 temp_high[2];
-       u8 temp_crit[2];
+       u16 temp[6][3];
        u8 temp_crit_hyst;
-       u16 temp_lowest[2];
-       u16 temp_highest[2];
 };
 
 /*
@@ -136,31 +181,10 @@ static int tmp401_register_to_temp(u16 reg, u8 config)
        if (config & TMP401_CONFIG_RANGE)
                temp -= 64 * 256;
 
-       return (temp * 625 + 80) / 160;
-}
-
-static u16 tmp401_temp_to_register(long temp, u8 config)
-{
-       if (config & TMP401_CONFIG_RANGE) {
-               temp = clamp_val(temp, -64000, 191000);
-               temp += 64000;
-       } else
-               temp = clamp_val(temp, 0, 127000);
-
-       return (temp * 160 + 312) / 625;
-}
-
-static int tmp401_crit_register_to_temp(u8 reg, u8 config)
-{
-       int temp = reg;
-
-       if (config & TMP401_CONFIG_RANGE)
-               temp -= 64;
-
-       return temp * 1000;
+       return DIV_ROUND_CLOSEST(temp * 125, 32);
 }
 
-static u8 tmp401_crit_temp_to_register(long temp, u8 config)
+static u16 tmp401_temp_to_register(long temp, u8 config, int zbits)
 {
        if (config & TMP401_CONFIG_RANGE) {
                temp = clamp_val(temp, -64000, 191000);
@@ -168,113 +192,127 @@ static u8 tmp401_crit_temp_to_register(long temp, u8 config)
        } else
                temp = clamp_val(temp, 0, 127000);
 
-       return (temp + 500) / 1000;
+       return DIV_ROUND_CLOSEST(temp * (1 << (8 - zbits)), 1000) << zbits;
 }
 
-static struct tmp401_data *tmp401_update_device_reg16(
-       struct i2c_client *client, struct tmp401_data *data)
+static int tmp401_update_device_reg16(struct i2c_client *client,
+                                     struct tmp401_data *data)
 {
-       int i;
-
-       for (i = 0; i < 2; i++) {
-               /*
-                * High byte must be read first immediately followed
-                * by the low byte
-                */
-               data->temp[i] = i2c_smbus_read_byte_data(client,
-                       TMP401_TEMP_MSB[i]) << 8;
-               data->temp[i] |= i2c_smbus_read_byte_data(client,
-                       TMP401_TEMP_LSB[i]);
-               data->temp_low[i] = i2c_smbus_read_byte_data(client,
-                       TMP401_TEMP_LOW_LIMIT_MSB_READ[i]) << 8;
-               data->temp_low[i] |= i2c_smbus_read_byte_data(client,
-                       TMP401_TEMP_LOW_LIMIT_LSB[i]);
-               data->temp_high[i] = i2c_smbus_read_byte_data(client,
-                       TMP401_TEMP_HIGH_LIMIT_MSB_READ[i]) << 8;
-               data->temp_high[i] |= i2c_smbus_read_byte_data(client,
-                       TMP401_TEMP_HIGH_LIMIT_LSB[i]);
-               data->temp_crit[i] = i2c_smbus_read_byte_data(client,
-                       TMP401_TEMP_CRIT_LIMIT[i]);
-
-               if (data->kind == tmp411) {
-                       data->temp_lowest[i] = i2c_smbus_read_byte_data(client,
-                               TMP411_TEMP_LOWEST_MSB[i]) << 8;
-                       data->temp_lowest[i] |= i2c_smbus_read_byte_data(
-                               client, TMP411_TEMP_LOWEST_LSB[i]);
-
-                       data->temp_highest[i] = i2c_smbus_read_byte_data(
-                               client, TMP411_TEMP_HIGHEST_MSB[i]) << 8;
-                       data->temp_highest[i] |= i2c_smbus_read_byte_data(
-                               client, TMP411_TEMP_HIGHEST_LSB[i]);
+       int i, j, val;
+       int num_regs = data->kind == tmp411 ? 6 : 4;
+       int num_sensors = data->kind == tmp432 ? 3 : 2;
+
+       for (i = 0; i < num_sensors; i++) {             /* local / r1 / r2 */
+               for (j = 0; j < num_regs; j++) {        /* temp / low / ... */
+                       u8 regaddr;
+                       /*
+                        * High byte must be read first immediately followed
+                        * by the low byte
+                        */
+                       regaddr = data->kind == tmp432 ?
+                                               TMP432_TEMP_MSB_READ[j][i] :
+                                               TMP401_TEMP_MSB_READ[j][i];
+                       val = i2c_smbus_read_byte_data(client, regaddr);
+                       if (val < 0)
+                               return val;
+                       data->temp[j][i] = val << 8;
+                       if (j == 3)             /* crit is msb only */
+                               continue;
+                       regaddr = data->kind == tmp432 ? TMP432_TEMP_LSB[j][i]
+                                                      : TMP401_TEMP_LSB[j][i];
+                       val = i2c_smbus_read_byte_data(client, regaddr);
+                       if (val < 0)
+                               return val;
+                       data->temp[j][i] |= val;
                }
        }
-       return data;
+       return 0;
 }
 
 static struct tmp401_data *tmp401_update_device(struct device *dev)
 {
        struct i2c_client *client = to_i2c_client(dev);
        struct tmp401_data *data = i2c_get_clientdata(client);
+       struct tmp401_data *ret = data;
+       int i, val;
+       unsigned long next_update;
 
        mutex_lock(&data->update_lock);
 
-       if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
-               data->status = i2c_smbus_read_byte_data(client, TMP401_STATUS);
-               data->config = i2c_smbus_read_byte_data(client,
-                                               TMP401_CONFIG_READ);
-               tmp401_update_device_reg16(client, data);
+       next_update = data->last_updated +
+                     msecs_to_jiffies(data->update_interval) + 1;
+       if (time_after(jiffies, next_update) || !data->valid) {
+               if (data->kind != tmp432) {
+                       /*
+                        * The driver uses the TMP432 status format internally.
+                        * Convert status to TMP432 format for other chips.
+                        */
+                       val = i2c_smbus_read_byte_data(client, TMP401_STATUS);
+                       if (val < 0) {
+                               ret = ERR_PTR(val);
+                               goto abort;
+                       }
+                       data->status[0] =
+                         (val & TMP401_STATUS_REMOTE_OPEN) >> 1;
+                       data->status[1] =
+                         ((val & TMP401_STATUS_REMOTE_LOW) >> 2) |
+                         ((val & TMP401_STATUS_LOCAL_LOW) >> 5);
+                       data->status[2] =
+                         ((val & TMP401_STATUS_REMOTE_HIGH) >> 3) |
+                         ((val & TMP401_STATUS_LOCAL_HIGH) >> 6);
+                       data->status[3] = val & (TMP401_STATUS_LOCAL_CRIT
+                                               | TMP401_STATUS_REMOTE_CRIT);
+               } else {
+                       for (i = 0; i < ARRAY_SIZE(data->status); i++) {
+                               val = i2c_smbus_read_byte_data(client,
+                                                       TMP432_STATUS_REG[i]);
+                               if (val < 0) {
+                                       ret = ERR_PTR(val);
+                                       goto abort;
+                               }
+                               data->status[i] = val;
+                       }
+               }
 
-               data->temp_crit_hyst = i2c_smbus_read_byte_data(client,
-                                               TMP401_TEMP_CRIT_HYST);
+               val = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
+               if (val < 0) {
+                       ret = ERR_PTR(val);
+                       goto abort;
+               }
+               data->config = val;
+               val = tmp401_update_device_reg16(client, data);
+               if (val < 0) {
+                       ret = ERR_PTR(val);
+                       goto abort;
+               }
+               val = i2c_smbus_read_byte_data(client, TMP401_TEMP_CRIT_HYST);
+               if (val < 0) {
+                       ret = ERR_PTR(val);
+                       goto abort;
+               }
+               data->temp_crit_hyst = val;
 
                data->last_updated = jiffies;
                data->valid = 1;
        }
 
+abort:
        mutex_unlock(&data->update_lock);
-
-       return data;
-}
-
-static ssize_t show_temp_value(struct device *dev,
-       struct device_attribute *devattr, char *buf)
-{
-       int index = to_sensor_dev_attr(devattr)->index;
-       struct tmp401_data *data = tmp401_update_device(dev);
-
-       return sprintf(buf, "%d\n",
-               tmp401_register_to_temp(data->temp[index], data->config));
-}
-
-static ssize_t show_temp_min(struct device *dev,
-       struct device_attribute *devattr, char *buf)
-{
-       int index = to_sensor_dev_attr(devattr)->index;
-       struct tmp401_data *data = tmp401_update_device(dev);
-
-       return sprintf(buf, "%d\n",
-               tmp401_register_to_temp(data->temp_low[index], data->config));
+       return ret;
 }
 
-static ssize_t show_temp_max(struct device *dev,
-       struct device_attribute *devattr, char *buf)
+static ssize_t show_temp(struct device *dev,
+                        struct device_attribute *devattr, char *buf)
 {
-       int index = to_sensor_dev_attr(devattr)->index;
+       int nr = to_sensor_dev_attr_2(devattr)->nr;
+       int index = to_sensor_dev_attr_2(devattr)->index;
        struct tmp401_data *data = tmp401_update_device(dev);
 
-       return sprintf(buf, "%d\n",
-               tmp401_register_to_temp(data->temp_high[index], data->config));
-}
-
-static ssize_t show_temp_crit(struct device *dev,
-       struct device_attribute *devattr, char *buf)
-{
-       int index = to_sensor_dev_attr(devattr)->index;
-       struct tmp401_data *data = tmp401_update_device(dev);
+       if (IS_ERR(data))
+               return PTR_ERR(data);
 
        return sprintf(buf, "%d\n",
-                       tmp401_crit_register_to_temp(data->temp_crit[index],
-                                                       data->config));
+               tmp401_register_to_temp(data->temp[nr][index], data->config));
 }
 
 static ssize_t show_temp_crit_hyst(struct device *dev,
@@ -283,122 +321,60 @@ static ssize_t show_temp_crit_hyst(struct device *dev,
        int temp, index = to_sensor_dev_attr(devattr)->index;
        struct tmp401_data *data = tmp401_update_device(dev);
 
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
        mutex_lock(&data->update_lock);
-       temp = tmp401_crit_register_to_temp(data->temp_crit[index],
-                                               data->config);
+       temp = tmp401_register_to_temp(data->temp[3][index], data->config);
        temp -= data->temp_crit_hyst * 1000;
        mutex_unlock(&data->update_lock);
 
        return sprintf(buf, "%d\n", temp);
 }
 
-static ssize_t show_temp_lowest(struct device *dev,
-       struct device_attribute *devattr, char *buf)
-{
-       int index = to_sensor_dev_attr(devattr)->index;
-       struct tmp401_data *data = tmp401_update_device(dev);
-
-       return sprintf(buf, "%d\n",
-               tmp401_register_to_temp(data->temp_lowest[index],
-                                       data->config));
-}
-
-static ssize_t show_temp_highest(struct device *dev,
-       struct device_attribute *devattr, char *buf)
-{
-       int index = to_sensor_dev_attr(devattr)->index;
-       struct tmp401_data *data = tmp401_update_device(dev);
-
-       return sprintf(buf, "%d\n",
-               tmp401_register_to_temp(data->temp_highest[index],
-                                       data->config));
-}
-
 static ssize_t show_status(struct device *dev,
        struct device_attribute *devattr, char *buf)
 {
-       int mask = to_sensor_dev_attr(devattr)->index;
-       struct tmp401_data *data = tmp401_update_device(dev);
-
-       if (data->status & mask)
-               return sprintf(buf, "1\n");
-       else
-               return sprintf(buf, "0\n");
-}
-
-static ssize_t store_temp_min(struct device *dev, struct device_attribute
-       *devattr, const char *buf, size_t count)
-{
-       int index = to_sensor_dev_attr(devattr)->index;
+       int nr = to_sensor_dev_attr_2(devattr)->nr;
+       int mask = to_sensor_dev_attr_2(devattr)->index;
        struct tmp401_data *data = tmp401_update_device(dev);
-       long val;
-       u16 reg;
-
-       if (kstrtol(buf, 10, &val))
-               return -EINVAL;
-
-       reg = tmp401_temp_to_register(val, data->config);
-
-       mutex_lock(&data->update_lock);
-
-       i2c_smbus_write_byte_data(to_i2c_client(dev),
-               TMP401_TEMP_LOW_LIMIT_MSB_WRITE[index], reg >> 8);
-       i2c_smbus_write_byte_data(to_i2c_client(dev),
-               TMP401_TEMP_LOW_LIMIT_LSB[index], reg & 0xFF);
-
-       data->temp_low[index] = reg;
 
-       mutex_unlock(&data->update_lock);
+       if (IS_ERR(data))
+               return PTR_ERR(data);
 
-       return count;
+       return sprintf(buf, "%d\n", !!(data->status[nr] & mask));
 }
 
-static ssize_t store_temp_max(struct device *dev, struct device_attribute
-       *devattr, const char *buf, size_t count)
+static ssize_t store_temp(struct device *dev, struct device_attribute *devattr,
+                         const char *buf, size_t count)
 {
-       int index = to_sensor_dev_attr(devattr)->index;
+       int nr = to_sensor_dev_attr_2(devattr)->nr;
+       int index = to_sensor_dev_attr_2(devattr)->index;
+       struct i2c_client *client = to_i2c_client(dev);
        struct tmp401_data *data = tmp401_update_device(dev);
        long val;
        u16 reg;
+       u8 regaddr;
 
-       if (kstrtol(buf, 10, &val))
-               return -EINVAL;
-
-       reg = tmp401_temp_to_register(val, data->config);
-
-       mutex_lock(&data->update_lock);
-
-       i2c_smbus_write_byte_data(to_i2c_client(dev),
-               TMP401_TEMP_HIGH_LIMIT_MSB_WRITE[index], reg >> 8);
-       i2c_smbus_write_byte_data(to_i2c_client(dev),
-               TMP401_TEMP_HIGH_LIMIT_LSB[index], reg & 0xFF);
-
-       data->temp_high[index] = reg;
-
-       mutex_unlock(&data->update_lock);
-
-       return count;
-}
-
-static ssize_t store_temp_crit(struct device *dev, struct device_attribute
-       *devattr, const char *buf, size_t count)
-{
-       int index = to_sensor_dev_attr(devattr)->index;
-       struct tmp401_data *data = tmp401_update_device(dev);
-       long val;
-       u8 reg;
+       if (IS_ERR(data))
+               return PTR_ERR(data);
 
        if (kstrtol(buf, 10, &val))
                return -EINVAL;
 
-       reg = tmp401_crit_temp_to_register(val, data->config);
+       reg = tmp401_temp_to_register(val, data->config, nr == 3 ? 8 : 4);
 
        mutex_lock(&data->update_lock);
 
-       i2c_smbus_write_byte_data(to_i2c_client(dev),
-               TMP401_TEMP_CRIT_LIMIT[index], reg);
-
-       data->temp_crit[index] = reg;
+       regaddr = data->kind == tmp432 ? TMP432_TEMP_MSB_WRITE[nr][index]
+                                      : TMP401_TEMP_MSB_WRITE[nr][index];
+       i2c_smbus_write_byte_data(client, regaddr, reg >> 8);
+       if (nr != 3) {
+               regaddr = data->kind == tmp432 ? TMP432_TEMP_LSB[nr][index]
+                                              : TMP401_TEMP_LSB[nr][index];
+               i2c_smbus_write_byte_data(client, regaddr, reg & 0xFF);
+       }
+       data->temp[nr][index] = reg;
 
        mutex_unlock(&data->update_lock);
 
@@ -413,6 +389,9 @@ static ssize_t store_temp_crit_hyst(struct device *dev, struct device_attribute
        long val;
        u8 reg;
 
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
        if (kstrtol(buf, 10, &val))
                return -EINVAL;
 
@@ -422,13 +401,12 @@ static ssize_t store_temp_crit_hyst(struct device *dev, struct device_attribute
                val = clamp_val(val, 0, 127000);
 
        mutex_lock(&data->update_lock);
-       temp = tmp401_crit_register_to_temp(data->temp_crit[index],
-                                               data->config);
+       temp = tmp401_register_to_temp(data->temp[3][index], data->config);
        val = clamp_val(val, temp - 255000, temp);
        reg = ((temp - val) + 500) / 1000;
 
-       i2c_smbus_write_byte_data(to_i2c_client(dev),
-               TMP401_TEMP_CRIT_HYST, reg);
+       i2c_smbus_write_byte_data(to_i2c_client(dev), TMP401_TEMP_CRIT_HYST,
+                                 reg);
 
        data->temp_crit_hyst = reg;
 
@@ -445,54 +423,130 @@ static ssize_t store_temp_crit_hyst(struct device *dev, struct device_attribute
 static ssize_t reset_temp_history(struct device *dev,
        struct device_attribute *devattr, const char *buf, size_t count)
 {
+       struct i2c_client *client = to_i2c_client(dev);
+       struct tmp401_data *data = i2c_get_clientdata(client);
        long val;
 
        if (kstrtol(buf, 10, &val))
                return -EINVAL;
 
        if (val != 1) {
-               dev_err(dev, "temp_reset_history value %ld not"
-                       " supported. Use 1 to reset the history!\n", val);
+               dev_err(dev,
+                       "temp_reset_history value %ld not supported. Use 1 to reset the history!\n",
+                       val);
                return -EINVAL;
        }
-       i2c_smbus_write_byte_data(to_i2c_client(dev),
-               TMP411_TEMP_LOWEST_MSB[0], val);
+       mutex_lock(&data->update_lock);
+       i2c_smbus_write_byte_data(client, TMP401_TEMP_MSB_WRITE[5][0], val);
+       data->valid = 0;
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t show_update_interval(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct tmp401_data *data = i2c_get_clientdata(client);
+
+       return sprintf(buf, "%u\n", data->update_interval);
+}
+
+static ssize_t set_update_interval(struct device *dev,
+                                  struct device_attribute *attr,
+                                  const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct tmp401_data *data = i2c_get_clientdata(client);
+       unsigned long val;
+       int err, rate;
+
+       err = kstrtoul(buf, 10, &val);
+       if (err)
+               return err;
+
+       /*
+        * For valid rates, interval can be calculated as
+        *      interval = (1 << (7 - rate)) * 125;
+        * Rounded rate is therefore
+        *      rate = 7 - __fls(interval * 4 / (125 * 3));
+        * Use clamp_val() to avoid overflows, and to ensure valid input
+        * for __fls.
+        */
+       val = clamp_val(val, 125, 16000);
+       rate = 7 - __fls(val * 4 / (125 * 3));
+       mutex_lock(&data->update_lock);
+       i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, rate);
+       data->update_interval = (1 << (7 - rate)) * 125;
+       mutex_unlock(&data->update_lock);
 
        return count;
 }
 
-static struct sensor_device_attribute tmp401_attr[] = {
-       SENSOR_ATTR(temp1_input, S_IRUGO, show_temp_value, NULL, 0),
-       SENSOR_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_min,
-                   store_temp_min, 0),
-       SENSOR_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
-                   store_temp_max, 0),
-       SENSOR_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp_crit,
-                   store_temp_crit, 0),
-       SENSOR_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp_crit_hyst,
-                   store_temp_crit_hyst, 0),
-       SENSOR_ATTR(temp1_min_alarm, S_IRUGO, show_status, NULL,
-                   TMP401_STATUS_LOCAL_LOW),
-       SENSOR_ATTR(temp1_max_alarm, S_IRUGO, show_status, NULL,
-                   TMP401_STATUS_LOCAL_HIGH),
-       SENSOR_ATTR(temp1_crit_alarm, S_IRUGO, show_status, NULL,
-                   TMP401_STATUS_LOCAL_CRIT),
-       SENSOR_ATTR(temp2_input, S_IRUGO, show_temp_value, NULL, 1),
-       SENSOR_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_min,
-                   store_temp_min, 1),
-       SENSOR_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_max,
-                   store_temp_max, 1),
-       SENSOR_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp_crit,
-                   store_temp_crit, 1),
-       SENSOR_ATTR(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 1),
-       SENSOR_ATTR(temp2_fault, S_IRUGO, show_status, NULL,
-                   TMP401_STATUS_REMOTE_OPEN),
-       SENSOR_ATTR(temp2_min_alarm, S_IRUGO, show_status, NULL,
-                   TMP401_STATUS_REMOTE_LOW),
-       SENSOR_ATTR(temp2_max_alarm, S_IRUGO, show_status, NULL,
-                   TMP401_STATUS_REMOTE_HIGH),
-       SENSOR_ATTR(temp2_crit_alarm, S_IRUGO, show_status, NULL,
-                   TMP401_STATUS_REMOTE_CRIT),
+static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_min, S_IWUSR | S_IRUGO, show_temp,
+                           store_temp, 1, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_max, S_IWUSR | S_IRUGO, show_temp,
+                           store_temp, 2, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_crit, S_IWUSR | S_IRUGO, show_temp,
+                           store_temp, 3, 0);
+static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO,
+                         show_temp_crit_hyst, store_temp_crit_hyst, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_min_alarm, S_IRUGO, show_status, NULL,
+                           1, TMP432_STATUS_LOCAL);
+static SENSOR_DEVICE_ATTR_2(temp1_max_alarm, S_IRUGO, show_status, NULL,
+                           2, TMP432_STATUS_LOCAL);
+static SENSOR_DEVICE_ATTR_2(temp1_crit_alarm, S_IRUGO, show_status, NULL,
+                           3, TMP432_STATUS_LOCAL);
+static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1);
+static SENSOR_DEVICE_ATTR_2(temp2_min, S_IWUSR | S_IRUGO, show_temp,
+                           store_temp, 1, 1);
+static SENSOR_DEVICE_ATTR_2(temp2_max, S_IWUSR | S_IRUGO, show_temp,
+                           store_temp, 2, 1);
+static SENSOR_DEVICE_ATTR_2(temp2_crit, S_IWUSR | S_IRUGO, show_temp,
+                           store_temp, 3, 1);
+static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst,
+                         NULL, 1);
+static SENSOR_DEVICE_ATTR_2(temp2_fault, S_IRUGO, show_status, NULL,
+                           0, TMP432_STATUS_REMOTE1);
+static SENSOR_DEVICE_ATTR_2(temp2_min_alarm, S_IRUGO, show_status, NULL,
+                           1, TMP432_STATUS_REMOTE1);
+static SENSOR_DEVICE_ATTR_2(temp2_max_alarm, S_IRUGO, show_status, NULL,
+                           2, TMP432_STATUS_REMOTE1);
+static SENSOR_DEVICE_ATTR_2(temp2_crit_alarm, S_IRUGO, show_status, NULL,
+                           3, TMP432_STATUS_REMOTE1);
+
+static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval,
+                  set_update_interval);
+
+static struct attribute *tmp401_attributes[] = {
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp1_min.dev_attr.attr,
+       &sensor_dev_attr_temp1_max.dev_attr.attr,
+       &sensor_dev_attr_temp1_crit.dev_attr.attr,
+       &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+
+       &sensor_dev_attr_temp2_input.dev_attr.attr,
+       &sensor_dev_attr_temp2_min.dev_attr.attr,
+       &sensor_dev_attr_temp2_max.dev_attr.attr,
+       &sensor_dev_attr_temp2_crit.dev_attr.attr,
+       &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp2_fault.dev_attr.attr,
+       &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+
+       &dev_attr_update_interval.attr,
+
+       NULL
+};
+
+static const struct attribute_group tmp401_group = {
+       .attrs = tmp401_attributes,
 };
 
 /*
@@ -502,12 +556,60 @@ static struct sensor_device_attribute tmp401_attr[] = {
  * minimum and maximum register reset for both the local
  * and remote channels.
  */
-static struct sensor_device_attribute tmp411_attr[] = {
-       SENSOR_ATTR(temp1_highest, S_IRUGO, show_temp_highest, NULL, 0),
-       SENSOR_ATTR(temp1_lowest, S_IRUGO, show_temp_lowest, NULL, 0),
-       SENSOR_ATTR(temp2_highest, S_IRUGO, show_temp_highest, NULL, 1),
-       SENSOR_ATTR(temp2_lowest, S_IRUGO, show_temp_lowest, NULL, 1),
-       SENSOR_ATTR(temp_reset_history, S_IWUSR, NULL, reset_temp_history, 0),
+static SENSOR_DEVICE_ATTR_2(temp1_lowest, S_IRUGO, show_temp, NULL, 4, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_highest, S_IRUGO, show_temp, NULL, 5, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_lowest, S_IRUGO, show_temp, NULL, 4, 1);
+static SENSOR_DEVICE_ATTR_2(temp2_highest, S_IRUGO, show_temp, NULL, 5, 1);
+static SENSOR_DEVICE_ATTR(temp_reset_history, S_IWUSR, NULL, reset_temp_history,
+                         0);
+
+static struct attribute *tmp411_attributes[] = {
+       &sensor_dev_attr_temp1_highest.dev_attr.attr,
+       &sensor_dev_attr_temp1_lowest.dev_attr.attr,
+       &sensor_dev_attr_temp2_highest.dev_attr.attr,
+       &sensor_dev_attr_temp2_lowest.dev_attr.attr,
+       &sensor_dev_attr_temp_reset_history.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group tmp411_group = {
+       .attrs = tmp411_attributes,
+};
+
+static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2);
+static SENSOR_DEVICE_ATTR_2(temp3_min, S_IWUSR | S_IRUGO, show_temp,
+                           store_temp, 1, 2);
+static SENSOR_DEVICE_ATTR_2(temp3_max, S_IWUSR | S_IRUGO, show_temp,
+                           store_temp, 2, 2);
+static SENSOR_DEVICE_ATTR_2(temp3_crit, S_IWUSR | S_IRUGO, show_temp,
+                           store_temp, 3, 2);
+static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst,
+                         NULL, 2);
+static SENSOR_DEVICE_ATTR_2(temp3_fault, S_IRUGO, show_status, NULL,
+                           0, TMP432_STATUS_REMOTE2);
+static SENSOR_DEVICE_ATTR_2(temp3_min_alarm, S_IRUGO, show_status, NULL,
+                           1, TMP432_STATUS_REMOTE2);
+static SENSOR_DEVICE_ATTR_2(temp3_max_alarm, S_IRUGO, show_status, NULL,
+                           2, TMP432_STATUS_REMOTE2);
+static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO, show_status, NULL,
+                           3, TMP432_STATUS_REMOTE2);
+
+static struct attribute *tmp432_attributes[] = {
+       &sensor_dev_attr_temp3_input.dev_attr.attr,
+       &sensor_dev_attr_temp3_min.dev_attr.attr,
+       &sensor_dev_attr_temp3_max.dev_attr.attr,
+       &sensor_dev_attr_temp3_crit.dev_attr.attr,
+       &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp3_fault.dev_attr.attr,
+       &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
+
+       NULL
+};
+
+static const struct attribute_group tmp432_group = {
+       .attrs = tmp432_attributes,
 };
 
 /*
@@ -517,9 +619,11 @@ static struct sensor_device_attribute tmp411_attr[] = {
 static void tmp401_init_client(struct i2c_client *client)
 {
        int config, config_orig;
+       struct tmp401_data *data = i2c_get_clientdata(client);
 
        /* Set the conversion rate to 2 Hz */
        i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, 5);
+       data->update_interval = 500;
 
        /* Start conversions (disable shutdown if necessary) */
        config = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
@@ -554,11 +658,35 @@ static int tmp401_detect(struct i2c_client *client,
 
        switch (reg) {
        case TMP401_DEVICE_ID:
+               if (client->addr != 0x4c)
+                       return -ENODEV;
                kind = tmp401;
                break;
-       case TMP411_DEVICE_ID:
+       case TMP411A_DEVICE_ID:
+               if (client->addr != 0x4c)
+                       return -ENODEV;
+               kind = tmp411;
+               break;
+       case TMP411B_DEVICE_ID:
+               if (client->addr != 0x4d)
+                       return -ENODEV;
                kind = tmp411;
                break;
+       case TMP411C_DEVICE_ID:
+               if (client->addr != 0x4e)
+                       return -ENODEV;
+               kind = tmp411;
+               break;
+       case TMP431_DEVICE_ID:
+               if (client->addr == 0x4e)
+                       return -ENODEV;
+               kind = tmp431;
+               break;
+       case TMP432_DEVICE_ID:
+               if (client->addr == 0x4e)
+                       return -ENODEV;
+               kind = tmp432;
+               break;
        default:
                return -ENODEV;
        }
@@ -579,20 +707,19 @@ static int tmp401_detect(struct i2c_client *client,
 
 static int tmp401_remove(struct i2c_client *client)
 {
+       struct device *dev = &client->dev;
        struct tmp401_data *data = i2c_get_clientdata(client);
-       int i;
 
        if (data->hwmon_dev)
                hwmon_device_unregister(data->hwmon_dev);
 
-       for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++)
-               device_remove_file(&client->dev, &tmp401_attr[i].dev_attr);
+       sysfs_remove_group(&dev->kobj, &tmp401_group);
 
-       if (data->kind == tmp411) {
-               for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++)
-                       device_remove_file(&client->dev,
-                                          &tmp411_attr[i].dev_attr);
-       }
+       if (data->kind == tmp411)
+               sysfs_remove_group(&dev->kobj, &tmp411_group);
+
+       if (data->kind == tmp432)
+               sysfs_remove_group(&dev->kobj, &tmp432_group);
 
        return 0;
 }
@@ -600,12 +727,12 @@ static int tmp401_remove(struct i2c_client *client)
 static int tmp401_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
 {
-       int i, err = 0;
+       struct device *dev = &client->dev;
+       int err;
        struct tmp401_data *data;
-       const char *names[] = { "TMP401", "TMP411" };
+       const char *names[] = { "TMP401", "TMP411", "TMP431", "TMP432" };
 
-       data = devm_kzalloc(&client->dev, sizeof(struct tmp401_data),
-                           GFP_KERNEL);
+       data = devm_kzalloc(dev, sizeof(struct tmp401_data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
@@ -617,31 +744,32 @@ static int tmp401_probe(struct i2c_client *client,
        tmp401_init_client(client);
 
        /* Register sysfs hooks */
-       for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++) {
-               err = device_create_file(&client->dev,
-                                        &tmp401_attr[i].dev_attr);
+       err = sysfs_create_group(&dev->kobj, &tmp401_group);
+       if (err)
+               return err;
+
+       /* Register additional tmp411 sysfs hooks */
+       if (data->kind == tmp411) {
+               err = sysfs_create_group(&dev->kobj, &tmp411_group);
                if (err)
                        goto exit_remove;
        }
 
-       /* Register additional tmp411 sysfs hooks */
-       if (data->kind == tmp411) {
-               for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++) {
-                       err = device_create_file(&client->dev,
-                                                &tmp411_attr[i].dev_attr);
-                       if (err)
-                               goto exit_remove;
-               }
+       /* Register additional tmp432 sysfs hooks */
+       if (data->kind == tmp432) {
+               err = sysfs_create_group(&dev->kobj, &tmp432_group);
+               if (err)
+                       goto exit_remove;
        }
 
-       data->hwmon_dev = hwmon_device_register(&client->dev);
+       data->hwmon_dev = hwmon_device_register(dev);
        if (IS_ERR(data->hwmon_dev)) {
                err = PTR_ERR(data->hwmon_dev);
                data->hwmon_dev = NULL;
                goto exit_remove;
        }
 
-       dev_info(&client->dev, "Detected TI %s chip\n", names[data->kind]);
+       dev_info(dev, "Detected TI %s chip\n", names[data->kind]);
 
        return 0;
 
index 6a8ded29f1ed55ddd21c6b0e7532c911a6649937..964c1d68827400ec9ac6d5ce515418c74294d3aa 100644 (file)
@@ -208,8 +208,8 @@ static int tmp421_init_client(struct i2c_client *client)
        /* Start conversions (disable shutdown if necessary) */
        config = i2c_smbus_read_byte_data(client, TMP421_CONFIG_REG_1);
        if (config < 0) {
-               dev_err(&client->dev, "Could not read configuration"
-                        " register (%d)\n", config);
+               dev_err(&client->dev,
+                       "Could not read configuration register (%d)\n", config);
                return -ENODEV;
        }
 
@@ -322,6 +322,5 @@ static struct i2c_driver tmp421_driver = {
 module_i2c_driver(tmp421_driver);
 
 MODULE_AUTHOR("Andre Prendel <andre.prendel@gmx.de>");
-MODULE_DESCRIPTION("Texas Instruments TMP421/422/423 temperature sensor"
-                  " driver");
+MODULE_DESCRIPTION("Texas Instruments TMP421/422/423 temperature sensor driver");
 MODULE_LICENSE("GPL");
index 3123b30208c580064596bbc41f3e1e9e168957b9..c9dcce8c3dc34fa7ec8e7eeb0c8ee6812dc159ce 100644 (file)
@@ -125,7 +125,7 @@ static const u8 VIA686A_REG_TEMP_HYST[]     = { 0x3a, 0x3e, 0x1e };
  * (These conversions were contributed by Jonathan Teh Soon Yew
  * <j.teh@iname.com>)
  */
-static inline u8 IN_TO_REG(long val, int inNum)
+static inline u8 IN_TO_REG(long val, int in_num)
 {
        /*
         * To avoid floating point, we multiply constants by 10 (100 for +12V).
@@ -134,29 +134,29 @@ static inline u8 IN_TO_REG(long val, int inNum)
         * by an additional 10000 (100000 for +12V): 1000 for val and 10 (100)
         * for the constants.
         */
-       if (inNum <= 1)
+       if (in_num <= 1)
                return (u8) clamp_val((val * 21024 - 1205000) / 250000, 0, 255);
-       else if (inNum == 2)
+       else if (in_num == 2)
                return (u8) clamp_val((val * 15737 - 1205000) / 250000, 0, 255);
-       else if (inNum == 3)
+       else if (in_num == 3)
                return (u8) clamp_val((val * 10108 - 1205000) / 250000, 0, 255);
        else
                return (u8) clamp_val((val * 41714 - 12050000) / 2500000, 0,
                                      255);
 }
 
-static inline long IN_FROM_REG(u8 val, int inNum)
+static inline long IN_FROM_REG(u8 val, int in_num)
 {
        /*
         * To avoid floating point, we multiply constants by 10 (100 for +12V).
         * We also multiply them by 1000 because we want 0.001V/bit for the
         * output value. Rounding is done.
         */
-       if (inNum <= 1)
+       if (in_num <= 1)
                return (long) ((250000 * val + 1330000 + 21024 / 2) / 21024);
-       else if (inNum == 2)
+       else if (in_num == 2)
                return (long) ((250000 * val + 1330000 + 15737 / 2) / 15737);
-       else if (inNum == 3)
+       else if (in_num == 3)
                return (long) ((250000 * val + 1330000 + 10108 / 2) / 10108);
        else
                return (long) ((2500000 * val + 13300000 + 41714 / 2) / 41714);
@@ -210,10 +210,10 @@ static inline u8 FAN_TO_REG(long rpm, int div)
  * VIA register values 0-255.  I *10 before rounding, so we get tenth-degree
  * precision.  (I could have done all 1024 values for our 10-bit readings,
  * but the function is very linear in the useful range (0-80 deg C), so
- * we'll just use linear interpolation for 10-bit readings.)  So, tempLUT
+ * we'll just use linear interpolation for 10-bit readings.)  So, temp_lut
  * is the temp at via register values 0-255:
  */
-static const s16 tempLUT[] = {
+static const s16 temp_lut[] = {
        -709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519,
        -503, -487, -471, -456, -442, -428, -414, -400, -387, -375,
        -362, -350, -339, -327, -316, -305, -295, -285, -275, -265,
@@ -261,7 +261,7 @@ static const s16 tempLUT[] = {
  * - 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01)
  * Note that n=161:
  */
-static const u8 viaLUT[] = {
+static const u8 via_lut[] = {
        12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23,
        23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 39, 40,
        41, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 60, 62, 64, 66,
@@ -284,26 +284,26 @@ static const u8 viaLUT[] = {
  */
 static inline u8 TEMP_TO_REG(long val)
 {
-       return viaLUT[val <= -50000 ? 0 : val >= 110000 ? 160 :
+       return via_lut[val <= -50000 ? 0 : val >= 110000 ? 160 :
                      (val < 0 ? val - 500 : val + 500) / 1000 + 50];
 }
 
 /* for 8-bit temperature hyst and over registers */
-#define TEMP_FROM_REG(val)     ((long)tempLUT[val] * 100)
+#define TEMP_FROM_REG(val)     ((long)temp_lut[val] * 100)
 
 /* for 10-bit temperature readings */
 static inline long TEMP_FROM_REG10(u16 val)
 {
-       u16 eightBits = val >> 2;
-       u16 twoBits = val & 3;
+       u16 eight_bits = val >> 2;
+       u16 two_bits = val & 3;
 
        /* no interpolation for these */
-       if (twoBits == 0 || eightBits == 255)
-               return TEMP_FROM_REG(eightBits);
+       if (two_bits == 0 || eight_bits == 255)
+               return TEMP_FROM_REG(eight_bits);
 
        /* do some linear interpolation */
-       return (tempLUT[eightBits] * (4 - twoBits) +
-               tempLUT[eightBits + 1] * twoBits) * 25;
+       return (temp_lut[eight_bits] * (4 - two_bits) +
+               temp_lut[eight_bits + 1] * two_bits) * 25;
 }
 
 #define DIV_FROM_REG(val) (1 << (val))
@@ -889,8 +889,8 @@ static int via686a_pci_probe(struct pci_dev *dev,
 
        address = val & ~(VIA686A_EXTENT - 1);
        if (address == 0) {
-               dev_err(&dev->dev, "base address not set - upgrade BIOS "
-                       "or use force_addr=0xaddr\n");
+               dev_err(&dev->dev,
+                       "base address not set - upgrade BIOS or use force_addr=0xaddr\n");
                return -ENODEV;
        }
 
@@ -899,8 +899,9 @@ static int via686a_pci_probe(struct pci_dev *dev,
                return -ENODEV;
        if (!(val & 0x0001)) {
                if (!force_addr) {
-                       dev_warn(&dev->dev, "Sensors disabled, enable "
-                                "with force_addr=0x%x\n", address);
+                       dev_warn(&dev->dev,
+                                "Sensors disabled, enable with force_addr=0x%x\n",
+                                address);
                        return -ENODEV;
                }
 
index dcc62f80f67bf967c8e38d9f85b3be31fe716979..6b2f1a42b3ff9d43f200234f6a408ad00a798170 100644 (file)
@@ -571,8 +571,9 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
                        break;
                default:
                        count = -EINVAL;
-                       dev_warn(dev, "fan div value %ld not supported. "
-                                "Choose one of 1, 2, 4, or 8.\n", val);
+                       dev_warn(dev,
+                                "fan div value %ld not supported. Choose one of 1, 2, 4, or 8.\n",
+                                val);
                        goto EXIT;
                }
                vt1211_write8(data, VT1211_REG_FAN_DIV,
@@ -674,8 +675,9 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
                        break;
                default:
                        count = -EINVAL;
-                       dev_warn(dev, "pwm mode %ld not supported. "
-                                "Choose one of 0 or 2.\n", val);
+                       dev_warn(dev,
+                                "pwm mode %ld not supported. Choose one of 0 or 2.\n",
+                                val);
                        goto EXIT;
                }
                vt1211_write8(data, VT1211_REG_PWM_CTL,
@@ -700,8 +702,9 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
        case SHOW_SET_PWM_AUTO_CHANNELS_TEMP:
                if (val < 1 || val > 7) {
                        count = -EINVAL;
-                       dev_warn(dev, "temp channel %ld not supported. "
-                                "Choose a value between 1 and 7.\n", val);
+                       dev_warn(dev,
+                                "temp channel %ld not supported. Choose a value between 1 and 7.\n",
+                                val);
                        goto EXIT;
                }
                if (!ISTEMP(val - 1, data->uch_config)) {
@@ -1325,15 +1328,15 @@ static int __init vt1211_init(void)
 
        if ((uch_config < -1) || (uch_config > 31)) {
                err = -EINVAL;
-               pr_warn("Invalid UCH configuration %d. "
-                       "Choose a value between 0 and 31.\n", uch_config);
+               pr_warn("Invalid UCH configuration %d. Choose a value between 0 and 31.\n",
+                       uch_config);
                goto EXIT;
        }
 
        if ((int_mode < -1) || (int_mode > 0)) {
                err = -EINVAL;
-               pr_warn("Invalid interrupt mode %d. "
-                       "Only mode 0 is supported.\n", int_mode);
+               pr_warn("Invalid interrupt mode %d. Only mode 0 is supported.\n",
+                       int_mode);
                goto EXIT;
        }
 
index 988a2a79676496e3acd7ec5e1e8e4c1399c9bf39..0e7017841f7dacb9b1341e4313f4acaf009349a6 100644 (file)
@@ -573,8 +573,9 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
                data->fan_div[nr] = 3;
                break;
        default:
-               dev_err(dev, "fan_div value %ld not supported. "
-                       "Choose one of 1, 2, 4 or 8!\n", val);
+               dev_err(dev,
+                       "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n",
+                       val);
                mutex_unlock(&data->update_lock);
                return -EINVAL;
        }
index 0a89211c25f6df2ad94efdb472a6cf547f502422..016027229e81835f205f0cf90afb6a4575043d63 100644 (file)
@@ -840,8 +840,8 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
                            && (reg >= 0xff || (sio_data->kind == nct6775
                                                && reg == 0x00))
                            && data->fan_div[i] < 0x07) {
-                               dev_dbg(dev, "Increasing fan%d "
-                                       "clock divider from %u to %u\n",
+                               dev_dbg(dev,
+                                       "Increasing fan%d clock divider from %u to %u\n",
                                        i + 1, div_from_reg(data->fan_div[i]),
                                        div_from_reg(data->fan_div[i] + 1));
                                data->fan_div[i]++;
@@ -1110,9 +1110,9 @@ store_fan_min(struct device *dev, struct device_attribute *attr,
                 */
                data->fan_min[nr] = 254;
                new_div = 7; /* 128 == (1 << 7) */
-               dev_warn(dev, "fan%u low limit %lu below minimum %u, set to "
-                        "minimum\n", nr + 1, val,
-                        data->fan_from_reg_min(254, 7));
+               dev_warn(dev,
+                        "fan%u low limit %lu below minimum %u, set to minimum\n",
+                        nr + 1, val, data->fan_from_reg_min(254, 7));
        } else if (!reg) {
                /*
                 * Speed above this value cannot possibly be represented,
@@ -1120,9 +1120,9 @@ store_fan_min(struct device *dev, struct device_attribute *attr,
                 */
                data->fan_min[nr] = 1;
                new_div = 0; /* 1 == (1 << 0) */
-               dev_warn(dev, "fan%u low limit %lu above maximum %u, set to "
-                        "maximum\n", nr + 1, val,
-                        data->fan_from_reg_min(1, 0));
+               dev_warn(dev,
+                        "fan%u low limit %lu above maximum %u, set to maximum\n",
+                        nr + 1, val, data->fan_from_reg_min(1, 0));
        } else {
                /*
                 * Automatically pick the best divider, i.e. the one such
@@ -2396,15 +2396,15 @@ static int w83627ehf_probe(struct platform_device *pdev)
                                en_vrm10 = superio_inb(sio_data->sioreg,
                                                       SIO_REG_EN_VRM10);
                                if ((en_vrm10 & 0x08) && data->vrm == 90) {
-                                       dev_warn(dev, "Setting VID input "
-                                                "voltage to TTL\n");
+                                       dev_warn(dev,
+                                                "Setting VID input voltage to TTL\n");
                                        superio_outb(sio_data->sioreg,
                                                     SIO_REG_EN_VRM10,
                                                     en_vrm10 & ~0x08);
                                } else if (!(en_vrm10 & 0x08)
                                           && data->vrm == 100) {
-                                       dev_warn(dev, "Setting VID input "
-                                                "voltage to VRM10\n");
+                                       dev_warn(dev,
+                                                "Setting VID input voltage to VRM10\n");
                                        superio_outb(sio_data->sioreg,
                                                     SIO_REG_EN_VRM10,
                                                     en_vrm10 | 0x08);
@@ -2420,8 +2420,8 @@ static int w83627ehf_probe(struct platform_device *pdev)
                        if (err)
                                goto exit_release;
                } else {
-                       dev_info(dev, "VID pins in output mode, CPU VID not "
-                                "available\n");
+                       dev_info(dev,
+                                "VID pins in output mode, CPU VID not available\n");
                }
        }
 
@@ -2795,8 +2795,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
        /* Activate logical device if needed */
        val = superio_inb(sioaddr, SIO_REG_ENABLE);
        if (!(val & 0x01)) {
-               pr_warn("Forcibly enabling Super-I/O. "
-                       "Sensor is probably unusable.\n");
+               pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n");
                superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01);
        }
 
index aeec5b1d81c93d10c81c7d79d1ef65f3996123f2..f9d513949a38a7a47dcb9317c529ef7b5f93a38a 100644 (file)
@@ -64,8 +64,8 @@ enum chips { w83781d, w83782d, w83783s, as99127f };
 /* Insmod parameters */
 static unsigned short force_subclients[4];
 module_param_array(force_subclients, short, NULL, 0);
-MODULE_PARM_DESC(force_subclients, "List of subclient addresses: "
-                   "{bus, clientaddr, subclientaddr1, subclientaddr2}");
+MODULE_PARM_DESC(force_subclients,
+                "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}");
 
 static bool reset;
 module_param(reset, bool, 0);
@@ -826,8 +826,9 @@ store_sensor(struct device *dev, struct device_attribute *da,
                data->sens[nr] = val;
                break;
        case W83781D_DEFAULT_BETA:
-               dev_warn(dev, "Sensor type %d is deprecated, please use 4 "
-                        "instead\n", W83781D_DEFAULT_BETA);
+               dev_warn(dev,
+                        "Sensor type %d is deprecated, please use 4 instead\n",
+                        W83781D_DEFAULT_BETA);
                /* fall through */
        case 4:         /* thermistor */
                tmp = w83781d_read_value(data, W83781D_REG_SCFG1);
@@ -874,8 +875,8 @@ w83781d_detect_subclients(struct i2c_client *new_client)
                for (i = 2; i <= 3; i++) {
                        if (force_subclients[i] < 0x48 ||
                            force_subclients[i] > 0x4f) {
-                               dev_err(&new_client->dev, "Invalid subclient "
-                                       "address %d; must be 0x48-0x4f\n",
+                               dev_err(&new_client->dev,
+                                       "Invalid subclient address %d; must be 0x48-0x4f\n",
                                        force_subclients[i]);
                                err = -EINVAL;
                                goto ERROR_SC_1;
@@ -910,9 +911,9 @@ w83781d_detect_subclients(struct i2c_client *new_client)
        for (i = 0; i < num_sc; i++) {
                data->lm75[i] = i2c_new_dummy(adapter, sc_addr[i]);
                if (!data->lm75[i]) {
-                       dev_err(&new_client->dev, "Subclient %d "
-                               "registration at address 0x%x "
-                               "failed.\n", i, sc_addr[i]);
+                       dev_err(&new_client->dev,
+                               "Subclient %d registration at address 0x%x failed.\n",
+                               i, sc_addr[i]);
                        err = -ENOMEM;
                        if (i == 1)
                                goto ERROR_SC_3;
@@ -1176,8 +1177,9 @@ w83781d_detect(struct i2c_client *client, struct i2c_board_info *info)
                goto err_nodev;
 
        if (val1 <= 0x30 && w83781d_alias_detect(client, val1)) {
-               dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
-                       "be the same as ISA device\n", address);
+               dev_dbg(&adapter->dev,
+                       "Device at 0x%02x appears to be the same as ISA device\n",
+                       address);
                goto err_nodev;
        }
 
@@ -1367,8 +1369,8 @@ w83781d_init_device(struct device *dev)
                 * as I see very little reason why this would be needed at
                 * all.
                 */
-               dev_info(dev, "If reset=1 solved a problem you were "
-                        "having, please report!\n");
+               dev_info(dev,
+                        "If reset=1 solved a problem you were having, please report!\n");
 
                /* save these registers */
                i = w83781d_read_value(data, W83781D_REG_BEEP_CONFIG);
@@ -1425,8 +1427,8 @@ w83781d_init_device(struct device *dev)
                /* Enable temp2 */
                tmp = w83781d_read_value(data, W83781D_REG_TEMP2_CONFIG);
                if (tmp & 0x01) {
-                       dev_warn(dev, "Enabling temp2, readings "
-                                "might not make sense\n");
+                       dev_warn(dev,
+                                "Enabling temp2, readings might not make sense\n");
                        w83781d_write_value(data, W83781D_REG_TEMP2_CONFIG,
                                tmp & 0xfe);
                }
@@ -1436,8 +1438,8 @@ w83781d_init_device(struct device *dev)
                        tmp = w83781d_read_value(data,
                                W83781D_REG_TEMP3_CONFIG);
                        if (tmp & 0x01) {
-                               dev_warn(dev, "Enabling temp3, "
-                                        "readings might not make sense\n");
+                               dev_warn(dev,
+                                        "Enabling temp3, readings might not make sense\n");
                                w83781d_write_value(data,
                                        W83781D_REG_TEMP3_CONFIG, tmp & 0xfe);
                        }
index 38dddddf887560f53add38dd9477acb6afef986f..a3feee332e200bd1cd1fd38e91b32a2b9ba00e28 100644 (file)
@@ -56,8 +56,8 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
 
 static unsigned short force_subclients[4];
 module_param_array(force_subclients, short, NULL, 0);
-MODULE_PARM_DESC(force_subclients, "List of subclient addresses: "
-                       "{bus, clientaddr, subclientaddr1, subclientaddr2}");
+MODULE_PARM_DESC(force_subclients,
+                "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}");
 
 static bool reset;
 module_param(reset, bool, 0);
index 5cb83ddf2cc6a48e1c7214a6e3a8b5706f11af1f..0b804895be43361a9230b4632e101945091298ad 100644 (file)
@@ -54,8 +54,8 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
 
 static unsigned short force_subclients[4];
 module_param_array(force_subclients, short, NULL, 0);
-MODULE_PARM_DESC(force_subclients, "List of subclient addresses: "
-                       "{bus, clientaddr, subclientaddr1, subclientaddr2}");
+MODULE_PARM_DESC(force_subclients,
+                "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}");
 
 static bool init;
 module_param(init, bool, 0);
@@ -951,8 +951,8 @@ w83792d_detect_subclients(struct i2c_client *new_client)
                for (i = 2; i <= 3; i++) {
                        if (force_subclients[i] < 0x48 ||
                            force_subclients[i] > 0x4f) {
-                               dev_err(&new_client->dev, "invalid subclient "
-                                       "address %d; must be 0x48-0x4f\n",
+                               dev_err(&new_client->dev,
+                                       "invalid subclient address %d; must be 0x48-0x4f\n",
                                        force_subclients[i]);
                                err = -ENODEV;
                                goto ERROR_SC_0;
@@ -969,8 +969,9 @@ w83792d_detect_subclients(struct i2c_client *new_client)
        if (!(val & 0x80)) {
                if ((data->lm75[0] != NULL) &&
                        ((val & 0x7) == ((val >> 4) & 0x7))) {
-                       dev_err(&new_client->dev, "duplicate addresses 0x%x, "
-                               "use force_subclient\n", data->lm75[0]->addr);
+                       dev_err(&new_client->dev,
+                               "duplicate addresses 0x%x, use force_subclient\n",
+                               data->lm75[0]->addr);
                        err = -ENODEV;
                        goto ERROR_SC_1;
                }
index 660427520c539a6993207862bdae6d2247fb2924..b0c30a546ff2e3079510e4858c6e8e15899ba092 100644 (file)
@@ -59,8 +59,8 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
 
 static unsigned short force_subclients[4];
 module_param_array(force_subclients, short, NULL, 0);
-MODULE_PARM_DESC(force_subclients, "List of subclient addresses: "
-                      "{bus, clientaddr, subclientaddr1, subclientaddr2}");
+MODULE_PARM_DESC(force_subclients,
+                "List of subclient addresses: {bus, clientaddr, subclientaddr1, subclientaddr2}");
 
 static bool reset;
 module_param(reset, bool, 0);
@@ -1921,8 +1921,8 @@ static int w83793_probe(struct i2c_client *client,
        }
        if (i == ARRAY_SIZE(watchdog_minors)) {
                data->watchdog_miscdev.minor = 0;
-               dev_warn(&client->dev, "Couldn't register watchdog chardev "
-                       "(due to no free minor)\n");
+               dev_warn(&client->dev,
+                        "Couldn't register watchdog chardev (due to no free minor)\n");
        }
 
        mutex_unlock(&watchdog_data_mutex);
index e226096148eb5eefd7db06d302f204341e4ba0a5..908209d24664e5fd206c0bfd53738a2030f1b1b6 100644 (file)
@@ -2120,11 +2120,12 @@ static void w83795_check_dynamic_in_limits(struct i2c_client *client)
                                           &w83795_in[i][3].dev_attr.attr,
                                           S_IRUGO);
                if (err_max || err_min)
-                       dev_warn(&client->dev, "Failed to set in%d limits "
-                                "read-only (%d, %d)\n", i, err_max, err_min);
+                       dev_warn(&client->dev,
+                                "Failed to set in%d limits read-only (%d, %d)\n",
+                                i, err_max, err_min);
                else
-                       dev_info(&client->dev, "in%d limits set dynamically "
-                                "from VID\n", i);
+                       dev_info(&client->dev,
+                                "in%d limits set dynamically from VID\n", i);
        }
 }
 
index 88734e871e3a8cc303c75beb121190a56ec58962..c7285b57546268e028d859b00328cacb018b637d 100644 (file)
@@ -21,6 +21,8 @@
 #ifndef _LINUX_NTC_H
 #define _LINUX_NTC_H
 
+struct iio_channel;
+
 enum ntc_thermistor_type {
        TYPE_NCPXXWB473,
        TYPE_NCPXXWL333,
@@ -39,13 +41,17 @@ struct ntc_thermistor_platform_data {
         * described at Documentation/hwmon/ntc_thermistor
         *
         * pullup/down_ohm: 0 for infinite / not-connected
+        *
+        * chan: iio_channel pointer to communicate with the ADC which the
+        * thermistor is using for conversion of the analog values.
         */
-       int (*read_uV)(void);
-       unsigned int pullup_uV;
+       int (*read_uv)(struct ntc_thermistor_platform_data *);
+       unsigned int pullup_uv;
 
        unsigned int pullup_ohm;
        unsigned int pulldown_ohm;
        enum { NTC_CONNECTED_POSITIVE, NTC_CONNECTED_GROUND } connect;
+       struct iio_channel *chan;
 
        int (*read_ohm)(void);
 };