Merge branch 'release' of git://lm-sensors.org/kernel/mhoffman/hwmon-2.6
Linus Torvalds [Sun, 14 Oct 2007 19:50:19 +0000 (12:50 -0700)]
* 'release' of git://lm-sensors.org/kernel/mhoffman/hwmon-2.6: (53 commits)
  hwmon: (vt8231) fix sparse warning
  hwmon: (sis5595) fix sparse warning
  hwmon: (w83627hf) don't assume bank 0
  hwmon: (w83627hf) Fix setting fan min right after driver load
  hwmon: (w83627hf) De-macro sysfs callback functions
  hwmon: Add new combined driver for FSC chips
  hwmon: (ibmpex) Release IPMI user if hwmon registration fails
  hwmon: (dme1737) Add sch311x support
  hwmon: (dme1737) group functions logically
  hwmon: (dme1737) cleanups
  hwmon: IBM power meter driver
  hwmon: (coretemp) Add support for Celeron 4xx
  hwmon: (lm87) Disable VID when it should be
  hwmon: (w83781d) Add individual alarm and beep files
  hwmon: VRM is not read from registers
  MAINTAINERS: update hwmon subsystem git trees
  hwmon: Fix the code examples in documentation
  hwmon: update sysfs interface document - error handling
  hwmon: (thmc50) Fix a debug message
  hwmon: (thmc50) Don't create temp3 if not enabled
  ...

75 files changed:
Documentation/hwmon/coretemp
Documentation/hwmon/dme1737
Documentation/hwmon/f71805f
Documentation/hwmon/it87
Documentation/hwmon/lm78
Documentation/hwmon/lm93
Documentation/hwmon/sysfs-interface
Documentation/hwmon/w83791d
MAINTAINERS
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/abituguru.c
drivers/hwmon/abituguru3.c
drivers/hwmon/ad7418.c
drivers/hwmon/adm1021.c
drivers/hwmon/adm1025.c
drivers/hwmon/adm1026.c
drivers/hwmon/adm1029.c
drivers/hwmon/adm1031.c
drivers/hwmon/adm9240.c
drivers/hwmon/adt7470.c [new file with mode: 0644]
drivers/hwmon/applesmc.c
drivers/hwmon/asb100.c
drivers/hwmon/atxp1.c
drivers/hwmon/coretemp.c
drivers/hwmon/dme1737.c
drivers/hwmon/ds1621.c
drivers/hwmon/f71805f.c
drivers/hwmon/f71882fg.c [new file with mode: 0644]
drivers/hwmon/f75375s.c [new file with mode: 0644]
drivers/hwmon/fscher.c
drivers/hwmon/fschmd.c [new file with mode: 0644]
drivers/hwmon/fscpos.c
drivers/hwmon/gl518sm.c
drivers/hwmon/gl520sm.c
drivers/hwmon/hwmon.c
drivers/hwmon/ibmpex.c [new file with mode: 0644]
drivers/hwmon/it87.c
drivers/hwmon/k8temp.c
drivers/hwmon/lm63.c
drivers/hwmon/lm70.c
drivers/hwmon/lm75.c
drivers/hwmon/lm75.h
drivers/hwmon/lm77.c
drivers/hwmon/lm78.c
drivers/hwmon/lm80.c
drivers/hwmon/lm83.c
drivers/hwmon/lm85.c
drivers/hwmon/lm87.c
drivers/hwmon/lm90.c
drivers/hwmon/lm92.c
drivers/hwmon/lm93.c
drivers/hwmon/max1619.c
drivers/hwmon/max6650.c
drivers/hwmon/pc87360.c
drivers/hwmon/pc87427.c
drivers/hwmon/sis5595.c
drivers/hwmon/smsc47b397.c
drivers/hwmon/smsc47m1.c
drivers/hwmon/smsc47m192.c
drivers/hwmon/thmc50.c
drivers/hwmon/via686a.c
drivers/hwmon/vt1211.c
drivers/hwmon/vt8231.c
drivers/hwmon/w83627ehf.c
drivers/hwmon/w83627hf.c
drivers/hwmon/w83781d.c
drivers/hwmon/w83791d.c
drivers/hwmon/w83792d.c
drivers/hwmon/w83793.c
drivers/hwmon/w83l785ts.c
drivers/input/touchscreen/ads7846.c
drivers/misc/thinkpad_acpi.c
drivers/misc/thinkpad_acpi.h
include/linux/hwmon.h

index 870cda9..170bf86 100644 (file)
@@ -4,7 +4,7 @@ Kernel driver coretemp
 Supported chips:
   * All Intel Core family
     Prefix: 'coretemp'
-    CPUID: family 0x6, models 0xe, 0xf
+    CPUID: family 0x6, models 0xe, 0xf, 0x16
     Datasheet: Intel 64 and IA-32 Architectures Software Developer's Manual
                Volume 3A: System Programming Guide
 
index 1a0f3d6..8f44607 100644 (file)
@@ -6,6 +6,10 @@ Supported chips:
     Prefix: 'dme1737'
     Addresses scanned: I2C 0x2c, 0x2d, 0x2e
     Datasheet: Provided by SMSC upon request and under NDA
+  * SMSC SCH3112, SCH3114, SCH3116
+    Prefix: 'sch311x'
+    Addresses scanned: none, address read from Super-I/O config space
+    Datasheet: http://www.nuhorizons.com/FeaturedProducts/Volume1/SMSC/311x.pdf
 
 Authors:
     Juerg Haefliger <juergh@gmail.com>
@@ -27,16 +31,25 @@ Description
 -----------
 
 This driver implements support for the hardware monitoring capabilities of the
-SMSC DME1737 and Asus A8000 (which are the same) Super-I/O chips. This chip
-features monitoring of 3 temp sensors temp[1-3] (2 remote diodes and 1
-internal), 7 voltages in[0-6] (6 external and 1 internal) and 6 fan speeds
-fan[1-6]. Additionally, the chip implements 5 PWM outputs pwm[1-3,5-6] for
-controlling fan speeds both manually and automatically.
-
-Fan[3-6] and pwm[3,5-6] are optional features and their availability is
-dependent on the configuration of the chip. The driver will detect which
-features are present during initialization and create the sysfs attributes
-accordingly.
+SMSC DME1737 and Asus A8000 (which are the same) and SMSC SCH311x Super-I/O
+chips. These chips feature monitoring of 3 temp sensors temp[1-3] (2 remote
+diodes and 1 internal), 7 voltages in[0-6] (6 external and 1 internal) and up
+to 6 fan speeds fan[1-6]. Additionally, the chips implement up to 5 PWM
+outputs pwm[1-3,5-6] for controlling fan speeds both manually and
+automatically.
+
+For the DME1737 and A8000, fan[1-2] and pwm[1-2] are always present. Fan[3-6]
+and pwm[3,5-6] are optional features and their availability depends on the
+configuration of the chip. The driver will detect which features are present
+during initialization and create the sysfs attributes accordingly.
+
+For the SCH311x, fan[1-3] and pwm[1-3] are always present and fan[4-6] and
+pwm[5-6] don't exist.
+
+The hardware monitoring features of the DME1737 and A8000 are only accessible
+via SMBus, while the SCH311x only provides access via the ISA bus. The driver
+will therefore register itself as an I2C client driver if it detects a DME1737
+or A8000 and as a platform driver if it detects a SCH311x chip.
 
 
 Voltage Monitoring
index 94e0d2c..f0d5597 100644 (file)
@@ -6,6 +6,10 @@ Supported chips:
     Prefix: 'f71805f'
     Addresses scanned: none, address read from Super I/O config space
     Datasheet: Available from the Fintek website
+  * Fintek F71806F/FG
+    Prefix: 'f71872f'
+    Addresses scanned: none, address read from Super I/O config space
+    Datasheet: Available from the Fintek website
   * Fintek F71872F/FG
     Prefix: 'f71872f'
     Addresses scanned: none, address read from Super I/O config space
@@ -38,6 +42,9 @@ The Fintek F71872F/FG Super I/O chip is almost the same, with two
 additional internal voltages monitored (VSB and battery). It also features
 6 VID inputs. The VID inputs are not yet supported by this driver.
 
+The Fintek F71806F/FG Super-I/O chip is essentially the same as the
+F71872F/FG, and is undistinguishable therefrom.
+
 The driver assumes that no more than one chip is present, which seems
 reasonable.
 
index 81ecc7e..5b704a4 100644 (file)
@@ -90,7 +90,8 @@ upper VID bits share their pins with voltage inputs (in5 and in6) so you
 can't have both on a given board.
 
 The IT8716F, IT8718F and later IT8712F revisions have support for
-2 additional fans. They are not yet supported by the driver.
+2 additional fans. They are supported by the driver for the IT8716F and
+IT8718F but not for the IT8712F
 
 The IT8716F and IT8718F, and late IT8712F and IT8705F also have optional
 16-bit tachometer counters for fans 1 to 3. This is better (no more fan
index fd5dc7a..dfc318a 100644 (file)
@@ -56,16 +56,6 @@ should work with. This is hardcoded by the mainboard and/or processor itself.
 It is a value in volts. When it is unconnected, you will often find the
 value 3.50 V here.
 
-In addition to the alarms described above, there are a couple of additional
-ones. There is a BTI alarm, which gets triggered when an external chip has
-crossed its limits. Usually, this is connected to all LM75 chips; if at
-least one crosses its limits, this bit gets set. The CHAS alarm triggers
-if your computer case is open. The FIFO alarms should never trigger; it
-indicates an internal error. The SMI_IN alarm indicates some other chip
-has triggered an SMI interrupt. As we do not use SMI interrupts at all,
-this condition usually indicates there is a problem with some other
-device.
-
 If an alarm triggers, it will remain triggered until the hardware register
 is read at least once. This means that the cause for the alarm may
 already have disappeared! Note that in the current implementation, all
index 4e4a1dc..ac711f3 100644 (file)
@@ -7,7 +7,7 @@ Supported chips:
     Addresses scanned: I2C 0x2c-0x2e
     Datasheet: http://www.national.com/ds.cgi/LM/LM93.pdf
 
-Author:
+Authors:
        Mark M. Hoffman <mhoffman@lightlink.com>
        Ported to 2.6 by Eric J. Bowersox <ericb@aspsys.com>
        Adapted to 2.6.20 by Carsten Emde <ce@osadl.org>
@@ -16,7 +16,6 @@ Author:
 Module Parameters
 -----------------
 
-(specific to LM93)
 * init: integer
   Set to non-zero to force some initializations (default is 0).
 * disable_block: integer
@@ -37,30 +36,13 @@ Module Parameters
   I.e. this parameter controls the VID pin input thresholds; if your VID
   inputs are not working, try changing this.  The default value is "0".
 
-(common among sensor drivers)
-* force: short array (min = 1, max = 48)
-  List of adapter,address pairs to assume to be present.  Autodetection
-  of the target device will still be attempted.  Use one of the more
-  specific force directives below if this doesn't detect the device.
-* force_lm93: short array (min = 1, max = 48)
-  List of adapter,address pairs which are unquestionably assumed to contain
-  a 'lm93' chip
-* ignore: short array (min = 1, max = 48)
-  List of adapter,address pairs not to scan
-* ignore_range: short array (min = 1, max = 48)
-  List of adapter,start-addr,end-addr triples not to scan
-* probe: short array (min = 1, max = 48)
-  List of adapter,address pairs to scan additionally
-* probe_range: short array (min = 1, max = 48)
-  List of adapter,start-addr,end-addr triples to scan additionally
-
 
 Hardware Description
 --------------------
 
 (from the datasheet)
 
-The LM93, hardware monitor, has a two wire digital interface compatible with
+The LM93 hardware monitor has a two wire digital interface compatible with
 SMBus 2.0. Using an 8-bit ADC, the LM93 measures the temperature of two remote
 diode connected transistors as well as its own die and 16 power supply
 voltages. To set fan speed, the LM93 has two PWM outputs that are each
@@ -69,18 +51,12 @@ table based. The LM93 includes a digital filter that can be invoked to smooth
 temperature readings for better control of fan speed. The LM93 has four
 tachometer inputs to measure fan speed. Limit and status registers for all
 measured values are included. The LM93 builds upon the functionality of
-previous motherboard management ASICs and uses some of the LM85 s features
+previous motherboard management ASICs and uses some of the LM85's features
 (i.e. smart tachometer mode). It also adds measurement and control support
 for dynamic Vccp monitoring and PROCHOT. It is designed to monitor a dual
 processor Xeon class motherboard with a minimum of external components.
 
 
-Driver Description
-------------------
-
-This driver implements support for the National Semiconductor LM93.
-
-
 User Interface
 --------------
 
@@ -101,7 +77,7 @@ These intervals can be found in the sysfs files prochot1_interval and
 prochot2_interval.  The values in these files specify the intervals for
 #P1_PROCHOT and #P2_PROCHOT, respectively.  Selecting a value not in this
 list will cause the driver to use the next largest interval.  The available
-intervals are:
+intervals are (in seconds):
 
 #PROCHOT intervals: 0.73, 1.46, 2.9, 5.8, 11.7, 23.3, 46.6, 93.2, 186, 372
 
@@ -111,12 +87,12 @@ assert #P2_PROCHOT, and vice-versa.  This mode is enabled by writing a
 non-zero integer to the sysfs file prochot_short.
 
 The LM93 can also override the #PROCHOT pins by driving a PWM signal onto
-one or both of them.  When overridden, the signal has a period of 3.56 mS,
+one or both of them.  When overridden, the signal has a period of 3.56 ms,
 a minimum pulse width of 5 clocks (at 22.5kHz => 6.25% duty cycle), and
 a maximum pulse width of 80 clocks (at 22.5kHz => 99.88% duty cycle).
 
 The sysfs files prochot1_override and prochot2_override contain boolean
-intgers which enable or disable the override function for #P1_PROCHOT and
+integers which enable or disable the override function for #P1_PROCHOT and
 #P2_PROCHOT, respectively.  The sysfs file prochot_override_duty_cycle
 contains a value controlling the duty cycle for the PWM signal used when
 the override function is enabled.  This value ranges from 0 to 15, with 0
@@ -166,7 +142,7 @@ frequency values are constrained by the hardware.  Selecting a value which is
 not available will cause the driver to use the next largest value.  Also note
 that this parameter has implications for the Smart Tach Mode (see above).
 
-PWM Output Frequencies: 12, 36, 48, 60, 72, 84, 96, 22500 (h/w default)
+PWM Output Frequencies (in Hz): 12, 36, 48, 60, 72, 84, 96, 22500 (default)
 
 Automatic PWM:
 
@@ -178,7 +154,7 @@ individual control sources to which the PWM output is bound.
 The eight control sources are: temp1-temp4 (aka "zones" in the datasheet),
 #PROCHOT 1 & 2, and #VRDHOT 1 & 2.  The bindings are expressed as a bitmask
 in the sysfs files pwm<n>_auto_channels, where a "1" enables the binding, and
- a "0" disables it. The h/w default is 0x0f (all temperatures bound).
+a "0" disables it. The h/w default is 0x0f (all temperatures bound).
 
        0x01 - Temp 1
        0x02 - Temp 2
@@ -324,89 +300,3 @@ LM93 Unique sysfs Files
 
        gpio                    input state of 8 GPIO pins; read-only
 
-
-Sample Configuration File
--------------------------
-
-Here is a sample LM93 chip config for sensors.conf:
-
----------- cut here ----------
-chip "lm93-*"
-
-# VOLTAGE INPUTS
-
-       # labels and scaling based on datasheet recommendations
-       label in1       "+12V1"
-       compute in1     @ * 12.945, @ / 12.945
-       set in1_min     12 * 0.90
-       set in1_max     12 * 1.10
-
-       label in2       "+12V2"
-       compute in2     @ * 12.945, @ / 12.945
-       set in2_min     12 * 0.90
-       set in2_max     12 * 1.10
-
-       label in3       "+12V3"
-       compute in3     @ * 12.945, @ / 12.945
-       set in3_min     12 * 0.90
-       set in3_max     12 * 1.10
-
-       label in4       "FSB_Vtt"
-
-       label in5       "3GIO"
-
-       label in6       "ICH_Core"
-
-       label in7       "Vccp1"
-
-       label in8       "Vccp2"
-
-       label in9       "+3.3V"
-       set in9_min     3.3 * 0.90
-       set in9_max     3.3 * 1.10
-
-       label in10      "+5V"
-       set in10_min    5.0 * 0.90
-       set in10_max    5.0 * 1.10
-
-       label in11      "SCSI_Core"
-
-       label in12      "Mem_Core"
-
-       label in13      "Mem_Vtt"
-
-       label in14      "Gbit_Core"
-
-       # Assuming R1/R2 = 4.1143, and 3.3V reference
-       # -12V = (4.1143 + 1) * (@ - 3.3) + 3.3
-       label in15      "-12V"
-       compute in15 @ * 5.1143 - 13.57719, (@ + 13.57719) / 5.1143
-       set in15_min    -12 * 0.90
-       set in15_max    -12 * 1.10
-
-       label in16      "+3.3VSB"
-       set in16_min    3.3 * 0.90
-       set in16_max    3.3 * 1.10
-
-# TEMPERATURE INPUTS
-
-       label temp1     "CPU1"
-       label temp2     "CPU2"
-       label temp3     "LM93"
-
-# TACHOMETER INPUTS
-
-       label fan1      "Fan1"
-       set fan1_min    3000
-       label fan2      "Fan2"
-       set fan2_min    3000
-       label fan3      "Fan3"
-       set fan3_min    3000
-       label fan4      "Fan4"
-       set fan4_min    3000
-
-# PWM OUTPUTS
-
-       label pwm1      "CPU1"
-       label pwm2      "CPU2"
-
index b3a9e1b..a17b692 100644 (file)
@@ -67,6 +67,10 @@ between readings to be caught and alarmed. The exact definition of an
 alarm (for example, whether a threshold must be met or must be exceeded
 to cause an alarm) is chip-dependent.
 
+When setting values of hwmon sysfs attributes, the string representation of
+the desired value must be written, note that strings which are not a number
+are interpreted as 0! For more on how written strings are interpreted see the
+"sysfs attribute writes interpretation" section at the end of this file.
 
 -------------------------------------------------------------------------
 
@@ -78,8 +82,21 @@ RW   read/write value
 Read/write values may be read-only for some chips, depending on the
 hardware implementation.
 
-All entries are optional, and should only be created in a given driver
-if the chip has the feature.
+All entries (except name) are optional, and should only be created in a
+given driver if the chip has the feature.
+
+
+********
+* Name *
+********
+
+name           The chip name.
+               This should be a short, lowercase string, not containing
+               spaces nor dashes, representing the chip name. This is
+               the only mandatory attribute.
+               I2C devices get this attribute created automatically.
+               RO
+
 
 ************
 * Voltages *
@@ -104,18 +121,17 @@ in[0-*]_input     Voltage input value.
                by the chip driver, and must be done by the application.
                However, some drivers (notably lm87 and via686a)
                do scale, because of internal resistors built into a chip.
-               These drivers will output the actual voltage.
-
-               Typical usage:
-                       in0_*   CPU #1 voltage (not scaled)
-                       in1_*   CPU #2 voltage (not scaled)
-                       in2_*   3.3V nominal (not scaled)
-                       in3_*   5.0V nominal (scaled)
-                       in4_*   12.0V nominal (scaled)
-                       in5_*   -12.0V nominal (scaled)
-                       in6_*   -5.0V nominal (scaled)
-                       in7_*   varies
-                       in8_*   varies
+               These drivers will output the actual voltage. Rule of
+               thumb: drivers should report the voltage values at the
+               "pins" of the chip.
+
+in[0-*]_label  Suggested voltage channel label.
+               Text string
+               Should only be created if the driver has hints about what
+               this voltage channel is being used for, and user-space
+               doesn't. In all other cases, the label is provided by
+               user-space.
+               RO
 
 cpu[0-*]_vid   CPU core reference voltage.
                Unit: millivolt
@@ -159,6 +175,13 @@ fan[1-*]_target
                Only makes sense if the chip supports closed-loop fan speed
                control based on the measured fan speed.
 
+fan[1-*]_label Suggested fan channel label.
+               Text string
+               Should only be created if the driver has hints about what
+               this fan channel is being used for, and user-space doesn't.
+               In all other cases, the label is provided by user-space.
+               RO
+
 Also see the Alarms section for status flags associated with fans.
 
 
@@ -219,12 +242,12 @@ temp[1-*]_auto_point[1-*]_temp_hyst
 ****************
 
 temp[1-*]_type Sensor type selection.
-               Integers 1 to 6 or thermistor Beta value (typically 3435)
+               Integers 1 to 6
                RW
                1: PII/Celeron Diode
                2: 3904 transistor
                3: thermal diode
-               4: thermistor (default/unknown Beta)
+               4: thermistor
                5: AMD AMDSI
                6: Intel PECI
                Not all types are supported by all chips
@@ -260,18 +283,19 @@ temp[1-*]_crit_hyst
                from the critical value.
                RW
 
-temp[1-4]_offset
+temp[1-*]_offset
                Temperature offset which is added to the temperature reading
                by the chip.
                Unit: millidegree Celsius
                Read/Write value.
 
-               If there are multiple temperature sensors, temp1_* is
-               generally the sensor inside the chip itself,
-               reported as "motherboard temperature".  temp2_* to
-               temp4_* are generally sensors external to the chip
-               itself, for example the thermal diode inside the CPU or
-               a thermistor nearby.
+temp[1-*]_label        Suggested temperature channel label.
+               Text string
+               Should only be created if the driver has hints about what
+               this temperature channel is being used for, and user-space
+               doesn't. In all other cases, the label is provided by
+               user-space.
+               RO
 
 Some chips measure temperature using external thermistors and an ADC, and
 report the temperature measurement as a voltage. Converting this voltage
@@ -393,14 +417,53 @@ beep_mask Bitmask for beep.
                RW
 
 
-*********
-* Other *
-*********
-
-eeprom         Raw EEPROM data in binary form.
-               RO
-
-pec            Enable or disable PEC (SMBus only)
-               0: disable
-               1: enable
-               RW
+sysfs attribute writes interpretation
+-------------------------------------
+
+hwmon sysfs attributes always contain numbers, so the first thing to do is to
+convert the input to a number, there are 2 ways todo this depending whether
+the number can be negative or not:
+unsigned long u = simple_strtoul(buf, NULL, 10);
+long s = simple_strtol(buf, NULL, 10);
+
+With buf being the buffer with the user input being passed by the kernel.
+Notice that we do not use the second argument of strto[u]l, and thus cannot
+tell when 0 is returned, if this was really 0 or is caused by invalid input.
+This is done deliberately as checking this everywhere would add a lot of
+code to the kernel.
+
+Notice that it is important to always store the converted value in an
+unsigned long or long, so that no wrap around can happen before any further
+checking.
+
+After the input string is converted to an (unsigned) long, the value should be
+checked if its acceptable. Be careful with further conversions on the value
+before checking it for validity, as these conversions could still cause a wrap
+around before the check. For example do not multiply the result, and only
+add/subtract if it has been divided before the add/subtract.
+
+What to do if a value is found to be invalid, depends on the type of the
+sysfs attribute that is being set. If it is a continuous setting like a
+tempX_max or inX_max attribute, then the value should be clamped to its
+limits using SENSORS_LIMIT(value, min_limit, max_limit). If it is not
+continuous like for example a tempX_type, then when an invalid value is
+written, -EINVAL should be returned.
+
+Example1, temp1_max, register is a signed 8 bit value (-128 - 127 degrees):
+
+       long v = simple_strtol(buf, NULL, 10) / 1000;
+       v = SENSORS_LIMIT(v, -128, 127);
+       /* write v to register */
+
+Example2, fan divider setting, valid values 2, 4 and 8:
+
+       unsigned long v = simple_strtoul(buf, NULL, 10);
+
+       switch (v) {
+       case 2: v = 1; break;
+       case 4: v = 2; break;
+       case 8: v = 3; break;
+       default:
+               return -EINVAL;
+       }
+       /* write v to register */
index db9881d..f153b2f 100644 (file)
@@ -75,46 +75,64 @@ 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 bit ordering for the alarm "realtime status register" and the
-"beep enable registers" are different.
-
-in0 (VCORE)  :  alarms: 0x000001 beep_enable: 0x000001
-in1 (VINR0)  :  alarms: 0x000002 beep_enable: 0x002000 <== mismatch
-in2 (+3.3VIN):  alarms: 0x000004 beep_enable: 0x000004
-in3 (5VDD)   :  alarms: 0x000008 beep_enable: 0x000008
-in4 (+12VIN) :  alarms: 0x000100 beep_enable: 0x000100
-in5 (-12VIN) :  alarms: 0x000200 beep_enable: 0x000200
-in6 (-5VIN)  :  alarms: 0x000400 beep_enable: 0x000400
-in7 (VSB)    :  alarms: 0x080000 beep_enable: 0x010000 <== mismatch
-in8 (VBAT)   :  alarms: 0x100000 beep_enable: 0x020000 <== mismatch
-in9 (VINR1)  :  alarms: 0x004000 beep_enable: 0x004000
-temp1        :  alarms: 0x000010 beep_enable: 0x000010
-temp2        :  alarms: 0x000020 beep_enable: 0x000020
-temp3        :  alarms: 0x002000 beep_enable: 0x000002 <== mismatch
-fan1         :  alarms: 0x000040 beep_enable: 0x000040
-fan2         :  alarms: 0x000080 beep_enable: 0x000080
-fan3         :  alarms: 0x000800 beep_enable: 0x000800
-fan4         :  alarms: 0x200000 beep_enable: 0x200000
-fan5         :  alarms: 0x400000 beep_enable: 0x400000
-tart1        :  alarms: 0x010000 beep_enable: 0x040000 <== mismatch
-tart2        :  alarms: 0x020000 beep_enable: 0x080000 <== mismatch
-tart3        :  alarms: 0x040000 beep_enable: 0x100000 <== mismatch
-case_open    :  alarms: 0x001000 beep_enable: 0x001000
-user_enable  :  alarms: -------- beep_enable: 0x800000
-
-*** NOTE: It is the responsibility of user-space code to handle the fact
-that the beep enable and alarm bits are in different positions when using that
-feature of the chip.
-
-When an alarm goes off, you can be warned by a beeping signal through your
-computer speaker. It is possible to enable all beeping globally, or only
-the beeping for some alarms.
-
-The driver only reads the chip values each 3 seconds; reading them more
-often will do no harm, but will return 'old' values.
+The w83791d has a global bit used to enable beeping from the speaker when an
+alarm is triggered as well as a bitmask to enable or disable the beep for
+specific alarms. You need both the global beep enable bit and the
+corresponding beep bit to be on for a triggered alarm to sound a beep.
+
+The sysfs interface to the gloabal enable is via the sysfs beep_enable file.
+This file is used for both legacy and new code.
+
+The sysfs interface to the beep bitmask has migrated from the original legacy
+method of a single sysfs beep_mask file to a newer method using multiple
+*_beep files as described in .../Documentation/hwmon/sysfs-interface.
+
+A similar change has occured for the bitmap corresponding to the alarms. The
+original legacy method used a single sysfs alarms file containing a bitmap
+of triggered alarms. The newer method uses multiple sysfs *_alarm files
+(again following the pattern described in sysfs-interface).
+
+Since both methods read and write the underlying hardware, they can be used
+interchangeably and changes in one will automatically be reflected by
+the other. If you use the legacy bitmask method, your user-space code is
+responsible for handling the fact that the alarms and beep_mask bitmaps
+are not the same (see the table below).
+
+NOTE: All new code should be written to use the newer sysfs-interface
+specification as that avoids bitmap problems and is the preferred interface
+going forward.
+
+The driver reads the hardware chip values at most once every three seconds.
+User mode code requesting values more often will receive cached values.
+
+Alarms bitmap vs. beep_mask bitmask
+------------------------------------
+For legacy code using the alarms and beep_mask files:
+
+in0 (VCORE)  :  alarms: 0x000001 beep_mask: 0x000001
+in1 (VINR0)  :  alarms: 0x000002 beep_mask: 0x002000 <== mismatch
+in2 (+3.3VIN):  alarms: 0x000004 beep_mask: 0x000004
+in3 (5VDD)   :  alarms: 0x000008 beep_mask: 0x000008
+in4 (+12VIN) :  alarms: 0x000100 beep_mask: 0x000100
+in5 (-12VIN) :  alarms: 0x000200 beep_mask: 0x000200
+in6 (-5VIN)  :  alarms: 0x000400 beep_mask: 0x000400
+in7 (VSB)    :  alarms: 0x080000 beep_mask: 0x010000 <== mismatch
+in8 (VBAT)   :  alarms: 0x100000 beep_mask: 0x020000 <== mismatch
+in9 (VINR1)  :  alarms: 0x004000 beep_mask: 0x004000
+temp1        :  alarms: 0x000010 beep_mask: 0x000010
+temp2        :  alarms: 0x000020 beep_mask: 0x000020
+temp3        :  alarms: 0x002000 beep_mask: 0x000002 <== mismatch
+fan1         :  alarms: 0x000040 beep_mask: 0x000040
+fan2         :  alarms: 0x000080 beep_mask: 0x000080
+fan3         :  alarms: 0x000800 beep_mask: 0x000800
+fan4         :  alarms: 0x200000 beep_mask: 0x200000
+fan5         :  alarms: 0x400000 beep_mask: 0x400000
+tart1        :  alarms: 0x010000 beep_mask: 0x040000 <== mismatch
+tart2        :  alarms: 0x020000 beep_mask: 0x080000 <== mismatch
+tart3        :  alarms: 0x040000 beep_mask: 0x100000 <== mismatch
+case_open    :  alarms: 0x001000 beep_mask: 0x001000
+global_enable:  alarms: -------- beep_mask: 0x800000 (modified via beep_enable)
 
 W83791D TODO:
 ---------------
-Provide a patch for per-file alarms and beep enables as defined in the hwmon
-       documentation (Documentation/hwmon/sysfs-interface)
 Provide a patch for smart-fan control (still need appropriate motherboard/fans)
index 0fdb8a5..012fa83 100644 (file)
@@ -1660,7 +1660,8 @@ P:        Mark M. Hoffman
 M:     mhoffman@lightlink.com
 L:     lm-sensors@lm-sensors.org
 W:     http://www.lm-sensors.org/
-T:     git lm-sensors.org:/kernel/mhoffman/hwmon-2.6.git
+T:     git lm-sensors.org:/kernel/mhoffman/hwmon-2.6.git testing
+T:     git lm-sensors.org:/kernel/mhoffman/hwmon-2.6.git release
 S:     Maintained
 
 HARDWARE RANDOM NUMBER GENERATOR CORE
@@ -4177,7 +4178,7 @@ W83791D HARDWARE MONITORING DRIVER
 P:     Charles Spirakis
 M:     bezaur@gmail.com
 L:     lm-sensors@lm-sensors.org
-S:     Maintained
+S:     Odd Fixes
 
 W83793 HARDWARE MONITORING DRIVER
 P:     Rudolf Marek
index 192953b..e47f881 100644 (file)
@@ -30,7 +30,7 @@ config HWMON_VID
 
 config SENSORS_ABITUGURU
        tristate "Abit uGuru (rev 1 & 2)"
-       depends on EXPERIMENTAL
+       depends on X86 && EXPERIMENTAL
        help
          If you say yes here you get support for the sensor part of the first
          and second revision of the Abit uGuru chip. The voltage and frequency
@@ -45,7 +45,7 @@ config SENSORS_ABITUGURU
 
 config SENSORS_ABITUGURU3
        tristate "Abit uGuru (rev 3)"
-       depends on HWMON && EXPERIMENTAL
+       depends on X86 && EXPERIMENTAL
        help
          If you say yes here you get support for the sensor part of the
          third revision of the Abit uGuru chip. Only reading the sensors
@@ -133,6 +133,16 @@ config SENSORS_ADM9240
          This driver can also be built as a module.  If so, the module
          will be called adm9240.
 
+config SENSORS_ADT7470
+       tristate "Analog Devices ADT7470"
+       depends on I2C && EXPERIMENTAL
+       help
+         If you say yes here you get support for the Analog Devices
+         ADT7470 temperature monitoring chips.
+
+         This driver can also be built as a module. If so, the module
+         will be called adt7470.
+
 config SENSORS_K8TEMP
        tristate "AMD Athlon64/FX or Opteron temperature sensor"
        depends on X86 && PCI && EXPERIMENTAL
@@ -172,7 +182,7 @@ config SENSORS_AMS_I2C
 
 config SENSORS_ASB100
        tristate "Asus ASB100 Bach"
-       depends on I2C && EXPERIMENTAL
+       depends on X86 && I2C && EXPERIMENTAL
        select HWMON_VID
        help
          If you say yes here you get support for the ASB100 Bach sensor
@@ -206,19 +216,39 @@ config SENSORS_DS1621
          will be called ds1621.
 
 config SENSORS_F71805F
-       tristate "Fintek F71805F/FG and F71872F/FG"
+       tristate "Fintek F71805F/FG, F71806F/FG and F71872F/FG"
        depends on EXPERIMENTAL
        help
          If you say yes here you get support for hardware monitoring
-         features of the Fintek F71805F/FG and F71872F/FG Super-I/O
-         chips.
+         features of the Fintek F71805F/FG, F71806F/FG and F71872F/FG
+         Super-I/O chips.
 
          This driver can also be built as a module.  If so, the module
          will be called f71805f.
 
+config SENSORS_F71882FG
+       tristate "Fintek F71882FG and F71883FG"
+       depends on EXPERIMENTAL
+       help
+         If you say yes here you get support for hardware monitoring
+         features of the Fintek F71882FG and F71883FG Super-I/O chips.
+
+         This driver can also be built as a module.  If so, the module
+         will be called f71882fg.
+
+config SENSORS_F75375S
+       tristate "Fintek F75375S/SP and F75373";
+       depends on I2C && EXPERIMENTAL
+       help
+         If you say yes here you get support for hardware monitoring
+         features of the Fintek F75375S/SP and F75373
+
+         This driver can also be built as a module.  If so, the module
+         will be called f75375s.
+
 config SENSORS_FSCHER
        tristate "FSC Hermes"
-       depends on I2C
+       depends on X86 && I2C
        help
          If you say yes here you get support for Fujitsu Siemens
          Computers Hermes sensor chips.
@@ -228,7 +258,7 @@ config SENSORS_FSCHER
 
 config SENSORS_FSCPOS
        tristate "FSC Poseidon"
-       depends on I2C
+       depends on X86 && I2C
        help
          If you say yes here you get support for Fujitsu Siemens
          Computers Poseidon sensor chips.
@@ -236,6 +266,20 @@ config SENSORS_FSCPOS
          This driver can also be built as a module.  If so, the module
          will be called fscpos.
 
+config SENSORS_FSCHMD
+       tristate "FSC Poseidon, Scylla, Hermes, Heimdall and Heracles"
+       depends on X86 && I2C && EXPERIMENTAL
+       help
+         If you say yes here you get support for various Fujitsu Siemens
+         Computers sensor chips.
+
+         This is a new merged driver for FSC sensor chips which is intended
+         as a replacment for the fscpos, fscscy and fscher drivers and adds
+         support for several other FCS sensor chips.
+
+         This driver can also be built as a module.  If so, the module
+         will be called fschmd.
+
 config SENSORS_GL518SM
        tristate "Genesys Logic GL518SM"
        depends on I2C
@@ -265,6 +309,19 @@ config SENSORS_CORETEMP
          sensor inside your CPU. Supported all are all known variants
          of Intel Core family.
 
+config SENSORS_IBMPEX
+       tristate "IBM PowerExecutive temperature/power sensors"
+       select IPMI_SI
+       depends on IPMI_HANDLER
+       help
+         If you say yes here you get support for the temperature and
+         power sensors in various IBM System X servers that support
+         PowerExecutive.  So far this includes the x3550, x3650, x3655,
+         x3755, and certain HS20 blades.
+
+         This driver can also be built as a module.  If so, the module
+         will be called ibmpex.
+
 config SENSORS_IT87
        tristate "ITE IT87xx and compatibles"
        select HWMON_VID
@@ -401,7 +458,7 @@ config SENSORS_LM92
 
 config SENSORS_LM93
        tristate "National Semiconductor LM93 and compatibles"
-       depends on HWMON && I2C
+       depends on I2C
        select HWMON_VID
        help
          If you say yes here you get support for National Semiconductor LM93
@@ -466,13 +523,13 @@ config SENSORS_SIS5595
          will be called sis5595.
 
 config SENSORS_DME1737
-       tristate "SMSC DME1737 and compatibles"
+       tristate "SMSC DME1737, SCH311x and compatibles"
        depends on I2C && EXPERIMENTAL
        select HWMON_VID
        help
          If you say yes here you get support for the hardware monitoring
          and fan control features of the SMSC DME1737 (and compatibles
-         like the Asus A8000) Super-I/O chip.
+         like the Asus A8000) and SCH311x Super-I/O chips.
 
          This driver can also be built as a module.  If so, the module
          will be called dme1737.
index d04f900..6da3eef 100644 (file)
@@ -22,6 +22,7 @@ obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o
 obj-$(CONFIG_SENSORS_ADM1029)  += adm1029.o
 obj-$(CONFIG_SENSORS_ADM1031)  += adm1031.o
 obj-$(CONFIG_SENSORS_ADM9240)  += adm9240.o
+obj-$(CONFIG_SENSORS_ADT7470)  += adt7470.o
 obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
 obj-$(CONFIG_SENSORS_AMS)      += ams/
 obj-$(CONFIG_SENSORS_ATXP1)    += atxp1.o
@@ -29,11 +30,15 @@ obj-$(CONFIG_SENSORS_CORETEMP)      += coretemp.o
 obj-$(CONFIG_SENSORS_DME1737)  += dme1737.o
 obj-$(CONFIG_SENSORS_DS1621)   += ds1621.o
 obj-$(CONFIG_SENSORS_F71805F)  += f71805f.o
+obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o
+obj-$(CONFIG_SENSORS_F75375S)  += f75375s.o
 obj-$(CONFIG_SENSORS_FSCHER)   += fscher.o
+obj-$(CONFIG_SENSORS_FSCHMD)   += fschmd.o
 obj-$(CONFIG_SENSORS_FSCPOS)   += fscpos.o
 obj-$(CONFIG_SENSORS_GL518SM)  += gl518sm.o
 obj-$(CONFIG_SENSORS_GL520SM)  += gl520sm.o
 obj-$(CONFIG_SENSORS_HDAPS)    += hdaps.o
+obj-$(CONFIG_SENSORS_IBMPEX)   += ibmpex.o
 obj-$(CONFIG_SENSORS_IT87)     += it87.o
 obj-$(CONFIG_SENSORS_K8TEMP)   += k8temp.o
 obj-$(CONFIG_SENSORS_LM63)     += lm63.o
index 2317f4b..4dbdb81 100644 (file)
@@ -176,7 +176,7 @@ MODULE_PARM_DESC(verbose, "How verbose should the driver be? (0-3):\n"
    The structure is dynamically allocated, at the same time when a new
    abituguru device is allocated. */
 struct abituguru_data {
-       struct class_device *class_dev; /* hwmon registered device */
+       struct device *hwmon_dev;       /* hwmon registered device */
        struct mutex update_lock;       /* protect access to data and uGuru */
        unsigned long last_updated;     /* In jiffies */
        unsigned short addr;            /* uguru base address */
@@ -1287,11 +1287,11 @@ static int __devinit abituguru_probe(struct platform_device *pdev)
                                &abituguru_sysfs_attr[i].dev_attr))
                        goto abituguru_probe_error;
 
-       data->class_dev = hwmon_device_register(&pdev->dev);
-       if (!IS_ERR(data->class_dev))
+       data->hwmon_dev = hwmon_device_register(&pdev->dev);
+       if (!IS_ERR(data->hwmon_dev))
                return 0; /* success */
 
-       res = PTR_ERR(data->class_dev);
+       res = PTR_ERR(data->hwmon_dev);
 abituguru_probe_error:
        for (i = 0; data->sysfs_attr[i].dev_attr.attr.name; i++)
                device_remove_file(&pdev->dev, &data->sysfs_attr[i].dev_attr);
@@ -1308,7 +1308,7 @@ static int __devexit abituguru_remove(struct platform_device *pdev)
        int i;
        struct abituguru_data *data = platform_get_drvdata(pdev);
 
-       hwmon_device_unregister(data->class_dev);
+       hwmon_device_unregister(data->hwmon_dev);
        for (i = 0; data->sysfs_attr[i].dev_attr.attr.name; i++)
                device_remove_file(&pdev->dev, &data->sysfs_attr[i].dev_attr);
        for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++)
index cdd8b6d..cb2331b 100644 (file)
@@ -124,7 +124,7 @@ struct abituguru3_motherboard_info {
    The structure is dynamically allocated, at the same time when a new
    abituguru3 device is allocated. */
 struct abituguru3_data {
-       struct class_device *class_dev; /* hwmon registered device */
+       struct device *hwmon_dev;       /* hwmon registered device */
        struct mutex update_lock;       /* protect access to data and uGuru */
        unsigned short addr;            /* uguru base address */
        char valid;                     /* !=0 if following fields are valid */
@@ -933,9 +933,9 @@ static int __devinit abituguru3_probe(struct platform_device *pdev)
                                &abituguru3_sysfs_attr[i].dev_attr))
                        goto abituguru3_probe_error;
 
-       data->class_dev = hwmon_device_register(&pdev->dev);
-       if (IS_ERR(data->class_dev)) {
-               res = PTR_ERR(data->class_dev);
+       data->hwmon_dev = hwmon_device_register(&pdev->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               res = PTR_ERR(data->hwmon_dev);
                goto abituguru3_probe_error;
        }
 
@@ -957,7 +957,7 @@ static int __devexit abituguru3_remove(struct platform_device *pdev)
        struct abituguru3_data *data = platform_get_drvdata(pdev);
 
        platform_set_drvdata(pdev, NULL);
-       hwmon_device_unregister(data->class_dev);
+       hwmon_device_unregister(data->hwmon_dev);
        for (i = 0; data->sysfs_attr[i].dev_attr.attr.name; i++)
                device_remove_file(&pdev->dev, &data->sysfs_attr[i].dev_attr);
        for (i = 0; i < ARRAY_SIZE(abituguru3_sysfs_attr); i++)
index cc8b624..fcd7fe7 100644 (file)
@@ -47,7 +47,7 @@ static const u8 AD7418_REG_TEMP[] = { AD7418_REG_TEMP_IN,
 
 struct ad7418_data {
        struct i2c_client       client;
-       struct class_device     *class_dev;
+       struct device           *hwmon_dev;
        struct attribute_group  attrs;
        enum chips              type;
        struct mutex            lock;
@@ -172,7 +172,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,
        struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
        struct i2c_client *client = to_i2c_client(dev);
        struct ad7418_data *data = i2c_get_clientdata(client);
-       int temp = simple_strtol(buf, NULL, 10);
+       long temp = simple_strtol(buf, NULL, 10);
 
        mutex_lock(&data->lock);
        data->temp[attr->index] = LM75_TEMP_TO_REG(temp);
@@ -326,9 +326,9 @@ static int ad7418_detect(struct i2c_adapter *adapter, int address, int kind)
        if ((err = sysfs_create_group(&client->dev.kobj, &data->attrs)))
                goto exit_detach;
 
-       data->class_dev = hwmon_device_register(&client->dev);
-       if (IS_ERR(data->class_dev)) {
-               err = PTR_ERR(data->class_dev);
+       data->hwmon_dev = hwmon_device_register(&client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
                goto exit_remove;
        }
 
@@ -347,7 +347,7 @@ exit:
 static int ad7418_detach_client(struct i2c_client *client)
 {
        struct ad7418_data *data = i2c_get_clientdata(client);
-       hwmon_device_unregister(data->class_dev);
+       hwmon_device_unregister(data->hwmon_dev);
        sysfs_remove_group(&client->dev.kobj, &data->attrs);
        i2c_detach_client(client);
        kfree(data);
index c466329..ebdc6d7 100644 (file)
@@ -1,6 +1,6 @@
 /*
     adm1021.c - Part of lm_sensors, Linux kernel modules for hardware
-             monitoring
+               monitoring
     Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl> and
     Philip Edelbrock <phil@netroedge.com>
 
@@ -25,6 +25,7 @@
 #include <linux/jiffies.h>
 #include <linux/i2c.h>
 #include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
 #include <linux/err.h>
 #include <linux/mutex.h>
 
 /* Addresses to scan */
 static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a,
                                        0x29, 0x2a, 0x2b,
-                                       0x4c, 0x4d, 0x4e, 
+                                       0x4c, 0x4d, 0x4e,
                                        I2C_CLIENT_END };
 
 /* Insmod parameters */
-I2C_CLIENT_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1066);
+I2C_CLIENT_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm,
+                       mc1066);
 
 /* adm1021 constants specified below */
 
 /* The adm1021 registers */
 /* Read-only */
-#define ADM1021_REG_TEMP               0x00
-#define ADM1021_REG_REMOTE_TEMP                0x01
+/* For nr in 0-1 */
+#define ADM1021_REG_TEMP(nr)           (nr)
 #define ADM1021_REG_STATUS             0x02
-#define ADM1021_REG_MAN_ID             0x0FE   /* 0x41 = AMD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys , 0x54 = Onsemi*/
-#define ADM1021_REG_DEV_ID             0x0FF   /* ADM1021 = 0x0X, ADM1023 = 0x3X */
-#define ADM1021_REG_DIE_CODE           0x0FF   /* MAX1617A */
+/* 0x41 = AD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys , 0x54 = Onsemi */
+#define ADM1021_REG_MAN_ID             0xFE
+/* ADM1021 = 0x0X, ADM1023 = 0x3X */
+#define ADM1021_REG_DEV_ID             0xFF
 /* These use different addresses for reading/writing */
 #define ADM1021_REG_CONFIG_R           0x03
 #define ADM1021_REG_CONFIG_W           0x09
 #define ADM1021_REG_CONV_RATE_R                0x04
 #define ADM1021_REG_CONV_RATE_W                0x0A
 /* These are for the ADM1023's additional precision on the remote temp sensor */
-#define ADM1021_REG_REM_TEMP_PREC      0x010
-#define ADM1021_REG_REM_OFFSET         0x011
-#define ADM1021_REG_REM_OFFSET_PREC    0x012
-#define ADM1021_REG_REM_TOS_PREC       0x013
-#define ADM1021_REG_REM_THYST_PREC     0x014
+#define ADM1023_REG_REM_TEMP_PREC      0x10
+#define ADM1023_REG_REM_OFFSET         0x11
+#define ADM1023_REG_REM_OFFSET_PREC    0x12
+#define ADM1023_REG_REM_TOS_PREC       0x13
+#define ADM1023_REG_REM_THYST_PREC     0x14
 /* limits */
-#define ADM1021_REG_TOS_R              0x05
-#define ADM1021_REG_TOS_W              0x0B
-#define ADM1021_REG_REMOTE_TOS_R       0x07
-#define ADM1021_REG_REMOTE_TOS_W       0x0D
-#define ADM1021_REG_THYST_R            0x06
-#define ADM1021_REG_THYST_W            0x0C
-#define ADM1021_REG_REMOTE_THYST_R     0x08
-#define ADM1021_REG_REMOTE_THYST_W     0x0E
+/* For nr in 0-1 */
+#define ADM1021_REG_TOS_R(nr)          (0x05 + 2 * (nr))
+#define ADM1021_REG_TOS_W(nr)          (0x0B + 2 * (nr))
+#define ADM1021_REG_THYST_R(nr)                (0x06 + 2 * (nr))
+#define ADM1021_REG_THYST_W(nr)                (0x0C + 2 * (nr))
 /* write-only */
 #define ADM1021_REG_ONESHOT            0x0F
 
-
-/* Conversions. Rounding and limit checking is only done on the TO_REG
-   variants. Note that you should be a bit careful with which arguments
-   these macros are called: arguments may be evaluated more than once.
-   Fixing this is just not worth it. */
-/* Conversions  note: 1021 uses normal integer signed-byte format*/
-#define TEMP_FROM_REG(val)     (val > 127 ? (val-256)*1000 : val*1000)
-#define TEMP_TO_REG(val)       (SENSORS_LIMIT((val < 0 ? (val/1000)+256 : val/1000),0,255))
-
 /* Initial values */
 
-/* Note: Even though I left the low and high limits named os and hyst, 
-they don't quite work like a thermostat the way the LM75 does.  I.e., 
-a lower temp than THYST actually triggers an alarm instead of 
+/* Note: Even though I left the low and high limits named os and hyst,
+they don't quite work like a thermostat the way the LM75 does.  I.e.,
+a lower temp than THYST actually triggers an alarm instead of
 clearing it.  Weird, ey?   --Phil  */
 
 /* Each client has this additional data */
 struct adm1021_data {
        struct i2c_client client;
-       struct class_device *class_dev;
+       struct device *hwmon_dev;
        enum chips type;
 
        struct mutex update_lock;
        char valid;             /* !=0 if following fields are valid */
        unsigned long last_updated;     /* In jiffies */
 
-       u8      temp_max;       /* Register values */
-       u8      temp_hyst;
-       u8      temp_input;
-       u8      remote_temp_max;
-       u8      remote_temp_hyst;
-       u8      remote_temp_input;
-       u8      alarms;
-        /* Special values for ADM1023 only */
-       u8      remote_temp_prec;
-       u8      remote_temp_os_prec;
-       u8      remote_temp_hyst_prec;
-       u8      remote_temp_offset;
-       u8      remote_temp_offset_prec;
+       s8 temp_max[2];         /* Register values */
+       s8 temp_min[2];
+       s8 temp[2];
+       u8 alarms;
+       /* Special values for ADM1023 only */
+       u8 remote_temp_prec;
+       u8 remote_temp_os_prec;
+       u8 remote_temp_hyst_prec;
+       u8 remote_temp_offset;
+       u8 remote_temp_offset_prec;
 };
 
 static int adm1021_attach_adapter(struct i2c_adapter *adapter);
 static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind);
 static void adm1021_init_client(struct i2c_client *client);
 static int adm1021_detach_client(struct i2c_client *client);
-static int adm1021_read_value(struct i2c_client *client, u8 reg);
-static int adm1021_write_value(struct i2c_client *client, u8 reg,
-                              u16 value);
 static struct adm1021_data *adm1021_update_device(struct device *dev);
 
 /* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */
@@ -135,53 +120,104 @@ static struct i2c_driver adm1021_driver = {
        .detach_client  = adm1021_detach_client,
 };
 
-#define show(value)    \
-static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf)              \
-{                                                                      \
-       struct adm1021_data *data = adm1021_update_device(dev);         \
-       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value));        \
+static ssize_t show_temp(struct device *dev,
+                        struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct adm1021_data *data = adm1021_update_device(dev);
+
+       return sprintf(buf, "%d\n", 1000 * data->temp[index]);
 }
-show(temp_max);
-show(temp_hyst);
-show(temp_input);
-show(remote_temp_max);
-show(remote_temp_hyst);
-show(remote_temp_input);
-
-#define show2(value)   \
-static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf)              \
-{                                                                      \
-       struct adm1021_data *data = adm1021_update_device(dev);         \
-       return sprintf(buf, "%d\n", data->value);                       \
+
+static ssize_t show_temp_max(struct device *dev,
+                            struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct adm1021_data *data = adm1021_update_device(dev);
+
+       return sprintf(buf, "%d\n", 1000 * data->temp_max[index]);
 }
-show2(alarms);
-
-#define set(value, reg)        \
-static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)   \
-{                                                              \
-       struct i2c_client *client = to_i2c_client(dev);         \
-       struct adm1021_data *data = i2c_get_clientdata(client); \
-       int temp = simple_strtoul(buf, NULL, 10);               \
-                                                               \
-       mutex_lock(&data->update_lock);                         \
-       data->value = TEMP_TO_REG(temp);                        \
-       adm1021_write_value(client, reg, data->value);          \
-       mutex_unlock(&data->update_lock);                       \
-       return count;                                           \
+
+static ssize_t show_temp_min(struct device *dev,
+                            struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct adm1021_data *data = adm1021_update_device(dev);
+
+       return sprintf(buf, "%d\n", 1000 * data->temp_min[index]);
+}
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       int index = to_sensor_dev_attr(attr)->index;
+       struct adm1021_data *data = adm1021_update_device(dev);
+       return sprintf(buf, "%u\n", (data->alarms >> index) & 1);
+}
+
+static ssize_t show_alarms(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       struct adm1021_data *data = adm1021_update_device(dev);
+       return sprintf(buf, "%u\n", data->alarms);
 }
-set(temp_max, ADM1021_REG_TOS_W);
-set(temp_hyst, ADM1021_REG_THYST_W);
-set(remote_temp_max, ADM1021_REG_REMOTE_TOS_W);
-set(remote_temp_hyst, ADM1021_REG_REMOTE_THYST_W);
-
-static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max);
-static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst);
-static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL);
-static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_remote_temp_max, set_remote_temp_max);
-static DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_remote_temp_hyst, set_remote_temp_hyst);
-static DEVICE_ATTR(temp2_input, S_IRUGO, show_remote_temp_input, NULL);
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
 
+static ssize_t set_temp_max(struct device *dev,
+                           struct device_attribute *devattr,
+                           const char *buf, size_t count)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1021_data *data = i2c_get_clientdata(client);
+       long temp = simple_strtol(buf, NULL, 10) / 1000;
+
+       mutex_lock(&data->update_lock);
+       data->temp_max[index] = SENSORS_LIMIT(temp, -128, 127);
+       if (!read_only)
+               i2c_smbus_write_byte_data(client, ADM1021_REG_TOS_W(index),
+                                         data->temp_max[index]);
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t set_temp_min(struct device *dev,
+                           struct device_attribute *devattr,
+                           const char *buf, size_t count)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1021_data *data = i2c_get_clientdata(client);
+       long temp = simple_strtol(buf, NULL, 10) / 1000;
+
+       mutex_lock(&data->update_lock);
+       data->temp_min[index] = SENSORS_LIMIT(temp, -128, 127);
+       if (!read_only)
+               i2c_smbus_write_byte_data(client, ADM1021_REG_THYST_W(index),
+                                         data->temp_min[index]);
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
+                         set_temp_max, 0);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_min,
+                         set_temp_min, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_max,
+                         set_temp_max, 1);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_min,
+                         set_temp_min, 1);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 2);
+
+static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
 
 static int adm1021_attach_adapter(struct i2c_adapter *adapter)
 {
@@ -191,12 +227,17 @@ static int adm1021_attach_adapter(struct i2c_adapter *adapter)
 }
 
 static struct attribute *adm1021_attributes[] = {
-       &dev_attr_temp1_max.attr,
-       &dev_attr_temp1_min.attr,
-       &dev_attr_temp1_input.attr,
-       &dev_attr_temp2_max.attr,
-       &dev_attr_temp2_min.attr,
-       &dev_attr_temp2_input.attr,
+       &sensor_dev_attr_temp1_max.dev_attr.attr,
+       &sensor_dev_attr_temp1_min.dev_attr.attr,
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp2_max.dev_attr.attr,
+       &sensor_dev_attr_temp2_min.dev_attr.attr,
+       &sensor_dev_attr_temp2_input.dev_attr.attr,
+       &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_fault.dev_attr.attr,
        &dev_attr_alarms.attr,
        NULL
 };
@@ -208,35 +249,44 @@ static const struct attribute_group adm1021_group = {
 static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind)
 {
        int i;
-       struct i2c_client *new_client;
+       struct i2c_client *client;
        struct adm1021_data *data;
        int err = 0;
        const char *type_name = "";
+       int conv_rate, status, config;
 
-       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+               pr_debug("adm1021: detect failed, "
+                        "smbus byte data not supported!\n");
                goto error0;
+       }
 
        /* OK. For now, we presume we have a valid client. We now create the
           client structure, even though we cannot fill it completely yet.
-          But it allows us to access adm1021_{read,write}_value. */
+          But it allows us to access adm1021 register values. */
 
        if (!(data = kzalloc(sizeof(struct adm1021_data), GFP_KERNEL))) {
+               pr_debug("adm1021: detect failed, kzalloc failed!\n");
                err = -ENOMEM;
                goto error0;
        }
 
-       new_client = &data->client;
-       i2c_set_clientdata(new_client, data);
-       new_client->addr = address;
-       new_client->adapter = adapter;
-       new_client->driver = &adm1021_driver;
-       new_client->flags = 0;
+       client = &data->client;
+       i2c_set_clientdata(client, data);
+       client->addr = address;
+       client->adapter = adapter;
+       client->driver = &adm1021_driver;
+       status = i2c_smbus_read_byte_data(client, ADM1021_REG_STATUS);
+       conv_rate = i2c_smbus_read_byte_data(client,
+                                            ADM1021_REG_CONV_RATE_R);
+       config = i2c_smbus_read_byte_data(client, ADM1021_REG_CONFIG_R);
 
        /* Now, we do the remaining detection. */
        if (kind < 0) {
-               if ((adm1021_read_value(new_client, ADM1021_REG_STATUS) & 0x03) != 0x00
-                || (adm1021_read_value(new_client, ADM1021_REG_CONFIG_R) & 0x3F) != 0x00
-                || (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) & 0xF8) != 0x00) {
+               if ((status & 0x03) != 0x00 || (config & 0x3F) != 0x00
+                   || (conv_rate & 0xF8) != 0x00) {
+                       pr_debug("adm1021: detect failed, "
+                                "chip not detected!\n");
                        err = -ENODEV;
                        goto error1;
                }
@@ -244,9 +294,10 @@ static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind)
 
        /* Determine the chip type. */
        if (kind <= 0) {
-               i = adm1021_read_value(new_client, ADM1021_REG_MAN_ID);
+               i = i2c_smbus_read_byte_data(client, ADM1021_REG_MAN_ID);
                if (i == 0x41)
-                       if ((adm1021_read_value(new_client, ADM1021_REG_DEV_ID) & 0x0F0) == 0x030)
+                       if ((i2c_smbus_read_byte_data(client,
+                                       ADM1021_REG_DEV_ID) & 0xF0) == 0x30)
                                kind = adm1023;
                        else
                                kind = adm1021;
@@ -255,15 +306,16 @@ static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind)
                else if (i == 0x23)
                        kind = gl523sm;
                else if ((i == 0x4d) &&
-                        (adm1021_read_value(new_client, ADM1021_REG_DEV_ID) == 0x01))
+                        (i2c_smbus_read_byte_data(client,
+                                                  ADM1021_REG_DEV_ID) == 0x01))
                        kind = max1617a;
                else if (i == 0x54)
                        kind = mc1066;
                /* LM84 Mfr ID in a different place, and it has more unused bits */
-               else if (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) == 0x00
-                     && (kind == 0 /* skip extra detection */
-                      || ((adm1021_read_value(new_client, ADM1021_REG_CONFIG_R) & 0x7F) == 0x00
-                       && (adm1021_read_value(new_client, ADM1021_REG_STATUS) & 0xAB) == 0x00)))
+               else if (conv_rate == 0x00
+                        && (kind == 0 /* skip extra detection */
+                            || ((config & 0x7F) == 0x00
+                                && (status & 0xAB) == 0x00)))
                        kind = lm84;
                else
                        kind = max1617;
@@ -286,37 +338,38 @@ static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind)
        } else if (kind == mc1066) {
                type_name = "mc1066";
        }
+       pr_debug("adm1021: Detected chip %s at adapter %d, address 0x%02x.\n",
+                type_name, i2c_adapter_id(adapter), address);
 
-       /* Fill in the remaining client fields and put it into the global list */
-       strlcpy(new_client->name, type_name, I2C_NAME_SIZE);
+       /* Fill in the remaining client fields */
+       strlcpy(client->name, type_name, I2C_NAME_SIZE);
        data->type = kind;
-       data->valid = 0;
        mutex_init(&data->update_lock);
 
        /* Tell the I2C layer a new client has arrived */
-       if ((err = i2c_attach_client(new_client)))
+       if ((err = i2c_attach_client(client)))
                goto error1;
 
        /* Initialize the ADM1021 chip */
-       if (kind != lm84)
-               adm1021_init_client(new_client);
+       if (kind != lm84 && !read_only)
+               adm1021_init_client(client);
 
        /* Register sysfs hooks */
-       if ((err = sysfs_create_group(&new_client->dev.kobj, &adm1021_group)))
+       if ((err = sysfs_create_group(&client->dev.kobj, &adm1021_group)))
                goto error2;
 
-       data->class_dev = hwmon_device_register(&new_client->dev);
-       if (IS_ERR(data->class_dev)) {
-               err = PTR_ERR(data->class_dev);
+       data->hwmon_dev = hwmon_device_register(&client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
                goto error3;
        }
 
        return 0;
 
 error3:
-       sysfs_remove_group(&new_client->dev.kobj, &adm1021_group);
+       sysfs_remove_group(&client->dev.kobj, &adm1021_group);
 error2:
-       i2c_detach_client(new_client);
+       i2c_detach_client(client);
 error1:
        kfree(data);
 error0:
@@ -326,10 +379,10 @@ error0:
 static void adm1021_init_client(struct i2c_client *client)
 {
        /* Enable ADC and disable suspend mode */
-       adm1021_write_value(client, ADM1021_REG_CONFIG_W,
-               adm1021_read_value(client, ADM1021_REG_CONFIG_R) & 0xBF);
+       i2c_smbus_write_byte_data(client, ADM1021_REG_CONFIG_W,
+               i2c_smbus_read_byte_data(client, ADM1021_REG_CONFIG_R) & 0xBF);
        /* Set Conversion rate to 1/sec (this can be tinkered with) */
-       adm1021_write_value(client, ADM1021_REG_CONV_RATE_W, 0x04);
+       i2c_smbus_write_byte_data(client, ADM1021_REG_CONV_RATE_W, 0x04);
 }
 
 static int adm1021_detach_client(struct i2c_client *client)
@@ -337,7 +390,7 @@ static int adm1021_detach_client(struct i2c_client *client)
        struct adm1021_data *data = i2c_get_clientdata(client);
        int err;
 
-       hwmon_device_unregister(data->class_dev);
+       hwmon_device_unregister(data->hwmon_dev);
        sysfs_remove_group(&client->dev.kobj, &adm1021_group);
 
        if ((err = i2c_detach_client(client)))
@@ -347,19 +400,6 @@ static int adm1021_detach_client(struct i2c_client *client)
        return 0;
 }
 
-/* All registers are byte-sized */
-static int adm1021_read_value(struct i2c_client *client, u8 reg)
-{
-       return i2c_smbus_read_byte_data(client, reg);
-}
-
-static int adm1021_write_value(struct i2c_client *client, u8 reg, u16 value)
-{
-       if (!read_only)
-               return i2c_smbus_write_byte_data(client, reg, value);
-       return 0;
-}
-
 static struct adm1021_data *adm1021_update_device(struct device *dev)
 {
        struct i2c_client *client = to_i2c_client(dev);
@@ -369,21 +409,36 @@ static struct adm1021_data *adm1021_update_device(struct device *dev)
 
        if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
            || !data->valid) {
+               int i;
+
                dev_dbg(&client->dev, "Starting adm1021 update\n");
 
-               data->temp_input = adm1021_read_value(client, ADM1021_REG_TEMP);
-               data->temp_max = adm1021_read_value(client, ADM1021_REG_TOS_R);
-               data->temp_hyst = adm1021_read_value(client, ADM1021_REG_THYST_R);
-               data->remote_temp_input = adm1021_read_value(client, ADM1021_REG_REMOTE_TEMP);
-               data->remote_temp_max = adm1021_read_value(client, ADM1021_REG_REMOTE_TOS_R);
-               data->remote_temp_hyst = adm1021_read_value(client, ADM1021_REG_REMOTE_THYST_R);
-               data->alarms = adm1021_read_value(client, ADM1021_REG_STATUS) & 0x7c;
+               for (i = 0; i < 2; i++) {
+                       data->temp[i] = i2c_smbus_read_byte_data(client,
+                                               ADM1021_REG_TEMP(i));
+                       data->temp_max[i] = i2c_smbus_read_byte_data(client,
+                                               ADM1021_REG_TOS_R(i));
+                       data->temp_min[i] = i2c_smbus_read_byte_data(client,
+                                               ADM1021_REG_THYST_R(i));
+               }
+               data->alarms = i2c_smbus_read_byte_data(client,
+                                               ADM1021_REG_STATUS) & 0x7c;
                if (data->type == adm1023) {
-                       data->remote_temp_prec = adm1021_read_value(client, ADM1021_REG_REM_TEMP_PREC);
-                       data->remote_temp_os_prec = adm1021_read_value(client, ADM1021_REG_REM_TOS_PREC);
-                       data->remote_temp_hyst_prec = adm1021_read_value(client, ADM1021_REG_REM_THYST_PREC);
-                       data->remote_temp_offset = adm1021_read_value(client, ADM1021_REG_REM_OFFSET);
-                       data->remote_temp_offset_prec = adm1021_read_value(client, ADM1021_REG_REM_OFFSET_PREC);
+                       data->remote_temp_prec =
+                               i2c_smbus_read_byte_data(client,
+                                               ADM1023_REG_REM_TEMP_PREC);
+                       data->remote_temp_os_prec =
+                               i2c_smbus_read_byte_data(client,
+                                               ADM1023_REG_REM_TOS_PREC);
+                       data->remote_temp_hyst_prec =
+                               i2c_smbus_read_byte_data(client,
+                                               ADM1023_REG_REM_THYST_PREC);
+                       data->remote_temp_offset =
+                               i2c_smbus_read_byte_data(client,
+                                               ADM1023_REG_REM_OFFSET);
+                       data->remote_temp_offset_prec =
+                               i2c_smbus_read_byte_data(client,
+                                               ADM1023_REG_REM_OFFSET_PREC);
                }
                data->last_updated = jiffies;
                data->valid = 1;
index 8c56288..041ecb0 100644 (file)
@@ -133,7 +133,7 @@ static struct i2c_driver adm1025_driver = {
 
 struct adm1025_data {
        struct i2c_client client;
-       struct class_device *class_dev;
+       struct device *hwmon_dev;
        struct mutex update_lock;
        char valid; /* zero until following fields are valid */
        unsigned long last_updated; /* in jiffies */
@@ -292,7 +292,7 @@ static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
 
 static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf)
 {
-       struct adm1025_data *data = adm1025_update_device(dev);
+       struct adm1025_data *data = dev_get_drvdata(dev);
        return sprintf(buf, "%u\n", data->vrm);
 }
 static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
@@ -472,9 +472,9 @@ static int adm1025_detect(struct i2c_adapter *adapter, int address, int kind)
                        goto exit_remove;
        }
 
-       data->class_dev = hwmon_device_register(&new_client->dev);
-       if (IS_ERR(data->class_dev)) {
-               err = PTR_ERR(data->class_dev);
+       data->hwmon_dev = hwmon_device_register(&new_client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
                goto exit_remove;
        }
 
@@ -538,7 +538,7 @@ static int adm1025_detach_client(struct i2c_client *client)
        struct adm1025_data *data = i2c_get_clientdata(client);
        int err;
 
-       hwmon_device_unregister(data->class_dev);
+       hwmon_device_unregister(data->hwmon_dev);
        sysfs_remove_group(&client->dev.kobj, &adm1025_group);
        sysfs_remove_group(&client->dev.kobj, &adm1025_group_opt);
 
index ba80cd3..aa875ca 100644 (file)
@@ -260,7 +260,7 @@ struct pwm_data {
 
 struct adm1026_data {
        struct i2c_client client;
-       struct class_device *class_dev;
+       struct device *hwmon_dev;
        enum chips type;
 
        struct mutex update_lock;
@@ -1221,7 +1221,7 @@ static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
 
 static ssize_t show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf)
 {
-       struct adm1026_data *data = adm1026_update_device(dev);
+       struct adm1026_data *data = dev_get_drvdata(dev);
        return sprintf(buf,"%d\n", data->vrm);
 }
 static ssize_t store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf,
@@ -1676,9 +1676,9 @@ static int adm1026_detect(struct i2c_adapter *adapter, int address,
        if ((err = sysfs_create_group(&new_client->dev.kobj, &adm1026_group)))
                goto exitdetach;
 
-       data->class_dev = hwmon_device_register(&new_client->dev);
-       if (IS_ERR(data->class_dev)) {
-               err = PTR_ERR(data->class_dev);
+       data->hwmon_dev = hwmon_device_register(&new_client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
                goto exitremove;
        }
 
@@ -1698,7 +1698,7 @@ exit:
 static int adm1026_detach_client(struct i2c_client *client)
 {
        struct adm1026_data *data = i2c_get_clientdata(client);
-       hwmon_device_unregister(data->class_dev);
+       hwmon_device_unregister(data->hwmon_dev);
        sysfs_remove_group(&client->dev.kobj, &adm1026_group);
        i2c_detach_client(client);
        kfree(data);
index 73ce31b..0bc897d 100644 (file)
@@ -141,7 +141,7 @@ static struct i2c_driver adm1029_driver = {
 
 struct adm1029_data {
        struct i2c_client client;
-       struct class_device *class_dev;
+       struct device *hwmon_dev;
        struct mutex update_lock;
        char valid;             /* zero until following fields are valid */
        unsigned long last_updated;     /* in jiffies */
@@ -391,9 +391,9 @@ static int adm1029_detect(struct i2c_adapter *adapter, int address, int kind)
        if ((err = sysfs_create_group(&client->dev.kobj, &adm1029_group)))
                goto exit_detach;
 
-       data->class_dev = hwmon_device_register(&client->dev);
-       if (IS_ERR(data->class_dev)) {
-               err = PTR_ERR(data->class_dev);
+       data->hwmon_dev = hwmon_device_register(&client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
                goto exit_remove_files;
        }
 
@@ -431,7 +431,7 @@ static int adm1029_detach_client(struct i2c_client *client)
        struct adm1029_data *data = i2c_get_clientdata(client);
        int err;
 
-       hwmon_device_unregister(data->class_dev);
+       hwmon_device_unregister(data->hwmon_dev);
        sysfs_remove_group(&client->dev.kobj, &adm1029_group);
 
        if ((err = i2c_detach_client(client)))
index 122683f..37cfc10 100644 (file)
@@ -70,7 +70,7 @@ typedef u8 auto_chan_table_t[8][2];
 /* Each client has this additional data */
 struct adm1031_data {
        struct i2c_client client;
-       struct class_device *class_dev;
+       struct device *hwmon_dev;
        struct mutex update_lock;
        int chip_type;
        char valid;             /* !=0 if following fields are valid */
@@ -853,9 +853,9 @@ static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind)
                        goto exit_remove;
        }
 
-       data->class_dev = hwmon_device_register(&new_client->dev);
-       if (IS_ERR(data->class_dev)) {
-               err = PTR_ERR(data->class_dev);
+       data->hwmon_dev = hwmon_device_register(&new_client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
                goto exit_remove;
        }
 
@@ -877,7 +877,7 @@ static int adm1031_detach_client(struct i2c_client *client)
        struct adm1031_data *data = i2c_get_clientdata(client);
        int ret;
 
-       hwmon_device_unregister(data->class_dev);
+       hwmon_device_unregister(data->hwmon_dev);
        sysfs_remove_group(&client->dev.kobj, &adm1031_group);
        sysfs_remove_group(&client->dev.kobj, &adm1031_group_opt);
        if ((ret = i2c_detach_client(client)) != 0) {
index aad594a..c17d0b6 100644 (file)
@@ -150,7 +150,7 @@ static struct i2c_driver adm9240_driver = {
 struct adm9240_data {
        enum chips type;
        struct i2c_client client;
-       struct class_device *class_dev;
+       struct device *hwmon_dev;
        struct mutex update_lock;
        char valid;
        unsigned long last_updated_measure;
@@ -590,9 +590,9 @@ static int adm9240_detect(struct i2c_adapter *adapter, int address, int kind)
        if ((err = sysfs_create_group(&new_client->dev.kobj, &adm9240_group)))
                goto exit_detach;
 
-       data->class_dev = hwmon_device_register(&new_client->dev);
-       if (IS_ERR(data->class_dev)) {
-               err = PTR_ERR(data->class_dev);
+       data->hwmon_dev = hwmon_device_register(&new_client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
                goto exit_remove;
        }
 
@@ -620,7 +620,7 @@ static int adm9240_detach_client(struct i2c_client *client)
        struct adm9240_data *data = i2c_get_clientdata(client);
        int err;
 
-       hwmon_device_unregister(data->class_dev);
+       hwmon_device_unregister(data->hwmon_dev);
        sysfs_remove_group(&client->dev.kobj, &adm9240_group);
 
        if ((err = i2c_detach_client(client)))
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
new file mode 100644 (file)
index 0000000..9810aaa
--- /dev/null
@@ -0,0 +1,1050 @@
+/*
+ * A hwmon driver for the Analog Devices ADT7470
+ * Copyright (C) 2007 IBM
+ *
+ * Author: Darrick J. Wong <djwong@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/log2.h>
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD_1(adt7470);
+
+/* ADT7470 registers */
+#define ADT7470_REG_BASE_ADDR                  0x20
+#define ADT7470_REG_TEMP_BASE_ADDR             0x20
+#define ADT7470_REG_TEMP_MAX_ADDR              0x29
+#define ADT7470_REG_FAN_BASE_ADDR              0x2A
+#define ADT7470_REG_FAN_MAX_ADDR               0x31
+#define ADT7470_REG_PWM_BASE_ADDR              0x32
+#define ADT7470_REG_PWM_MAX_ADDR               0x35
+#define ADT7470_REG_PWM_MAX_BASE_ADDR          0x38
+#define ADT7470_REG_PWM_MAX_MAX_ADDR           0x3B
+#define ADT7470_REG_CFG                                0x40
+#define                ADT7470_FSPD_MASK               0x04
+#define ADT7470_REG_ALARM1                     0x41
+#define ADT7470_REG_ALARM2                     0x42
+#define ADT7470_REG_TEMP_LIMITS_BASE_ADDR      0x44
+#define ADT7470_REG_TEMP_LIMITS_MAX_ADDR       0x57
+#define ADT7470_REG_FAN_MIN_BASE_ADDR          0x58
+#define ADT7470_REG_FAN_MIN_MAX_ADDR           0x5F
+#define ADT7470_REG_FAN_MAX_BASE_ADDR          0x60
+#define ADT7470_REG_FAN_MAX_MAX_ADDR           0x67
+#define ADT7470_REG_PWM_CFG_BASE_ADDR          0x68
+#define ADT7470_REG_PWM12_CFG                  0x68
+#define                ADT7470_PWM2_AUTO_MASK          0x40
+#define                ADT7470_PWM1_AUTO_MASK          0x80
+#define ADT7470_REG_PWM34_CFG                  0x69
+#define                ADT7470_PWM3_AUTO_MASK          0x40
+#define                ADT7470_PWM4_AUTO_MASK          0x80
+#define        ADT7470_REG_PWM_MIN_BASE_ADDR           0x6A
+#define ADT7470_REG_PWM_MIN_MAX_ADDR           0x6D
+#define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR     0x6E
+#define ADT7470_REG_PWM_TEMP_MIN_MAX_ADDR      0x71
+#define ADT7470_REG_ACOUSTICS12                        0x75
+#define ADT7470_REG_ACOUSTICS34                        0x76
+#define ADT7470_REG_DEVICE                     0x3D
+#define ADT7470_REG_VENDOR                     0x3E
+#define ADT7470_REG_REVISION                   0x3F
+#define ADT7470_REG_ALARM1_MASK                        0x72
+#define ADT7470_REG_ALARM2_MASK                        0x73
+#define ADT7470_REG_PWM_AUTO_TEMP_BASE_ADDR    0x7C
+#define ADT7470_REG_PWM_AUTO_TEMP_MAX_ADDR     0x7D
+#define ADT7470_REG_MAX_ADDR                   0x81
+
+#define ADT7470_TEMP_COUNT     10
+#define ADT7470_TEMP_REG(x)    (ADT7470_REG_TEMP_BASE_ADDR + (x))
+#define ADT7470_TEMP_MIN_REG(x) (ADT7470_REG_TEMP_LIMITS_BASE_ADDR + ((x) * 2))
+#define ADT7470_TEMP_MAX_REG(x) (ADT7470_REG_TEMP_LIMITS_BASE_ADDR + \
+                               ((x) * 2) + 1)
+
+#define ADT7470_FAN_COUNT      4
+#define ADT7470_REG_FAN(x)     (ADT7470_REG_FAN_BASE_ADDR + ((x) * 2))
+#define ADT7470_REG_FAN_MIN(x) (ADT7470_REG_FAN_MIN_BASE_ADDR + ((x) * 2))
+#define ADT7470_REG_FAN_MAX(x) (ADT7470_REG_FAN_MAX_BASE_ADDR + ((x) * 2))
+
+#define ADT7470_PWM_COUNT      4
+#define ADT7470_REG_PWM(x)     (ADT7470_REG_PWM_BASE_ADDR + (x))
+#define ADT7470_REG_PWM_MAX(x) (ADT7470_REG_PWM_MAX_BASE_ADDR + (x))
+#define ADT7470_REG_PWM_MIN(x) (ADT7470_REG_PWM_MIN_BASE_ADDR + (x))
+#define ADT7470_REG_PWM_TMIN(x)        (ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR + (x))
+#define ADT7470_REG_PWM_CFG(x) (ADT7470_REG_PWM_CFG_BASE_ADDR + ((x) / 2))
+#define ADT7470_REG_PWM_AUTO_TEMP(x)   (ADT7470_REG_PWM_AUTO_TEMP_BASE_ADDR + \
+                                       ((x) / 2))
+
+#define ADT7470_VENDOR         0x41
+#define ADT7470_DEVICE         0x70
+/* datasheet only mentions a revision 2 */
+#define ADT7470_REVISION       0x02
+
+/* "all temps" according to hwmon sysfs interface spec */
+#define ADT7470_PWM_ALL_TEMPS  0x3FF
+
+/* How often do we reread sensors values? (In jiffies) */
+#define SENSOR_REFRESH_INTERVAL        (5 * HZ)
+
+/* How often do we reread sensor limit values? (In jiffies) */
+#define LIMIT_REFRESH_INTERVAL (60 * HZ)
+
+/* sleep 1s while gathering temperature data */
+#define TEMP_COLLECTION_TIME   1000
+
+#define power_of_2(x)  (((x) & ((x) - 1)) == 0)
+
+/* datasheet says to divide this number by the fan reading to get fan rpm */
+#define FAN_PERIOD_TO_RPM(x)   ((90000 * 60) / (x))
+#define FAN_RPM_TO_PERIOD      FAN_PERIOD_TO_RPM
+#define FAN_PERIOD_INVALID     65535
+#define FAN_DATA_VALID(x)      ((x) && (x) != FAN_PERIOD_INVALID)
+
+struct adt7470_data {
+       struct i2c_client       client;
+       struct device           *hwmon_dev;
+       struct attribute_group  attrs;
+       struct mutex            lock;
+       char                    sensors_valid;
+       char                    limits_valid;
+       unsigned long           sensors_last_updated;   /* In jiffies */
+       unsigned long           limits_last_updated;    /* In jiffies */
+
+       s8                      temp[ADT7470_TEMP_COUNT];
+       s8                      temp_min[ADT7470_TEMP_COUNT];
+       s8                      temp_max[ADT7470_TEMP_COUNT];
+       u16                     fan[ADT7470_FAN_COUNT];
+       u16                     fan_min[ADT7470_FAN_COUNT];
+       u16                     fan_max[ADT7470_FAN_COUNT];
+       u16                     alarms, alarms_mask;
+       u8                      force_pwm_max;
+       u8                      pwm[ADT7470_PWM_COUNT];
+       u8                      pwm_max[ADT7470_PWM_COUNT];
+       u8                      pwm_automatic[ADT7470_PWM_COUNT];
+       u8                      pwm_min[ADT7470_PWM_COUNT];
+       s8                      pwm_tmin[ADT7470_PWM_COUNT];
+       u8                      pwm_auto_temp[ADT7470_PWM_COUNT];
+};
+
+static int adt7470_attach_adapter(struct i2c_adapter *adapter);
+static int adt7470_detect(struct i2c_adapter *adapter, int address, int kind);
+static int adt7470_detach_client(struct i2c_client *client);
+
+static struct i2c_driver adt7470_driver = {
+       .driver = {
+               .name   = "adt7470",
+       },
+       .attach_adapter = adt7470_attach_adapter,
+       .detach_client  = adt7470_detach_client,
+};
+
+/*
+ * 16-bit registers on the ADT7470 are low-byte first.  The data sheet says
+ * that the low byte must be read before the high byte.
+ */
+static inline int adt7470_read_word_data(struct i2c_client *client, u8 reg)
+{
+       u16 foo;
+       foo = i2c_smbus_read_byte_data(client, reg);
+       foo |= ((u16)i2c_smbus_read_byte_data(client, reg + 1) << 8);
+       return foo;
+}
+
+static inline int adt7470_write_word_data(struct i2c_client *client, u8 reg,
+                                         u16 value)
+{
+       return i2c_smbus_write_byte_data(client, reg, value & 0xFF)
+              && i2c_smbus_write_byte_data(client, reg + 1, value >> 8);
+}
+
+static void adt7470_init_client(struct i2c_client *client)
+{
+       int reg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
+
+       if (reg < 0) {
+               dev_err(&client->dev, "cannot read configuration register\n");
+       } else {
+               /* start monitoring (and do a self-test) */
+               i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, reg | 3);
+       }
+}
+
+static struct adt7470_data *adt7470_update_device(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adt7470_data *data = i2c_get_clientdata(client);
+       unsigned long local_jiffies = jiffies;
+       u8 cfg;
+       int i;
+
+       mutex_lock(&data->lock);
+       if (time_before(local_jiffies, data->sensors_last_updated +
+               SENSOR_REFRESH_INTERVAL)
+               && data->sensors_valid)
+               goto no_sensor_update;
+
+       /* start reading temperature sensors */
+       cfg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
+       cfg |= 0x80;
+       i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg);
+
+       /*
+        * Delay is 200ms * number of tmp05 sensors.  Too bad
+        * there's no way to figure out how many are connected.
+        * For now, assume 1s will work.
+        */
+       msleep(TEMP_COLLECTION_TIME);
+
+       /* done reading temperature sensors */
+       cfg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
+       cfg &= ~0x80;
+       i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg);
+
+       for (i = 0; i < ADT7470_TEMP_COUNT; i++)
+               data->temp[i] = i2c_smbus_read_byte_data(client,
+                                               ADT7470_TEMP_REG(i));
+
+       for (i = 0; i < ADT7470_FAN_COUNT; i++)
+               data->fan[i] = adt7470_read_word_data(client,
+                                               ADT7470_REG_FAN(i));
+
+       for (i = 0; i < ADT7470_PWM_COUNT; i++) {
+               int reg;
+               int reg_mask;
+
+               data->pwm[i] = i2c_smbus_read_byte_data(client,
+                                               ADT7470_REG_PWM(i));
+
+               if (i % 2)
+                       reg_mask = ADT7470_PWM2_AUTO_MASK;
+               else
+                       reg_mask = ADT7470_PWM1_AUTO_MASK;
+
+               reg = ADT7470_REG_PWM_CFG(i);
+               if (i2c_smbus_read_byte_data(client, reg) & reg_mask)
+                       data->pwm_automatic[i] = 1;
+               else
+                       data->pwm_automatic[i] = 0;
+
+               reg = ADT7470_REG_PWM_AUTO_TEMP(i);
+               cfg = i2c_smbus_read_byte_data(client, reg);
+               if (!(i % 2))
+                       data->pwm_auto_temp[i] = cfg >> 4;
+               else
+                       data->pwm_auto_temp[i] = cfg & 0xF;
+       }
+
+       if (i2c_smbus_read_byte_data(client, ADT7470_REG_CFG) &
+           ADT7470_FSPD_MASK)
+               data->force_pwm_max = 1;
+       else
+               data->force_pwm_max = 0;
+
+       data->alarms = adt7470_read_word_data(client, ADT7470_REG_ALARM1);
+       data->alarms_mask = adt7470_read_word_data(client,
+                                                  ADT7470_REG_ALARM1_MASK);
+
+       data->sensors_last_updated = local_jiffies;
+       data->sensors_valid = 1;
+
+no_sensor_update:
+       if (time_before(local_jiffies, data->limits_last_updated +
+               LIMIT_REFRESH_INTERVAL)
+               && data->limits_valid)
+               goto out;
+
+       for (i = 0; i < ADT7470_TEMP_COUNT; i++) {
+               data->temp_min[i] = i2c_smbus_read_byte_data(client,
+                                               ADT7470_TEMP_MIN_REG(i));
+               data->temp_max[i] = i2c_smbus_read_byte_data(client,
+                                               ADT7470_TEMP_MAX_REG(i));
+       }
+
+       for (i = 0; i < ADT7470_FAN_COUNT; i++) {
+               data->fan_min[i] = adt7470_read_word_data(client,
+                                               ADT7470_REG_FAN_MIN(i));
+               data->fan_max[i] = adt7470_read_word_data(client,
+                                               ADT7470_REG_FAN_MAX(i));
+       }
+
+       for (i = 0; i < ADT7470_PWM_COUNT; i++) {
+               data->pwm_max[i] = i2c_smbus_read_byte_data(client,
+                                               ADT7470_REG_PWM_MAX(i));
+               data->pwm_min[i] = i2c_smbus_read_byte_data(client,
+                                               ADT7470_REG_PWM_MIN(i));
+               data->pwm_tmin[i] = i2c_smbus_read_byte_data(client,
+                                               ADT7470_REG_PWM_TMIN(i));
+       }
+
+       data->limits_last_updated = local_jiffies;
+       data->limits_valid = 1;
+
+out:
+       mutex_unlock(&data->lock);
+       return data;
+}
+
+static ssize_t show_temp_min(struct device *dev,
+                            struct device_attribute *devattr,
+                            char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
+       return sprintf(buf, "%d\n", 1000 * data->temp_min[attr->index]);
+}
+
+static ssize_t set_temp_min(struct device *dev,
+                           struct device_attribute *devattr,
+                           const char *buf,
+                           size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adt7470_data *data = i2c_get_clientdata(client);
+       int temp = simple_strtol(buf, NULL, 10) / 1000;
+
+       mutex_lock(&data->lock);
+       data->temp_min[attr->index] = temp;
+       i2c_smbus_write_byte_data(client, ADT7470_TEMP_MIN_REG(attr->index),
+                                 temp);
+       mutex_unlock(&data->lock);
+
+       return count;
+}
+
+static ssize_t show_temp_max(struct device *dev,
+                            struct device_attribute *devattr,
+                            char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
+       return sprintf(buf, "%d\n", 1000 * data->temp_max[attr->index]);
+}
+
+static ssize_t set_temp_max(struct device *dev,
+                           struct device_attribute *devattr,
+                           const char *buf,
+                           size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adt7470_data *data = i2c_get_clientdata(client);
+       int temp = simple_strtol(buf, NULL, 10) / 1000;
+
+       mutex_lock(&data->lock);
+       data->temp_max[attr->index] = temp;
+       i2c_smbus_write_byte_data(client, ADT7470_TEMP_MAX_REG(attr->index),
+                                 temp);
+       mutex_unlock(&data->lock);
+
+       return count;
+}
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
+                        char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
+       return sprintf(buf, "%d\n", 1000 * data->temp[attr->index]);
+}
+
+static ssize_t show_alarms(struct device *dev,
+                          struct device_attribute *devattr,
+                          char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
+
+       if (attr->index)
+               return sprintf(buf, "%x\n", data->alarms);
+       else
+               return sprintf(buf, "%x\n", data->alarms_mask);
+}
+
+static ssize_t show_fan_max(struct device *dev,
+                           struct device_attribute *devattr,
+                           char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
+
+       if (FAN_DATA_VALID(data->fan_max[attr->index]))
+               return sprintf(buf, "%d\n",
+                              FAN_PERIOD_TO_RPM(data->fan_max[attr->index]));
+       else
+               return sprintf(buf, "0\n");
+}
+
+static ssize_t set_fan_max(struct device *dev,
+                          struct device_attribute *devattr,
+                          const char *buf, size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adt7470_data *data = i2c_get_clientdata(client);
+       int temp = simple_strtol(buf, NULL, 10);
+
+       if (!temp)
+               return -EINVAL;
+       temp = FAN_RPM_TO_PERIOD(temp);
+
+       mutex_lock(&data->lock);
+       data->fan_max[attr->index] = temp;
+       adt7470_write_word_data(client, ADT7470_REG_FAN_MAX(attr->index), temp);
+       mutex_unlock(&data->lock);
+
+       return count;
+}
+
+static ssize_t show_fan_min(struct device *dev,
+                           struct device_attribute *devattr,
+                           char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
+
+       if (FAN_DATA_VALID(data->fan_min[attr->index]))
+               return sprintf(buf, "%d\n",
+                              FAN_PERIOD_TO_RPM(data->fan_min[attr->index]));
+       else
+               return sprintf(buf, "0\n");
+}
+
+static ssize_t set_fan_min(struct device *dev,
+                          struct device_attribute *devattr,
+                          const char *buf, size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adt7470_data *data = i2c_get_clientdata(client);
+       int temp = simple_strtol(buf, NULL, 10);
+
+       if (!temp)
+               return -EINVAL;
+       temp = FAN_RPM_TO_PERIOD(temp);
+
+       mutex_lock(&data->lock);
+       data->fan_min[attr->index] = temp;
+       adt7470_write_word_data(client, ADT7470_REG_FAN_MIN(attr->index), temp);
+       mutex_unlock(&data->lock);
+
+       return count;
+}
+
+static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
+                       char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
+
+       if (FAN_DATA_VALID(data->fan[attr->index]))
+               return sprintf(buf, "%d\n",
+                              FAN_PERIOD_TO_RPM(data->fan[attr->index]));
+       else
+               return sprintf(buf, "0\n");
+}
+
+static ssize_t show_force_pwm_max(struct device *dev,
+                                 struct device_attribute *devattr,
+                                 char *buf)
+{
+       struct adt7470_data *data = adt7470_update_device(dev);
+       return sprintf(buf, "%d\n", data->force_pwm_max);
+}
+
+static ssize_t set_force_pwm_max(struct device *dev,
+                                struct device_attribute *devattr,
+                                const char *buf,
+                                size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adt7470_data *data = i2c_get_clientdata(client);
+       int temp = simple_strtol(buf, NULL, 10);
+       u8 reg;
+
+       mutex_lock(&data->lock);
+       data->force_pwm_max = temp;
+       reg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
+       if (temp)
+               reg |= ADT7470_FSPD_MASK;
+       else
+               reg &= ~ADT7470_FSPD_MASK;
+       i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, reg);
+       mutex_unlock(&data->lock);
+
+       return count;
+}
+
+static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr,
+                       char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
+       return sprintf(buf, "%d\n", data->pwm[attr->index]);
+}
+
+static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
+                       const char *buf, size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adt7470_data *data = i2c_get_clientdata(client);
+       int temp = simple_strtol(buf, NULL, 10);
+
+       mutex_lock(&data->lock);
+       data->pwm[attr->index] = temp;
+       i2c_smbus_write_byte_data(client, ADT7470_REG_PWM(attr->index), temp);
+       mutex_unlock(&data->lock);
+
+       return count;
+}
+
+static ssize_t show_pwm_max(struct device *dev,
+                           struct device_attribute *devattr,
+                           char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
+       return sprintf(buf, "%d\n", data->pwm_max[attr->index]);
+}
+
+static ssize_t set_pwm_max(struct device *dev,
+                          struct device_attribute *devattr,
+                          const char *buf,
+                          size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adt7470_data *data = i2c_get_clientdata(client);
+       int temp = simple_strtol(buf, NULL, 10);
+
+       mutex_lock(&data->lock);
+       data->pwm_max[attr->index] = temp;
+       i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_MAX(attr->index),
+                                 temp);
+       mutex_unlock(&data->lock);
+
+       return count;
+}
+
+static ssize_t show_pwm_min(struct device *dev,
+                           struct device_attribute *devattr,
+                           char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
+       return sprintf(buf, "%d\n", data->pwm_min[attr->index]);
+}
+
+static ssize_t set_pwm_min(struct device *dev,
+                          struct device_attribute *devattr,
+                          const char *buf,
+                          size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adt7470_data *data = i2c_get_clientdata(client);
+       int temp = simple_strtol(buf, NULL, 10);
+
+       mutex_lock(&data->lock);
+       data->pwm_min[attr->index] = temp;
+       i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_MIN(attr->index),
+                                 temp);
+       mutex_unlock(&data->lock);
+
+       return count;
+}
+
+static ssize_t show_pwm_tmax(struct device *dev,
+                            struct device_attribute *devattr,
+                            char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
+       /* the datasheet says that tmax = tmin + 20C */
+       return sprintf(buf, "%d\n", 1000 * (20 + data->pwm_tmin[attr->index]));
+}
+
+static ssize_t show_pwm_tmin(struct device *dev,
+                            struct device_attribute *devattr,
+                            char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
+       return sprintf(buf, "%d\n", 1000 * data->pwm_tmin[attr->index]);
+}
+
+static ssize_t set_pwm_tmin(struct device *dev,
+                           struct device_attribute *devattr,
+                           const char *buf,
+                           size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adt7470_data *data = i2c_get_clientdata(client);
+       int temp = simple_strtol(buf, NULL, 10) / 1000;
+
+       mutex_lock(&data->lock);
+       data->pwm_tmin[attr->index] = temp;
+       i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_TMIN(attr->index),
+                                 temp);
+       mutex_unlock(&data->lock);
+
+       return count;
+}
+
+static ssize_t show_pwm_auto(struct device *dev,
+                            struct device_attribute *devattr,
+                            char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
+       return sprintf(buf, "%d\n", 1 + data->pwm_automatic[attr->index]);
+}
+
+static ssize_t set_pwm_auto(struct device *dev,
+                           struct device_attribute *devattr,
+                           const char *buf,
+                           size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adt7470_data *data = i2c_get_clientdata(client);
+       int temp = simple_strtol(buf, NULL, 10);
+       int pwm_auto_reg = ADT7470_REG_PWM_CFG(attr->index);
+       int pwm_auto_reg_mask;
+       u8 reg;
+
+       if (attr->index % 2)
+               pwm_auto_reg_mask = ADT7470_PWM2_AUTO_MASK;
+       else
+               pwm_auto_reg_mask = ADT7470_PWM1_AUTO_MASK;
+
+       if (temp != 2 && temp != 1)
+               return -EINVAL;
+       temp--;
+
+       mutex_lock(&data->lock);
+       data->pwm_automatic[attr->index] = temp;
+       reg = i2c_smbus_read_byte_data(client, pwm_auto_reg);
+       if (temp)
+               reg |= pwm_auto_reg_mask;
+       else
+               reg &= ~pwm_auto_reg_mask;
+       i2c_smbus_write_byte_data(client, pwm_auto_reg, reg);
+       mutex_unlock(&data->lock);
+
+       return count;
+}
+
+static ssize_t show_pwm_auto_temp(struct device *dev,
+                                 struct device_attribute *devattr,
+                                 char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct adt7470_data *data = adt7470_update_device(dev);
+       u8 ctrl = data->pwm_auto_temp[attr->index];
+
+       if (ctrl)
+               return sprintf(buf, "%d\n", 1 << (ctrl - 1));
+       else
+               return sprintf(buf, "%d\n", ADT7470_PWM_ALL_TEMPS);
+}
+
+static int cvt_auto_temp(int input)
+{
+       if (input == ADT7470_PWM_ALL_TEMPS)
+               return 0;
+       if (input < 1 || !power_of_2(input))
+               return -EINVAL;
+       return ilog2(input) + 1;
+}
+
+static ssize_t set_pwm_auto_temp(struct device *dev,
+                                struct device_attribute *devattr,
+                                const char *buf,
+                                size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adt7470_data *data = i2c_get_clientdata(client);
+       int temp = cvt_auto_temp(simple_strtol(buf, NULL, 10));
+       int pwm_auto_reg = ADT7470_REG_PWM_AUTO_TEMP(attr->index);
+       u8 reg;
+
+       if (temp < 0)
+               return temp;
+
+       mutex_lock(&data->lock);
+       data->pwm_automatic[attr->index] = temp;
+       reg = i2c_smbus_read_byte_data(client, pwm_auto_reg);
+
+       if (!(attr->index % 2)) {
+               reg &= 0xF;
+               reg |= (temp << 4) & 0xF0;
+       } else {
+               reg &= 0xF0;
+               reg |= temp & 0xF;
+       }
+
+       i2c_smbus_write_byte_data(client, pwm_auto_reg, reg);
+       mutex_unlock(&data->lock);
+
+       return count;
+}
+
+static SENSOR_DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL, 0);
+static SENSOR_DEVICE_ATTR(alarm_mask, S_IRUGO, show_alarms, NULL, 1);
+
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
+                   set_temp_max, 0);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_max,
+                   set_temp_max, 1);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_temp_max,
+                   set_temp_max, 2);
+static SENSOR_DEVICE_ATTR(temp4_max, S_IWUSR | S_IRUGO, show_temp_max,
+                   set_temp_max, 3);
+static SENSOR_DEVICE_ATTR(temp5_max, S_IWUSR | S_IRUGO, show_temp_max,
+                   set_temp_max, 4);
+static SENSOR_DEVICE_ATTR(temp6_max, S_IWUSR | S_IRUGO, show_temp_max,
+                   set_temp_max, 5);
+static SENSOR_DEVICE_ATTR(temp7_max, S_IWUSR | S_IRUGO, show_temp_max,
+                   set_temp_max, 6);
+static SENSOR_DEVICE_ATTR(temp8_max, S_IWUSR | S_IRUGO, show_temp_max,
+                   set_temp_max, 7);
+static SENSOR_DEVICE_ATTR(temp9_max, S_IWUSR | S_IRUGO, show_temp_max,
+                   set_temp_max, 8);
+static SENSOR_DEVICE_ATTR(temp10_max, S_IWUSR | S_IRUGO, show_temp_max,
+                   set_temp_max, 9);
+
+static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_min,
+                   set_temp_min, 0);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_min,
+                   set_temp_min, 1);
+static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_temp_min,
+                   set_temp_min, 2);
+static SENSOR_DEVICE_ATTR(temp4_min, S_IWUSR | S_IRUGO, show_temp_min,
+                   set_temp_min, 3);
+static SENSOR_DEVICE_ATTR(temp5_min, S_IWUSR | S_IRUGO, show_temp_min,
+                   set_temp_min, 4);
+static SENSOR_DEVICE_ATTR(temp6_min, S_IWUSR | S_IRUGO, show_temp_min,
+                   set_temp_min, 5);
+static SENSOR_DEVICE_ATTR(temp7_min, S_IWUSR | S_IRUGO, show_temp_min,
+                   set_temp_min, 6);
+static SENSOR_DEVICE_ATTR(temp8_min, S_IWUSR | S_IRUGO, show_temp_min,
+                   set_temp_min, 7);
+static SENSOR_DEVICE_ATTR(temp9_min, S_IWUSR | S_IRUGO, show_temp_min,
+                   set_temp_min, 8);
+static SENSOR_DEVICE_ATTR(temp10_min, S_IWUSR | S_IRUGO, show_temp_min,
+                   set_temp_min, 9);
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_temp, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, show_temp, NULL, 6);
+static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, show_temp, NULL, 7);
+static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO, show_temp, NULL, 8);
+static SENSOR_DEVICE_ATTR(temp10_input, S_IRUGO, show_temp, NULL, 9);
+
+static SENSOR_DEVICE_ATTR(fan1_max, S_IWUSR | S_IRUGO, show_fan_max,
+                   set_fan_max, 0);
+static SENSOR_DEVICE_ATTR(fan2_max, S_IWUSR | S_IRUGO, show_fan_max,
+                   set_fan_max, 1);
+static SENSOR_DEVICE_ATTR(fan3_max, S_IWUSR | S_IRUGO, show_fan_max,
+                   set_fan_max, 2);
+static SENSOR_DEVICE_ATTR(fan4_max, S_IWUSR | S_IRUGO, show_fan_max,
+                   set_fan_max, 3);
+
+static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
+                   set_fan_min, 0);
+static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min,
+                   set_fan_min, 1);
+static SENSOR_DEVICE_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min,
+                   set_fan_min, 2);
+static SENSOR_DEVICE_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min,
+                   set_fan_min, 3);
+
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3);
+
+static SENSOR_DEVICE_ATTR(force_pwm_max, S_IWUSR | S_IRUGO,
+                   show_force_pwm_max, set_force_pwm_max, 0);
+
+static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0);
+static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1);
+static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2);
+static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3);
+
+static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
+                   show_pwm_min, set_pwm_min, 0);
+static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO,
+                   show_pwm_min, set_pwm_min, 1);
+static SENSOR_DEVICE_ATTR(pwm3_auto_point1_pwm, S_IWUSR | S_IRUGO,
+                   show_pwm_min, set_pwm_min, 2);
+static SENSOR_DEVICE_ATTR(pwm4_auto_point1_pwm, S_IWUSR | S_IRUGO,
+                   show_pwm_min, set_pwm_min, 3);
+
+static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO,
+                   show_pwm_max, set_pwm_max, 0);
+static SENSOR_DEVICE_ATTR(pwm2_auto_point2_pwm, S_IWUSR | S_IRUGO,
+                   show_pwm_max, set_pwm_max, 1);
+static SENSOR_DEVICE_ATTR(pwm3_auto_point2_pwm, S_IWUSR | S_IRUGO,
+                   show_pwm_max, set_pwm_max, 2);
+static SENSOR_DEVICE_ATTR(pwm4_auto_point2_pwm, S_IWUSR | S_IRUGO,
+                   show_pwm_max, set_pwm_max, 3);
+
+static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO,
+                   show_pwm_tmin, set_pwm_tmin, 0);
+static SENSOR_DEVICE_ATTR(pwm2_auto_point1_temp, S_IWUSR | S_IRUGO,
+                   show_pwm_tmin, set_pwm_tmin, 1);
+static SENSOR_DEVICE_ATTR(pwm3_auto_point1_temp, S_IWUSR | S_IRUGO,
+                   show_pwm_tmin, set_pwm_tmin, 2);
+static SENSOR_DEVICE_ATTR(pwm4_auto_point1_temp, S_IWUSR | S_IRUGO,
+                   show_pwm_tmin, set_pwm_tmin, 3);
+
+static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp, S_IRUGO, show_pwm_tmax,
+                   NULL, 0);
+static SENSOR_DEVICE_ATTR(pwm2_auto_point2_temp, S_IRUGO, show_pwm_tmax,
+                   NULL, 1);
+static SENSOR_DEVICE_ATTR(pwm3_auto_point2_temp, S_IRUGO, show_pwm_tmax,
+                   NULL, 2);
+static SENSOR_DEVICE_ATTR(pwm4_auto_point2_temp, S_IRUGO, show_pwm_tmax,
+                   NULL, 3);
+
+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_auto,
+                   set_pwm_auto, 0);
+static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_auto,
+                   set_pwm_auto, 1);
+static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_auto,
+                   set_pwm_auto, 2);
+static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_auto,
+                   set_pwm_auto, 3);
+
+static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, S_IWUSR | S_IRUGO,
+                   show_pwm_auto_temp, set_pwm_auto_temp, 0);
+static SENSOR_DEVICE_ATTR(pwm2_auto_channels_temp, S_IWUSR | S_IRUGO,
+                   show_pwm_auto_temp, set_pwm_auto_temp, 1);
+static SENSOR_DEVICE_ATTR(pwm3_auto_channels_temp, S_IWUSR | S_IRUGO,
+                   show_pwm_auto_temp, set_pwm_auto_temp, 2);
+static SENSOR_DEVICE_ATTR(pwm4_auto_channels_temp, S_IWUSR | S_IRUGO,
+                   show_pwm_auto_temp, set_pwm_auto_temp, 3);
+
+static struct attribute *adt7470_attr[] =
+{
+       &sensor_dev_attr_alarms.dev_attr.attr,
+       &sensor_dev_attr_alarm_mask.dev_attr.attr,
+       &sensor_dev_attr_temp1_max.dev_attr.attr,
+       &sensor_dev_attr_temp2_max.dev_attr.attr,
+       &sensor_dev_attr_temp3_max.dev_attr.attr,
+       &sensor_dev_attr_temp4_max.dev_attr.attr,
+       &sensor_dev_attr_temp5_max.dev_attr.attr,
+       &sensor_dev_attr_temp6_max.dev_attr.attr,
+       &sensor_dev_attr_temp7_max.dev_attr.attr,
+       &sensor_dev_attr_temp8_max.dev_attr.attr,
+       &sensor_dev_attr_temp9_max.dev_attr.attr,
+       &sensor_dev_attr_temp10_max.dev_attr.attr,
+       &sensor_dev_attr_temp1_min.dev_attr.attr,
+       &sensor_dev_attr_temp2_min.dev_attr.attr,
+       &sensor_dev_attr_temp3_min.dev_attr.attr,
+       &sensor_dev_attr_temp4_min.dev_attr.attr,
+       &sensor_dev_attr_temp5_min.dev_attr.attr,
+       &sensor_dev_attr_temp6_min.dev_attr.attr,
+       &sensor_dev_attr_temp7_min.dev_attr.attr,
+       &sensor_dev_attr_temp8_min.dev_attr.attr,
+       &sensor_dev_attr_temp9_min.dev_attr.attr,
+       &sensor_dev_attr_temp10_min.dev_attr.attr,
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp2_input.dev_attr.attr,
+       &sensor_dev_attr_temp3_input.dev_attr.attr,
+       &sensor_dev_attr_temp4_input.dev_attr.attr,
+       &sensor_dev_attr_temp5_input.dev_attr.attr,
+       &sensor_dev_attr_temp6_input.dev_attr.attr,
+       &sensor_dev_attr_temp7_input.dev_attr.attr,
+       &sensor_dev_attr_temp8_input.dev_attr.attr,
+       &sensor_dev_attr_temp9_input.dev_attr.attr,
+       &sensor_dev_attr_temp10_input.dev_attr.attr,
+       &sensor_dev_attr_fan1_max.dev_attr.attr,
+       &sensor_dev_attr_fan2_max.dev_attr.attr,
+       &sensor_dev_attr_fan3_max.dev_attr.attr,
+       &sensor_dev_attr_fan4_max.dev_attr.attr,
+       &sensor_dev_attr_fan1_min.dev_attr.attr,
+       &sensor_dev_attr_fan2_min.dev_attr.attr,
+       &sensor_dev_attr_fan3_min.dev_attr.attr,
+       &sensor_dev_attr_fan4_min.dev_attr.attr,
+       &sensor_dev_attr_fan1_input.dev_attr.attr,
+       &sensor_dev_attr_fan2_input.dev_attr.attr,
+       &sensor_dev_attr_fan3_input.dev_attr.attr,
+       &sensor_dev_attr_fan4_input.dev_attr.attr,
+       &sensor_dev_attr_force_pwm_max.dev_attr.attr,
+       &sensor_dev_attr_pwm1.dev_attr.attr,
+       &sensor_dev_attr_pwm2.dev_attr.attr,
+       &sensor_dev_attr_pwm3.dev_attr.attr,
+       &sensor_dev_attr_pwm4.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm4_auto_point1_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm4_auto_point2_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm4_auto_point1_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm4_auto_point2_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+       &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+       &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+       &sensor_dev_attr_pwm4_enable.dev_attr.attr,
+       &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm4_auto_channels_temp.dev_attr.attr,
+       NULL
+};
+
+static int adt7470_attach_adapter(struct i2c_adapter *adapter)
+{
+       if (!(adapter->class & I2C_CLASS_HWMON))
+               return 0;
+       return i2c_probe(adapter, &addr_data, adt7470_detect);
+}
+
+static int adt7470_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+       struct i2c_client *client;
+       struct adt7470_data *data;
+       int err = 0;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               goto exit;
+
+       if (!(data = kzalloc(sizeof(struct adt7470_data), GFP_KERNEL))) {
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       client = &data->client;
+       client->addr = address;
+       client->adapter = adapter;
+       client->driver = &adt7470_driver;
+
+       i2c_set_clientdata(client, data);
+
+       mutex_init(&data->lock);
+
+       if (kind <= 0) {
+               int vendor, device, revision;
+
+               vendor = i2c_smbus_read_byte_data(client, ADT7470_REG_VENDOR);
+               if (vendor != ADT7470_VENDOR) {
+                       err = -ENODEV;
+                       goto exit_free;
+               }
+
+               device = i2c_smbus_read_byte_data(client, ADT7470_REG_DEVICE);
+               if (device != ADT7470_DEVICE) {
+                       err = -ENODEV;
+                       goto exit_free;
+               }
+
+               revision = i2c_smbus_read_byte_data(client,
+                                                   ADT7470_REG_REVISION);
+               if (revision != ADT7470_REVISION) {
+                       err = -ENODEV;
+                       goto exit_free;
+               }
+       } else
+               dev_dbg(&adapter->dev, "detection forced\n");
+
+       strlcpy(client->name, "adt7470", I2C_NAME_SIZE);
+
+       if ((err = i2c_attach_client(client)))
+               goto exit_free;
+
+       dev_info(&client->dev, "%s chip found\n", client->name);
+
+       /* Initialize the ADT7470 chip */
+       adt7470_init_client(client);
+
+       /* Register sysfs hooks */
+       data->attrs.attrs = adt7470_attr;
+       if ((err = sysfs_create_group(&client->dev.kobj, &data->attrs)))
+               goto exit_detach;
+
+       data->hwmon_dev = hwmon_device_register(&client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
+               goto exit_remove;
+       }
+
+       return 0;
+
+exit_remove:
+       sysfs_remove_group(&client->dev.kobj, &data->attrs);
+exit_detach:
+       i2c_detach_client(client);
+exit_free:
+       kfree(data);
+exit:
+       return err;
+}
+
+static int adt7470_detach_client(struct i2c_client *client)
+{
+       struct adt7470_data *data = i2c_get_clientdata(client);
+
+       hwmon_device_unregister(data->hwmon_dev);
+       sysfs_remove_group(&client->dev.kobj, &data->attrs);
+       i2c_detach_client(client);
+       kfree(data);
+       return 0;
+}
+
+static int __init adt7470_init(void)
+{
+       return i2c_add_driver(&adt7470_driver);
+}
+
+static void __exit adt7470_exit(void)
+{
+       i2c_del_driver(&adt7470_driver);
+}
+
+MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
+MODULE_DESCRIPTION("ADT7470 driver");
+MODULE_LICENSE("GPL");
+
+module_init(adt7470_init);
+module_exit(adt7470_exit);
index 56213b7..f37fd7e 100644 (file)
@@ -127,7 +127,7 @@ static s16 rest_x;
 static s16 rest_y;
 static struct timer_list applesmc_timer;
 static struct input_dev *applesmc_idev;
-static struct class_device *hwmon_class_dev;
+static struct device *hwmon_dev;
 
 /* Indicates whether this computer has an accelerometer. */
 static unsigned int applesmc_accelerometer;
@@ -1287,9 +1287,9 @@ static int __init applesmc_init(void)
                        goto out_light_wq;
        }
 
-       hwmon_class_dev = hwmon_device_register(&pdev->dev);
-       if (IS_ERR(hwmon_class_dev)) {
-               ret = PTR_ERR(hwmon_class_dev);
+       hwmon_dev = hwmon_device_register(&pdev->dev);
+       if (IS_ERR(hwmon_dev)) {
+               ret = PTR_ERR(hwmon_dev);
                goto out_light_ledclass;
        }
 
@@ -1331,7 +1331,7 @@ out:
 
 static void __exit applesmc_exit(void)
 {
-       hwmon_device_unregister(hwmon_class_dev);
+       hwmon_device_unregister(hwmon_dev);
        if (applesmc_light) {
                led_classdev_unregister(&applesmc_backlight);
                destroy_workqueue(applesmc_led_wq);
index 57b1c7b..9460dba 100644 (file)
@@ -143,7 +143,7 @@ static int FAN_FROM_REG(u8 val, int div)
 
 /* TEMP: 0.001C/bit (-128C to +127C)
    REG: 1C/bit, two's complement */
-static u8 TEMP_TO_REG(int temp)
+static u8 TEMP_TO_REG(long temp)
 {
        int ntemp = SENSORS_LIMIT(temp, ASB100_TEMP_MIN, ASB100_TEMP_MAX);
        ntemp += (ntemp<0 ? -500 : 500);
@@ -182,7 +182,7 @@ static u8 DIV_TO_REG(long val)
    dynamically allocated, at the same time the client itself is allocated. */
 struct asb100_data {
        struct i2c_client client;
-       struct class_device *class_dev;
+       struct device *hwmon_dev;
        struct mutex lock;
        enum chips type;
 
@@ -448,7 +448,7 @@ static ssize_t set_##reg(struct device *dev, const char *buf, \
 { \
        struct i2c_client *client = to_i2c_client(dev); \
        struct asb100_data *data = i2c_get_clientdata(client); \
-       unsigned long val = simple_strtoul(buf, NULL, 10); \
+       long val = simple_strtol(buf, NULL, 10); \
  \
        mutex_lock(&data->update_lock); \
        switch (nr) { \
@@ -514,7 +514,7 @@ static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
 /* VRM */
 static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf)
 {
-       struct asb100_data *data = asb100_update_device(dev);
+       struct asb100_data *data = dev_get_drvdata(dev);
        return sprintf(buf, "%d\n", data->vrm);
 }
 
@@ -844,9 +844,9 @@ static int asb100_detect(struct i2c_adapter *adapter, int address, int kind)
        if ((err = sysfs_create_group(&new_client->dev.kobj, &asb100_group)))
                goto ERROR3;
 
-       data->class_dev = hwmon_device_register(&new_client->dev);
-       if (IS_ERR(data->class_dev)) {
-               err = PTR_ERR(data->class_dev);
+       data->hwmon_dev = hwmon_device_register(&new_client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
                goto ERROR4;
        }
 
@@ -874,7 +874,7 @@ static int asb100_detach_client(struct i2c_client *client)
 
        /* main client */
        if (data) {
-               hwmon_device_unregister(data->class_dev);
+               hwmon_device_unregister(data->hwmon_dev);
                sysfs_remove_group(&client->dev.kobj, &asb100_group);
        }
 
index 0ccdd07..cce3350 100644 (file)
@@ -61,7 +61,7 @@ static struct i2c_driver atxp1_driver = {
 
 struct atxp1_data {
        struct i2c_client client;
-       struct class_device *class_dev;
+       struct device *hwmon_dev;
        struct mutex update_lock;
        unsigned long last_updated;
        u8 valid;
@@ -335,9 +335,9 @@ static int atxp1_detect(struct i2c_adapter *adapter, int address, int kind)
        if ((err = sysfs_create_group(&new_client->dev.kobj, &atxp1_group)))
                goto exit_detach;
 
-       data->class_dev = hwmon_device_register(&new_client->dev);
-       if (IS_ERR(data->class_dev)) {
-               err = PTR_ERR(data->class_dev);
+       data->hwmon_dev = hwmon_device_register(&new_client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
                goto exit_remove_files;
        }
 
@@ -361,7 +361,7 @@ static int atxp1_detach_client(struct i2c_client * client)
        struct atxp1_data * data = i2c_get_clientdata(client);
        int err;
 
-       hwmon_device_unregister(data->class_dev);
+       hwmon_device_unregister(data->hwmon_dev);
        sysfs_remove_group(&client->dev.kobj, &atxp1_group);
 
        err = i2c_detach_client(client);
index 7c17952..6f66551 100644 (file)
@@ -47,7 +47,7 @@ typedef enum { SHOW_TEMP, SHOW_TJMAX, SHOW_LABEL, SHOW_NAME } SHOW;
 static struct coretemp_data *coretemp_update_device(struct device *dev);
 
 struct coretemp_data {
-       struct class_device *class_dev;
+       struct device *hwmon_dev;
        struct mutex update_lock;
        const char *name;
        u32 id;
@@ -58,8 +58,6 @@ struct coretemp_data {
        u8 alarm;
 };
 
-static struct coretemp_data *coretemp_update_device(struct device *dev);
-
 /*
  * Sysfs stuff
  */
@@ -228,9 +226,9 @@ static int __devinit coretemp_probe(struct platform_device *pdev)
        if ((err = sysfs_create_group(&pdev->dev.kobj, &coretemp_group)))
                goto exit_free;
 
-       data->class_dev = hwmon_device_register(&pdev->dev);
-       if (IS_ERR(data->class_dev)) {
-               err = PTR_ERR(data->class_dev);
+       data->hwmon_dev = hwmon_device_register(&pdev->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
                dev_err(&pdev->dev, "Class registration failed (%d)\n",
                        err);
                goto exit_class;
@@ -250,7 +248,7 @@ static int __devexit coretemp_remove(struct platform_device *pdev)
 {
        struct coretemp_data *data = platform_get_drvdata(pdev);
 
-       hwmon_device_unregister(data->class_dev);
+       hwmon_device_unregister(data->hwmon_dev);
        sysfs_remove_group(&pdev->dev.kobj, &coretemp_group);
        platform_set_drvdata(pdev, NULL);
        kfree(data);
@@ -350,7 +348,7 @@ static int coretemp_cpu_callback(struct notifier_block *nfb,
        return NOTIFY_OK;
 }
 
-static struct notifier_block __cpuinitdata coretemp_cpu_notifier = {
+static struct notifier_block coretemp_cpu_notifier = {
        .notifier_call = coretemp_cpu_callback,
 };
 #endif                         /* !CONFIG_HOTPLUG_CPU */
@@ -371,9 +369,10 @@ static int __init coretemp_init(void)
        for_each_online_cpu(i) {
                struct cpuinfo_x86 *c = &(cpu_data)[i];
 
-               /* check if family 6, models e, f */
+               /* check if family 6, models e, f, 16 */
                if ((c->cpuid_level < 0) || (c->x86 != 0x6) ||
-                   !((c->x86_model == 0xe) || (c->x86_model == 0xf))) {
+                   !((c->x86_model == 0xe) || (c->x86_model == 0xf) ||
+                       (c->x86_model == 0x16))) {
 
                        /* supported CPU not found, but report the unknown
                           family 6 CPU */
index e9cbc72..a878c98 100644 (file)
@@ -1,12 +1,12 @@
 /*
- * dme1737.c - driver for the SMSC DME1737 and Asus A8000 Super-I/O chips
- *             integrated hardware monitoring features.
+ * dme1737.c - Driver for the SMSC DME1737, Asus A8000, and SMSC SCH311x
+ *             Super-I/O chips integrated hardware monitoring features.
  * Copyright (c) 2007 Juerg Haefliger <juergh@gmail.com>
  *
- * This driver is based on the LM85 driver. The hardware monitoring
- * capabilities of the DME1737 are very similar to the LM85 with some
- * additional features. Even though the DME1737 is a Super-I/O chip, the
- * hardware monitoring registers are only accessible via SMBus.
+ * This driver is an I2C/ISA hybrid, meaning that it uses the I2C bus to access
+ * the chip registers if a DME1737 (or A8000) is found and the ISA bus if a
+ * SCH311x chip is found. Both types of chips have very similar hardware
+ * monitoring capabilities but differ in the way they can be accessed.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -28,6 +28,7 @@
 #include <linux/slab.h>
 #include <linux/jiffies.h>
 #include <linux/i2c.h>
+#include <linux/platform_device.h>
 #include <linux/hwmon.h>
 #include <linux/hwmon-sysfs.h>
 #include <linux/hwmon-vid.h>
@@ -35,6 +36,9 @@
 #include <linux/mutex.h>
 #include <asm/io.h>
 
+/* ISA device, if found */
+static struct platform_device *pdev;
+
 /* Module load parameters */
 static int force_start;
 module_param(force_start, bool, 0);
@@ -133,6 +137,7 @@ static const u8 DME1737_BIT_ALARM_TEMP[] = {4, 5, 6};
 static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23};
 
 /* Miscellaneous registers */
+#define DME1737_REG_DEVICE             0x3d
 #define DME1737_REG_COMPANY            0x3e
 #define DME1737_REG_VERSTEP            0x3f
 #define DME1737_REG_CONFIG             0x40
@@ -148,14 +153,20 @@ static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23};
 #define DME1737_COMPANY_SMSC   0x5c
 #define DME1737_VERSTEP                0x88
 #define DME1737_VERSTEP_MASK   0xf8
+#define SCH311X_DEVICE         0x8c
+
+/* Length of ISA address segment */
+#define DME1737_EXTENT 2
 
 /* ---------------------------------------------------------------------
  * Data structures and manipulation thereof
  * --------------------------------------------------------------------- */
 
+/* For ISA chips, we abuse the i2c_client addr and name fields. We also use
+   the driver field to differentiate between I2C and ISA chips. */
 struct dme1737_data {
        struct i2c_client client;
-       struct class_device *class_dev;
+       struct device *hwmon_dev;
 
        struct mutex update_lock;
        int valid;                      /* !=0 if following fields are valid */
@@ -465,27 +476,48 @@ static inline int PWM_OFF_TO_REG(int val, int ix, int reg)
 
 /* ---------------------------------------------------------------------
  * Device I/O access
+ *
+ * ISA access is performed through an index/data register pair and needs to
+ * be protected by a mutex during runtime (not required for initialization).
+ * We use data->update_lock for this and need to ensure that we acquire it
+ * before calling dme1737_read or dme1737_write.
  * --------------------------------------------------------------------- */
 
 static u8 dme1737_read(struct i2c_client *client, u8 reg)
 {
-       s32 val = i2c_smbus_read_byte_data(client, reg);
+       s32 val;
+
+       if (client->driver) { /* I2C device */
+               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);
+               if (val < 0) {
+                       dev_warn(&client->dev, "Read from register "
+                                "0x%02x failed! Please report to the driver "
+                                "maintainer.\n", reg);
+               }
+       } else { /* ISA device */
+               outb(reg, client->addr);
+               val = inb(client->addr + 1);
        }
 
        return val;
 }
 
-static s32 dme1737_write(struct i2c_client *client, u8 reg, u8 value)
+static s32 dme1737_write(struct i2c_client *client, u8 reg, u8 val)
 {
-       s32 res = i2c_smbus_write_byte_data(client, reg, value);
+       s32 res = 0;
+
+       if (client->driver) { /* I2C device */
+               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);
+               if (res < 0) {
+                       dev_warn(&client->dev, "Write to register "
+                                "0x%02x failed! Please report to the driver "
+                                "maintainer.\n", reg);
+               }
+       } else { /* ISA device */
+               outb(reg, client->addr);
+               outb(val, client->addr + 1);
        }
 
        return res;
@@ -493,8 +525,8 @@ static s32 dme1737_write(struct i2c_client *client, u8 reg, u8 value)
 
 static struct dme1737_data *dme1737_update_device(struct device *dev)
 {
-       struct i2c_client *client = to_i2c_client(dev);
-       struct dme1737_data *data = i2c_get_clientdata(client);
+       struct dme1737_data *data = dev_get_drvdata(dev);
+       struct i2c_client *client = &data->client;
        int ix;
        u8 lsb[5];
 
@@ -630,6 +662,24 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
                                                DME1737_REG_ALARM3) << 16;
                }
 
+               /* The ISA chips require explicit clearing of alarm bits.
+                * Don't worry, an alarm will come back if the condition
+                * that causes it still exists */
+               if (!client->driver) {
+                       if (data->alarms & 0xff0000) {
+                               dme1737_write(client, DME1737_REG_ALARM3,
+                                             0xff);
+                       }
+                       if (data->alarms & 0xff00) {
+                               dme1737_write(client, DME1737_REG_ALARM2,
+                                             0xff);
+                       }
+                       if (data->alarms & 0xff) {
+                               dme1737_write(client, DME1737_REG_ALARM1,
+                                             0xff);
+                       }
+               }
+
                data->last_update = jiffies;
                data->valid = 1;
        }
@@ -674,7 +724,7 @@ static ssize_t show_in(struct device *dev, struct device_attribute *attr,
                break;
        default:
                res = 0;
-               dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+               dev_dbg(dev, "Unknown function %d.\n", fn);
        }
 
        return sprintf(buf, "%d\n", res);
@@ -683,8 +733,8 @@ static ssize_t show_in(struct device *dev, struct device_attribute *attr,
 static ssize_t set_in(struct device *dev, struct device_attribute *attr,
                      const char *buf, size_t count)
 {
-       struct i2c_client *client = to_i2c_client(dev);
-       struct dme1737_data *data = i2c_get_clientdata(client);
+       struct dme1737_data *data = dev_get_drvdata(dev);
+       struct i2c_client *client = &data->client;
        struct sensor_device_attribute_2
                *sensor_attr_2 = to_sensor_dev_attr_2(attr);
        int ix = sensor_attr_2->index;
@@ -704,7 +754,7 @@ static ssize_t set_in(struct device *dev, struct device_attribute *attr,
                              data->in_max[ix]);
                break;
        default:
-               dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+               dev_dbg(dev, "Unknown function %d.\n", fn);
        }
        mutex_unlock(&data->update_lock);
 
@@ -754,7 +804,7 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
                break;
        default:
                res = 0;
-               dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+               dev_dbg(dev, "Unknown function %d.\n", fn);
        }
 
        return sprintf(buf, "%d\n", res);
@@ -763,8 +813,8 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
 static ssize_t set_temp(struct device *dev, struct device_attribute *attr,
                        const char *buf, size_t count)
 {
-       struct i2c_client *client = to_i2c_client(dev);
-       struct dme1737_data *data = i2c_get_clientdata(client);
+       struct dme1737_data *data = dev_get_drvdata(dev);
+       struct i2c_client *client = &data->client;
        struct sensor_device_attribute_2
                *sensor_attr_2 = to_sensor_dev_attr_2(attr);
        int ix = sensor_attr_2->index;
@@ -789,7 +839,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *attr,
                              data->temp_offset[ix]);
                break;
        default:
-               dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+               dev_dbg(dev, "Unknown function %d.\n", fn);
        }
        mutex_unlock(&data->update_lock);
 
@@ -843,7 +893,7 @@ static ssize_t show_zone(struct device *dev, struct device_attribute *attr,
                break;
        default:
                res = 0;
-               dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+               dev_dbg(dev, "Unknown function %d.\n", fn);
        }
 
        return sprintf(buf, "%d\n", res);
@@ -852,8 +902,8 @@ static ssize_t show_zone(struct device *dev, struct device_attribute *attr,
 static ssize_t set_zone(struct device *dev, struct device_attribute *attr,
                        const char *buf, size_t count)
 {
-       struct i2c_client *client = to_i2c_client(dev);
-       struct dme1737_data *data = i2c_get_clientdata(client);
+       struct dme1737_data *data = dev_get_drvdata(dev);
+       struct i2c_client *client = &data->client;
        struct sensor_device_attribute_2
                *sensor_attr_2 = to_sensor_dev_attr_2(attr);
        int ix = sensor_attr_2->index;
@@ -898,7 +948,7 @@ static ssize_t set_zone(struct device *dev, struct device_attribute *attr,
                              data->zone_abs[ix]);
                break;
        default:
-               dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+               dev_dbg(dev, "Unknown function %d.\n", fn);
        }
        mutex_unlock(&data->update_lock);
 
@@ -950,7 +1000,7 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *attr,
                break;
        default:
                res = 0;
-               dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+               dev_dbg(dev, "Unknown function %d.\n", fn);
        }
 
        return sprintf(buf, "%d\n", res);
@@ -959,8 +1009,8 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *attr,
 static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
                       const char *buf, size_t count)
 {
-       struct i2c_client *client = to_i2c_client(dev);
-       struct dme1737_data *data = i2c_get_clientdata(client);
+       struct dme1737_data *data = dev_get_drvdata(dev);
+       struct i2c_client *client = &data->client;
        struct sensor_device_attribute_2
                *sensor_attr_2 = to_sensor_dev_attr_2(attr);
        int ix = sensor_attr_2->index;
@@ -995,7 +1045,7 @@ 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(&client->dev, "Fan type value %ld not "
+                       dev_warn(dev, "Fan type value %ld not "
                                 "supported. Choose one of 1, 2, or 4.\n",
                                 val);
                        goto exit;
@@ -1006,7 +1056,7 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
                              data->fan_opt[ix]);
                break;
        default:
-               dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+               dev_dbg(dev, "Unknown function %d.\n", fn);
        }
 exit:
        mutex_unlock(&data->update_lock);
@@ -1086,20 +1136,20 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr,
                break;
        default:
                res = 0;
-               dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+               dev_dbg(dev, "Unknown function %d.\n", fn);
        }
 
        return sprintf(buf, "%d\n", res);
 }
 
 static struct attribute *dme1737_attr_pwm[];
-static void dme1737_chmod_file(struct i2c_client*, struct attribute*, mode_t);
+static void dme1737_chmod_file(struct device*, struct attribute*, mode_t);
 
 static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
                       const char *buf, size_t count)
 {
-       struct i2c_client *client = to_i2c_client(dev);
-       struct dme1737_data *data = i2c_get_clientdata(client);
+       struct dme1737_data *data = dev_get_drvdata(dev);
+       struct i2c_client *client = &data->client;
        struct sensor_device_attribute_2
                *sensor_attr_2 = to_sensor_dev_attr_2(attr);
        int ix = sensor_attr_2->index;
@@ -1122,7 +1172,7 @@ 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(&client->dev, "PWM enable %ld not "
+                       dev_warn(dev, "PWM enable %ld not "
                                 "supported. Choose one of 0, 1, or 2.\n",
                                 val);
                        goto exit;
@@ -1156,7 +1206,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
                switch (val) {
                case 0:
                        /* Change permissions of pwm[ix] to read-only */
-                       dme1737_chmod_file(client, dme1737_attr_pwm[ix],
+                       dme1737_chmod_file(dev, dme1737_attr_pwm[ix],
                                           S_IRUGO);
                        /* Turn fan fully on */
                        data->pwm_config[ix] = PWM_EN_TO_REG(0,
@@ -1171,12 +1221,12 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
                        dme1737_write(client, DME1737_REG_PWM_CONFIG(ix),
                                      data->pwm_config[ix]);
                        /* Change permissions of pwm[ix] to read-writeable */
-                       dme1737_chmod_file(client, dme1737_attr_pwm[ix],
+                       dme1737_chmod_file(dev, dme1737_attr_pwm[ix],
                                           S_IRUGO | S_IWUSR);
                        break;
                case 2:
                        /* Change permissions of pwm[ix] to read-only */
-                       dme1737_chmod_file(client, dme1737_attr_pwm[ix],
+                       dme1737_chmod_file(dev, dme1737_attr_pwm[ix],
                                           S_IRUGO);
                        /* Turn on auto mode using the saved zone channel
                         * assignment */
@@ -1223,7 +1273,7 @@ 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(&client->dev, "PWM auto channels zone %ld "
+                       dev_warn(dev, "PWM auto channels zone %ld "
                                 "not supported. Choose one of 1, 2, 4, 6, "
                                 "or 7.\n", val);
                        goto exit;
@@ -1257,12 +1307,10 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
                        data->pwm_rr[0] = PWM_OFF_TO_REG(1, ix,
                                                dme1737_read(client,
                                                DME1737_REG_PWM_RR(0)));
-
                } else {
                        data->pwm_rr[0] = PWM_OFF_TO_REG(0, ix,
                                                dme1737_read(client,
                                                DME1737_REG_PWM_RR(0)));
-
                }
                dme1737_write(client, DME1737_REG_PWM_RR(0),
                              data->pwm_rr[0]);
@@ -1274,7 +1322,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
                              data->pwm_min[ix]);
                break;
        default:
-               dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+               dev_dbg(dev, "Unknown function %d.\n", fn);
        }
 exit:
        mutex_unlock(&data->update_lock);
@@ -1298,8 +1346,7 @@ static ssize_t show_vrm(struct device *dev, struct device_attribute *attr,
 static ssize_t set_vrm(struct device *dev, struct device_attribute *attr,
                       const char *buf, size_t count)
 {
-       struct i2c_client *client = to_i2c_client(dev);
-       struct dme1737_data *data = i2c_get_clientdata(client);
+       struct dme1737_data *data = dev_get_drvdata(dev);
        long val = simple_strtol(buf, NULL, 10);
 
        data->vrm = val;
@@ -1314,6 +1361,14 @@ static ssize_t show_vid(struct device *dev, struct device_attribute *attr,
        return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
 }
 
+static ssize_t show_name(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       struct dme1737_data *data = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%s\n", data->client.name);
+}
+
 /* ---------------------------------------------------------------------
  * Sysfs device attribute defines and structs
  * --------------------------------------------------------------------- */
@@ -1322,13 +1377,13 @@ static ssize_t show_vid(struct device *dev, struct device_attribute *attr,
 
 #define SENSOR_DEVICE_ATTR_IN(ix) \
 static SENSOR_DEVICE_ATTR_2(in##ix##_input, S_IRUGO, \
-        show_in, NULL, SYS_IN_INPUT, ix); \
+       show_in, NULL, SYS_IN_INPUT, ix); \
 static SENSOR_DEVICE_ATTR_2(in##ix##_min, S_IRUGO | S_IWUSR, \
-        show_in, set_in, SYS_IN_MIN, ix); \
+       show_in, set_in, SYS_IN_MIN, ix); \
 static SENSOR_DEVICE_ATTR_2(in##ix##_max, S_IRUGO | S_IWUSR, \
-        show_in, set_in, SYS_IN_MAX, ix); \
+       show_in, set_in, SYS_IN_MAX, ix); \
 static SENSOR_DEVICE_ATTR_2(in##ix##_alarm, S_IRUGO, \
-        show_in, NULL, SYS_IN_ALARM, ix)
+       show_in, NULL, SYS_IN_ALARM, ix)
 
 SENSOR_DEVICE_ATTR_IN(0);
 SENSOR_DEVICE_ATTR_IN(1);
@@ -1342,17 +1397,17 @@ SENSOR_DEVICE_ATTR_IN(6);
 
 #define SENSOR_DEVICE_ATTR_TEMP(ix) \
 static SENSOR_DEVICE_ATTR_2(temp##ix##_input, S_IRUGO, \
-        show_temp, NULL, SYS_TEMP_INPUT, ix-1); \
+       show_temp, NULL, SYS_TEMP_INPUT, ix-1); \
 static SENSOR_DEVICE_ATTR_2(temp##ix##_min, S_IRUGO | S_IWUSR, \
-        show_temp, set_temp, SYS_TEMP_MIN, ix-1); \
+       show_temp, set_temp, SYS_TEMP_MIN, ix-1); \
 static SENSOR_DEVICE_ATTR_2(temp##ix##_max, S_IRUGO | S_IWUSR, \
-        show_temp, set_temp, SYS_TEMP_MAX, ix-1); \
+       show_temp, set_temp, SYS_TEMP_MAX, ix-1); \
 static SENSOR_DEVICE_ATTR_2(temp##ix##_offset, S_IRUGO, \
-        show_temp, set_temp, SYS_TEMP_OFFSET, ix-1); \
+       show_temp, set_temp, SYS_TEMP_OFFSET, ix-1); \
 static SENSOR_DEVICE_ATTR_2(temp##ix##_alarm, S_IRUGO, \
-        show_temp, NULL, SYS_TEMP_ALARM, ix-1); \
+       show_temp, NULL, SYS_TEMP_ALARM, ix-1); \
 static SENSOR_DEVICE_ATTR_2(temp##ix##_fault, S_IRUGO, \
-        show_temp, NULL, SYS_TEMP_FAULT, ix-1)
+       show_temp, NULL, SYS_TEMP_FAULT, ix-1)
 
 SENSOR_DEVICE_ATTR_TEMP(1);
 SENSOR_DEVICE_ATTR_TEMP(2);
@@ -1362,15 +1417,15 @@ SENSOR_DEVICE_ATTR_TEMP(3);
 
 #define SENSOR_DEVICE_ATTR_ZONE(ix) \
 static SENSOR_DEVICE_ATTR_2(zone##ix##_auto_channels_temp, S_IRUGO, \
-        show_zone, NULL, SYS_ZONE_AUTO_CHANNELS_TEMP, ix-1); \
+       show_zone, NULL, SYS_ZONE_AUTO_CHANNELS_TEMP, ix-1); \
 static SENSOR_DEVICE_ATTR_2(zone##ix##_auto_point1_temp_hyst, S_IRUGO, \
-        show_zone, set_zone, SYS_ZONE_AUTO_POINT1_TEMP_HYST, ix-1); \
+       show_zone, set_zone, SYS_ZONE_AUTO_POINT1_TEMP_HYST, ix-1); \
 static SENSOR_DEVICE_ATTR_2(zone##ix##_auto_point1_temp, S_IRUGO, \
-        show_zone, set_zone, SYS_ZONE_AUTO_POINT1_TEMP, ix-1); \
+       show_zone, set_zone, SYS_ZONE_AUTO_POINT1_TEMP, ix-1); \
 static SENSOR_DEVICE_ATTR_2(zone##ix##_auto_point2_temp, S_IRUGO, \
-        show_zone, set_zone, SYS_ZONE_AUTO_POINT2_TEMP, ix-1); \
+       show_zone, set_zone, SYS_ZONE_AUTO_POINT2_TEMP, ix-1); \
 static SENSOR_DEVICE_ATTR_2(zone##ix##_auto_point3_temp, S_IRUGO, \
-        show_zone, set_zone, SYS_ZONE_AUTO_POINT3_TEMP, ix-1)
+       show_zone, set_zone, SYS_ZONE_AUTO_POINT3_TEMP, ix-1)
 
 SENSOR_DEVICE_ATTR_ZONE(1);
 SENSOR_DEVICE_ATTR_ZONE(2);
@@ -1380,13 +1435,13 @@ SENSOR_DEVICE_ATTR_ZONE(3);
 
 #define SENSOR_DEVICE_ATTR_FAN_1TO4(ix) \
 static SENSOR_DEVICE_ATTR_2(fan##ix##_input, S_IRUGO, \
-        show_fan, NULL, SYS_FAN_INPUT, ix-1); \
+       show_fan, NULL, SYS_FAN_INPUT, ix-1); \
 static SENSOR_DEVICE_ATTR_2(fan##ix##_min, S_IRUGO | S_IWUSR, \
-        show_fan, set_fan, SYS_FAN_MIN, ix-1); \
+       show_fan, set_fan, SYS_FAN_MIN, ix-1); \
 static SENSOR_DEVICE_ATTR_2(fan##ix##_alarm, S_IRUGO, \
-        show_fan, NULL, SYS_FAN_ALARM, ix-1); \
+       show_fan, NULL, SYS_FAN_ALARM, ix-1); \
 static SENSOR_DEVICE_ATTR_2(fan##ix##_type, S_IRUGO | S_IWUSR, \
-        show_fan, set_fan, SYS_FAN_TYPE, ix-1)
+       show_fan, set_fan, SYS_FAN_TYPE, ix-1)
 
 SENSOR_DEVICE_ATTR_FAN_1TO4(1);
 SENSOR_DEVICE_ATTR_FAN_1TO4(2);
@@ -1397,13 +1452,13 @@ SENSOR_DEVICE_ATTR_FAN_1TO4(4);
 
 #define SENSOR_DEVICE_ATTR_FAN_5TO6(ix) \
 static SENSOR_DEVICE_ATTR_2(fan##ix##_input, S_IRUGO, \
-        show_fan, NULL, SYS_FAN_INPUT, ix-1); \
+       show_fan, NULL, SYS_FAN_INPUT, ix-1); \
 static SENSOR_DEVICE_ATTR_2(fan##ix##_min, S_IRUGO | S_IWUSR, \
-        show_fan, set_fan, SYS_FAN_MIN, ix-1); \
+       show_fan, set_fan, SYS_FAN_MIN, ix-1); \
 static SENSOR_DEVICE_ATTR_2(fan##ix##_alarm, S_IRUGO, \
-        show_fan, NULL, SYS_FAN_ALARM, ix-1); \
+       show_fan, NULL, SYS_FAN_ALARM, ix-1); \
 static SENSOR_DEVICE_ATTR_2(fan##ix##_max, S_IRUGO | S_IWUSR, \
-        show_fan, set_fan, SYS_FAN_MAX, ix-1)
+       show_fan, set_fan, SYS_FAN_MAX, ix-1)
 
 SENSOR_DEVICE_ATTR_FAN_5TO6(5);
 SENSOR_DEVICE_ATTR_FAN_5TO6(6);
@@ -1412,21 +1467,21 @@ SENSOR_DEVICE_ATTR_FAN_5TO6(6);
 
 #define SENSOR_DEVICE_ATTR_PWM_1TO3(ix) \
 static SENSOR_DEVICE_ATTR_2(pwm##ix, S_IRUGO, \
-        show_pwm, set_pwm, SYS_PWM, ix-1); \
+       show_pwm, set_pwm, SYS_PWM, ix-1); \
 static SENSOR_DEVICE_ATTR_2(pwm##ix##_freq, S_IRUGO, \
-        show_pwm, set_pwm, SYS_PWM_FREQ, ix-1); \
+       show_pwm, set_pwm, SYS_PWM_FREQ, ix-1); \
 static SENSOR_DEVICE_ATTR_2(pwm##ix##_enable, S_IRUGO, \
-        show_pwm, set_pwm, SYS_PWM_ENABLE, ix-1); \
+       show_pwm, set_pwm, SYS_PWM_ENABLE, ix-1); \
 static SENSOR_DEVICE_ATTR_2(pwm##ix##_ramp_rate, S_IRUGO, \
-        show_pwm, set_pwm, SYS_PWM_RAMP_RATE, ix-1); \
+       show_pwm, set_pwm, SYS_PWM_RAMP_RATE, ix-1); \
 static SENSOR_DEVICE_ATTR_2(pwm##ix##_auto_channels_zone, S_IRUGO, \
-        show_pwm, set_pwm, SYS_PWM_AUTO_CHANNELS_ZONE, ix-1); \
+       show_pwm, set_pwm, SYS_PWM_AUTO_CHANNELS_ZONE, ix-1); \
 static SENSOR_DEVICE_ATTR_2(pwm##ix##_auto_pwm_min, S_IRUGO, \
-        show_pwm, set_pwm, SYS_PWM_AUTO_PWM_MIN, ix-1); \
+       show_pwm, set_pwm, SYS_PWM_AUTO_PWM_MIN, ix-1); \
 static SENSOR_DEVICE_ATTR_2(pwm##ix##_auto_point1_pwm, S_IRUGO, \
-        show_pwm, set_pwm, SYS_PWM_AUTO_POINT1_PWM, ix-1); \
+       show_pwm, set_pwm, SYS_PWM_AUTO_POINT1_PWM, ix-1); \
 static SENSOR_DEVICE_ATTR_2(pwm##ix##_auto_point2_pwm, S_IRUGO, \
-        show_pwm, NULL, SYS_PWM_AUTO_POINT2_PWM, ix-1)
+       show_pwm, NULL, SYS_PWM_AUTO_POINT2_PWM, ix-1)
 
 SENSOR_DEVICE_ATTR_PWM_1TO3(1);
 SENSOR_DEVICE_ATTR_PWM_1TO3(2);
@@ -1436,11 +1491,11 @@ SENSOR_DEVICE_ATTR_PWM_1TO3(3);
 
 #define SENSOR_DEVICE_ATTR_PWM_5TO6(ix) \
 static SENSOR_DEVICE_ATTR_2(pwm##ix, S_IRUGO | S_IWUSR, \
-        show_pwm, set_pwm, SYS_PWM, ix-1); \
+       show_pwm, set_pwm, SYS_PWM, ix-1); \
 static SENSOR_DEVICE_ATTR_2(pwm##ix##_freq, S_IRUGO | S_IWUSR, \
-        show_pwm, set_pwm, SYS_PWM_FREQ, ix-1); \
+       show_pwm, set_pwm, SYS_PWM_FREQ, ix-1); \
 static SENSOR_DEVICE_ATTR_2(pwm##ix##_enable, S_IRUGO, \
-        show_pwm, NULL, SYS_PWM_ENABLE, ix-1)
+       show_pwm, NULL, SYS_PWM_ENABLE, ix-1)
 
 SENSOR_DEVICE_ATTR_PWM_5TO6(5);
 SENSOR_DEVICE_ATTR_PWM_5TO6(6);
@@ -1449,6 +1504,7 @@ SENSOR_DEVICE_ATTR_PWM_5TO6(6);
 
 static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
 static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);   /* for ISA devices */
 
 #define SENSOR_DEV_ATTR_IN(ix) \
 &sensor_dev_attr_in##ix##_input.dev_attr.attr, \
@@ -1519,53 +1575,53 @@ SENSOR_DEV_ATTR_PWM_5TO6_LOCK(ix), \
  * permissions are created read-only and write permissions are added or removed
  * on the fly when required */
 static struct attribute *dme1737_attr[] ={
-        /* Voltages */
-        SENSOR_DEV_ATTR_IN(0),
-        SENSOR_DEV_ATTR_IN(1),
-        SENSOR_DEV_ATTR_IN(2),
-        SENSOR_DEV_ATTR_IN(3),
-        SENSOR_DEV_ATTR_IN(4),
-        SENSOR_DEV_ATTR_IN(5),
-        SENSOR_DEV_ATTR_IN(6),
-        /* Temperatures */
-        SENSOR_DEV_ATTR_TEMP(1),
-        SENSOR_DEV_ATTR_TEMP(2),
-        SENSOR_DEV_ATTR_TEMP(3),
-        /* Zones */
-        SENSOR_DEV_ATTR_ZONE(1),
-        SENSOR_DEV_ATTR_ZONE(2),
-        SENSOR_DEV_ATTR_ZONE(3),
-        /* Misc */
-        &dev_attr_vrm.attr,
-        &dev_attr_cpu0_vid.attr,
+       /* Voltages */
+       SENSOR_DEV_ATTR_IN(0),
+       SENSOR_DEV_ATTR_IN(1),
+       SENSOR_DEV_ATTR_IN(2),
+       SENSOR_DEV_ATTR_IN(3),
+       SENSOR_DEV_ATTR_IN(4),
+       SENSOR_DEV_ATTR_IN(5),
+       SENSOR_DEV_ATTR_IN(6),
+       /* Temperatures */
+       SENSOR_DEV_ATTR_TEMP(1),
+       SENSOR_DEV_ATTR_TEMP(2),
+       SENSOR_DEV_ATTR_TEMP(3),
+       /* Zones */
+       SENSOR_DEV_ATTR_ZONE(1),
+       SENSOR_DEV_ATTR_ZONE(2),
+       SENSOR_DEV_ATTR_ZONE(3),
+       /* Misc */
+       &dev_attr_vrm.attr,
+       &dev_attr_cpu0_vid.attr,
        NULL
 };
 
 static const struct attribute_group dme1737_group = {
-        .attrs = dme1737_attr,
+       .attrs = dme1737_attr,
 };
 
 /* The following structs hold the PWM attributes, some of which are optional.
  * Their creation depends on the chip configuration which is determined during
  * module load. */
 static struct attribute *dme1737_attr_pwm1[] = {
-        SENSOR_DEV_ATTR_PWM_1TO3(1),
+       SENSOR_DEV_ATTR_PWM_1TO3(1),
        NULL
 };
 static struct attribute *dme1737_attr_pwm2[] = {
-        SENSOR_DEV_ATTR_PWM_1TO3(2),
+       SENSOR_DEV_ATTR_PWM_1TO3(2),
        NULL
 };
 static struct attribute *dme1737_attr_pwm3[] = {
-        SENSOR_DEV_ATTR_PWM_1TO3(3),
+       SENSOR_DEV_ATTR_PWM_1TO3(3),
        NULL
 };
 static struct attribute *dme1737_attr_pwm5[] = {
-        SENSOR_DEV_ATTR_PWM_5TO6(5),
+       SENSOR_DEV_ATTR_PWM_5TO6(5),
        NULL
 };
 static struct attribute *dme1737_attr_pwm6[] = {
-        SENSOR_DEV_ATTR_PWM_5TO6(6),
+       SENSOR_DEV_ATTR_PWM_5TO6(6),
        NULL
 };
 
@@ -1582,27 +1638,27 @@ static const struct attribute_group dme1737_pwm_group[] = {
  * Their creation depends on the chip configuration which is determined during
  * module load. */
 static struct attribute *dme1737_attr_fan1[] = {
-        SENSOR_DEV_ATTR_FAN_1TO4(1),
+       SENSOR_DEV_ATTR_FAN_1TO4(1),
        NULL
 };
 static struct attribute *dme1737_attr_fan2[] = {
-        SENSOR_DEV_ATTR_FAN_1TO4(2),
+       SENSOR_DEV_ATTR_FAN_1TO4(2),
        NULL
 };
 static struct attribute *dme1737_attr_fan3[] = {
-        SENSOR_DEV_ATTR_FAN_1TO4(3),
+       SENSOR_DEV_ATTR_FAN_1TO4(3),
        NULL
 };
 static struct attribute *dme1737_attr_fan4[] = {
-        SENSOR_DEV_ATTR_FAN_1TO4(4),
+       SENSOR_DEV_ATTR_FAN_1TO4(4),
        NULL
 };
 static struct attribute *dme1737_attr_fan5[] = {
-        SENSOR_DEV_ATTR_FAN_5TO6(5),
+       SENSOR_DEV_ATTR_FAN_5TO6(5),
        NULL
 };
 static struct attribute *dme1737_attr_fan6[] = {
-        SENSOR_DEV_ATTR_FAN_5TO6(6),
+       SENSOR_DEV_ATTR_FAN_5TO6(6),
        NULL
 };
 
@@ -1637,23 +1693,23 @@ static const struct attribute_group dme1737_lock_group = {
  * writeable if the chip is *not* locked and the respective PWM is available.
  * Otherwise they stay read-only. */
 static struct attribute *dme1737_attr_pwm1_lock[] = {
-        SENSOR_DEV_ATTR_PWM_1TO3_LOCK(1),
+       SENSOR_DEV_ATTR_PWM_1TO3_LOCK(1),
        NULL
 };
 static struct attribute *dme1737_attr_pwm2_lock[] = {
-        SENSOR_DEV_ATTR_PWM_1TO3_LOCK(2),
+       SENSOR_DEV_ATTR_PWM_1TO3_LOCK(2),
        NULL
 };
 static struct attribute *dme1737_attr_pwm3_lock[] = {
-        SENSOR_DEV_ATTR_PWM_1TO3_LOCK(3),
+       SENSOR_DEV_ATTR_PWM_1TO3_LOCK(3),
        NULL
 };
 static struct attribute *dme1737_attr_pwm5_lock[] = {
-        SENSOR_DEV_ATTR_PWM_5TO6_LOCK(5),
+       SENSOR_DEV_ATTR_PWM_5TO6_LOCK(5),
        NULL
 };
 static struct attribute *dme1737_attr_pwm6_lock[] = {
-        SENSOR_DEV_ATTR_PWM_5TO6_LOCK(6),
+       SENSOR_DEV_ATTR_PWM_5TO6_LOCK(6),
        NULL
 };
 
@@ -1678,6 +1734,16 @@ static struct attribute *dme1737_attr_pwm[] = {
  * Super-IO functions
  * --------------------------------------------------------------------- */
 
+static inline void dme1737_sio_enter(int sio_cip)
+{
+       outb(0x55, sio_cip);
+}
+
+static inline void dme1737_sio_exit(int sio_cip)
+{
+       outb(0xaa, sio_cip);
+}
+
 static inline int dme1737_sio_inb(int sio_cip, int reg)
 {
        outb(reg, sio_cip);
@@ -1690,136 +1756,196 @@ static inline void dme1737_sio_outb(int sio_cip, int reg, int val)
        outb(val, sio_cip + 1);
 }
 
-static int dme1737_sio_get_features(int sio_cip, struct i2c_client *client)
+/* ---------------------------------------------------------------------
+ * Device initialization
+ * --------------------------------------------------------------------- */
+
+static int dme1737_i2c_get_features(int, struct dme1737_data*);
+
+static void dme1737_chmod_file(struct device *dev,
+                              struct attribute *attr, mode_t mode)
 {
-       struct dme1737_data *data = i2c_get_clientdata(client);
-       int err = 0, reg;
-       u16 addr;
+       if (sysfs_chmod_file(&dev->kobj, attr, mode)) {
+               dev_warn(dev, "Failed to change permissions of %s.\n",
+                        attr->name);
+       }
+}
 
-       /* Enter configuration mode */
-       outb(0x55, sio_cip);
+static void dme1737_chmod_group(struct device *dev,
+                               const struct attribute_group *group,
+                               mode_t mode)
+{
+       struct attribute **attr;
 
-       /* Check device ID
-        * The DME1737 can return either 0x78 or 0x77 as its device ID. */
-       reg = dme1737_sio_inb(sio_cip, 0x20);
-       if (!(reg == 0x77 || reg == 0x78)) {
-               err = -ENODEV;
-               goto exit;
+       for (attr = group->attrs; *attr; attr++) {
+               dme1737_chmod_file(dev, *attr, mode);
        }
+}
 
-       /* Select logical device A (runtime registers) */
-       dme1737_sio_outb(sio_cip, 0x07, 0x0a);
+static void dme1737_remove_files(struct device *dev)
+{
+       struct dme1737_data *data = dev_get_drvdata(dev);
+       int ix;
 
-       /* Get the base address of the runtime registers */
-       if (!(addr = (dme1737_sio_inb(sio_cip, 0x60) << 8) |
-                     dme1737_sio_inb(sio_cip, 0x61))) {
-               err = -ENODEV;
-               goto exit;
+       for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) {
+               if (data->has_fan & (1 << ix)) {
+                       sysfs_remove_group(&dev->kobj,
+                                          &dme1737_fan_group[ix]);
+               }
        }
 
-       /* Read the runtime registers to determine which optional features
-        * are enabled and available. Bits [3:2] of registers 0x43-0x46 are set
-        * to '10' if the respective feature is enabled. */
-       if ((inb(addr + 0x43) & 0x0c) == 0x08) { /* fan6 */
-               data->has_fan |= (1 << 5);
+       for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_group); ix++) {
+               if (data->has_pwm & (1 << ix)) {
+                       sysfs_remove_group(&dev->kobj,
+                                          &dme1737_pwm_group[ix]);
+               }
        }
-       if ((inb(addr + 0x44) & 0x0c) == 0x08) { /* pwm6 */
-               data->has_pwm |= (1 << 5);
+
+       sysfs_remove_group(&dev->kobj, &dme1737_group);
+
+       if (!data->client.driver) {
+               sysfs_remove_file(&dev->kobj, &dev_attr_name.attr);
        }
-       if ((inb(addr + 0x45) & 0x0c) == 0x08) { /* fan5 */
-               data->has_fan |= (1 << 4);
+}
+
+static int dme1737_create_files(struct device *dev)
+{
+       struct dme1737_data *data = dev_get_drvdata(dev);
+       int err, ix;
+
+       /* Create a name attribute for ISA devices */
+       if (!data->client.driver &&
+           (err = sysfs_create_file(&dev->kobj, &dev_attr_name.attr))) {
+               goto exit;
        }
-       if ((inb(addr + 0x46) & 0x0c) == 0x08) { /* pwm5 */
-               data->has_pwm |= (1 << 4);
+
+       /* Create standard sysfs attributes */
+       if ((err = sysfs_create_group(&dev->kobj, &dme1737_group))) {
+               goto exit_remove;
        }
 
-exit:
-       /* Exit configuration mode */
-       outb(0xaa, sio_cip);
+       /* Create fan sysfs attributes */
+       for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) {
+               if (data->has_fan & (1 << ix)) {
+                       if ((err = sysfs_create_group(&dev->kobj,
+                                               &dme1737_fan_group[ix]))) {
+                               goto exit_remove;
+                       }
+               }
+       }
 
-       return err;
-}
+       /* Create PWM sysfs attributes */
+       for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_group); ix++) {
+               if (data->has_pwm & (1 << ix)) {
+                       if ((err = sysfs_create_group(&dev->kobj,
+                                               &dme1737_pwm_group[ix]))) {
+                               goto exit_remove;
+                       }
+               }
+       }
 
-/* ---------------------------------------------------------------------
- * Device detection, registration and initialization
- * --------------------------------------------------------------------- */
+       /* Inform if the device is locked. Otherwise change the permissions of
+        * selected attributes from read-only to read-writeable. */
+       if (data->config & 0x02) {
+               dev_info(dev, "Device is locked. Some attributes "
+                        "will be read-only.\n");
+       } else {
+               /* Change permissions of standard attributes */
+               dme1737_chmod_group(dev, &dme1737_lock_group,
+                                   S_IRUGO | S_IWUSR);
 
-static struct i2c_driver dme1737_driver;
+               /* Change permissions of PWM attributes */
+               for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_lock_group); ix++) {
+                       if (data->has_pwm & (1 << ix)) {
+                               dme1737_chmod_group(dev,
+                                               &dme1737_pwm_lock_group[ix],
+                                               S_IRUGO | S_IWUSR);
+                       }
+               }
 
-static void dme1737_chmod_file(struct i2c_client *client,
-                              struct attribute *attr, mode_t mode)
-{
-       if (sysfs_chmod_file(&client->dev.kobj, attr, mode)) {
-               dev_warn(&client->dev, "Failed to change permissions of %s.\n",
-                        attr->name);
+               /* Change permissions of pwm[1-3] if in manual mode */
+               for (ix = 0; ix < 3; ix++) {
+                       if ((data->has_pwm & (1 << ix)) &&
+                           (PWM_EN_FROM_REG(data->pwm_config[ix]) == 1)) {
+                               dme1737_chmod_file(dev,
+                                               dme1737_attr_pwm[ix],
+                                               S_IRUGO | S_IWUSR);
+                       }
+               }
        }
-}
 
-static void dme1737_chmod_group(struct i2c_client *client,
-                               const struct attribute_group *group,
-                               mode_t mode)
-{
-       struct attribute **attr;
+       return 0;
 
-       for (attr = group->attrs; *attr; attr++) {
-               dme1737_chmod_file(client, *attr, mode);
-       }
+exit_remove:
+       dme1737_remove_files(dev);
+exit:
+       return err;
 }
 
-static int dme1737_init_client(struct i2c_client *client)
+static int dme1737_init_device(struct device *dev)
 {
-       struct dme1737_data *data = i2c_get_clientdata(client);
+       struct dme1737_data *data = dev_get_drvdata(dev);
+       struct i2c_client *client = &data->client;
        int ix;
        u8 reg;
 
-        data->config = dme1737_read(client, DME1737_REG_CONFIG);
-        /* Inform if part is not monitoring/started */
-        if (!(data->config & 0x01)) {
-                if (!force_start) {
-                        dev_err(&client->dev, "Device is not monitoring. "
-                                "Use the force_start load parameter to "
-                                "override.\n");
-                        return -EFAULT;
-                }
-
-                /* Force monitoring */
-                data->config |= 0x01;
-                dme1737_write(client, DME1737_REG_CONFIG, data->config);
-        }
+       data->config = dme1737_read(client, DME1737_REG_CONFIG);
+       /* 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");
+                       return -EFAULT;
+               }
+
+               /* Force monitoring */
+               data->config |= 0x01;
+               dme1737_write(client, DME1737_REG_CONFIG, data->config);
+       }
        /* Inform if part is not ready */
        if (!(data->config & 0x04)) {
-               dev_err(&client->dev, "Device is not ready.\n");
+               dev_err(dev, "Device is not ready.\n");
                return -EFAULT;
        }
 
-       data->config2 = dme1737_read(client, DME1737_REG_CONFIG2);
-       /* Check if optional fan3 input is enabled */
-       if (data->config2 & 0x04) {
-               data->has_fan |= (1 << 2);
-       }
+       /* Determine which optional fan and pwm features are enabled/present */
+       if (client->driver) {   /* I2C chip */
+               data->config2 = dme1737_read(client, DME1737_REG_CONFIG2);
+               /* Check if optional fan3 input is enabled */
+               if (data->config2 & 0x04) {
+                       data->has_fan |= (1 << 2);
+               }
 
-       /* Fan4 and pwm3 are only available if the client's I2C address
-        * is the default 0x2e. Otherwise the I/Os associated with these
-        * functions are used for addr enable/select. */
-       if (client->addr == 0x2e) {
-               data->has_fan |= (1 << 3);
-               data->has_pwm |= (1 << 2);
-       }
+               /* Fan4 and pwm3 are only available if the client's I2C address
+                * is the default 0x2e. Otherwise the I/Os associated with
+                * these functions are used for addr enable/select. */
+               if (data->client.addr == 0x2e) {
+                       data->has_fan |= (1 << 3);
+                       data->has_pwm |= (1 << 2);
+               }
 
-       /* Determine if the optional fan[5-6] and/or pwm[5-6] are enabled.
-        * For this, we need to query the runtime registers through the
-        * Super-IO LPC interface. Try both config ports 0x2e and 0x4e. */
-       if (dme1737_sio_get_features(0x2e, client) &&
-           dme1737_sio_get_features(0x4e, client)) {
-               dev_warn(&client->dev, "Failed to query Super-IO for optional "
-                        "features.\n");
+               /* Determine which of the optional fan[5-6] and pwm[5-6]
+                * features are enabled. For this, we need to query the runtime
+                * registers through the Super-IO LPC interface. Try both
+                * config ports 0x2e and 0x4e. */
+               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");
+               }
+       } else {   /* ISA chip */
+               /* Fan3 and pwm3 are always available. Fan[4-5] and pwm[5-6]
+                * don't exist in the ISA chip. */
+               data->has_fan |= (1 << 2);
+               data->has_pwm |= (1 << 2);
        }
 
        /* Fan1, fan2, pwm1, and pwm2 are always present */
        data->has_fan |= 0x03;
        data->has_pwm |= 0x03;
 
-       dev_info(&client->dev, "Optional features: pwm3=%s, pwm5=%s, pwm6=%s, "
+       dev_info(dev, "Optional features: pwm3=%s, pwm5=%s, pwm6=%s, "
                 "fan3=%s, fan4=%s, fan5=%s, fan6=%s.\n",
                 (data->has_pwm & (1 << 2)) ? "yes" : "no",
                 (data->has_pwm & (1 << 4)) ? "yes" : "no",
@@ -1831,13 +1957,19 @@ static int dme1737_init_client(struct i2c_client *client)
 
        reg = dme1737_read(client, DME1737_REG_TACH_PWM);
        /* Inform if fan-to-pwm mapping differs from the default */
-       if (reg != 0xa4) {
-               dev_warn(&client->dev, "Non-standard fan to pwm mapping: "
+       if (client->driver && 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",
                         (reg & 0x03) + 1, ((reg >> 2) & 0x03) + 1,
                         ((reg >> 4) & 0x03) + 1, ((reg >> 6) & 0x03) + 1);
+       } else if (!client->driver && 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",
+                        (reg & 0x03) + 1, ((reg >> 2) & 0x03) + 1,
+                        ((reg >> 4) & 0x03) + 1);
        }
 
        /* Switch pwm[1-3] to manual mode if they are currently disabled and
@@ -1849,7 +1981,7 @@ static int dme1737_init_client(struct i2c_client *client)
                                                DME1737_REG_PWM_CONFIG(ix));
                        if ((data->has_pwm & (1 << ix)) &&
                            (PWM_EN_FROM_REG(data->pwm_config[ix]) == -1)) {
-                               dev_info(&client->dev, "Switching pwm%d to "
+                               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]);
@@ -1872,13 +2004,67 @@ static int dme1737_init_client(struct i2c_client *client)
        return 0;
 }
 
-static int dme1737_detect(struct i2c_adapter *adapter, int address,
-                         int kind)
+/* ---------------------------------------------------------------------
+ * I2C device detection and registration
+ * --------------------------------------------------------------------- */
+
+static struct i2c_driver dme1737_i2c_driver;
+
+static int dme1737_i2c_get_features(int sio_cip, struct dme1737_data *data)
+{
+       int err = 0, reg;
+       u16 addr;
+
+       dme1737_sio_enter(sio_cip);
+
+       /* Check device ID
+        * The DME1737 can return either 0x78 or 0x77 as its device ID. */
+       reg = dme1737_sio_inb(sio_cip, 0x20);
+       if (!(reg == 0x77 || reg == 0x78)) {
+               err = -ENODEV;
+               goto exit;
+       }
+
+       /* Select logical device A (runtime registers) */
+       dme1737_sio_outb(sio_cip, 0x07, 0x0a);
+
+       /* Get the base address of the runtime registers */
+       if (!(addr = (dme1737_sio_inb(sio_cip, 0x60) << 8) |
+                     dme1737_sio_inb(sio_cip, 0x61))) {
+               err = -ENODEV;
+               goto exit;
+       }
+
+       /* Read the runtime registers to determine which optional features
+        * are enabled and available. Bits [3:2] of registers 0x43-0x46 are set
+        * to '10' if the respective feature is enabled. */
+       if ((inb(addr + 0x43) & 0x0c) == 0x08) { /* fan6 */
+               data->has_fan |= (1 << 5);
+       }
+       if ((inb(addr + 0x44) & 0x0c) == 0x08) { /* pwm6 */
+               data->has_pwm |= (1 << 5);
+       }
+       if ((inb(addr + 0x45) & 0x0c) == 0x08) { /* fan5 */
+               data->has_fan |= (1 << 4);
+       }
+       if ((inb(addr + 0x46) & 0x0c) == 0x08) { /* pwm5 */
+               data->has_pwm |= (1 << 4);
+       }
+
+exit:
+       dme1737_sio_exit(sio_cip);
+
+       return err;
+}
+
+static int dme1737_i2c_detect(struct i2c_adapter *adapter, int address,
+                             int kind)
 {
        u8 company, verstep = 0;
        struct i2c_client *client;
        struct dme1737_data *data;
-       int ix, err = 0;
+       struct device *dev;
+       int err = 0;
        const char *name;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
@@ -1894,7 +2080,8 @@ static int dme1737_detect(struct i2c_adapter *adapter, int address,
        i2c_set_clientdata(client, data);
        client->addr = address;
        client->adapter = adapter;
-       client->driver = &dme1737_driver;
+       client->driver = &dme1737_i2c_driver;
+       dev = &client->dev;
 
        /* A negative kind means that the driver was loaded with no force
         * parameter (default), so we must identify the chip. */
@@ -1922,92 +2109,33 @@ static int dme1737_detect(struct i2c_adapter *adapter, int address,
                goto exit_kfree;
        }
 
+       dev_info(dev, "Found a DME1737 chip at 0x%02x (rev 0x%02x).\n",
+                client->addr, verstep);
+
        /* Initialize the DME1737 chip */
-       if ((err = dme1737_init_client(client))) {
+       if ((err = dme1737_init_device(dev))) {
+               dev_err(dev, "Failed to initialize device.\n");
                goto exit_detach;
        }
 
-       /* Create standard sysfs attributes */
-       if ((err = sysfs_create_group(&client->dev.kobj, &dme1737_group))) {
-                goto exit_detach;
-       }
-
-       /* Create fan sysfs attributes */
-       for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) {
-               if (data->has_fan & (1 << ix)) {
-                       if ((err = sysfs_create_group(&client->dev.kobj,
-                                               &dme1737_fan_group[ix]))) {
-                               goto exit_remove;
-                       }
-               }
-       }
-
-       /* Create PWM sysfs attributes */
-       for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_group); ix++) {
-               if (data->has_pwm & (1 << ix)) {
-                       if ((err = sysfs_create_group(&client->dev.kobj,
-                                               &dme1737_pwm_group[ix]))) {
-                               goto exit_remove;
-                       }
-               }
-       }
-
-       /* Inform if the device is locked. Otherwise change the permissions of
-        * selected attributes from read-only to read-writeable. */
-       if (data->config & 0x02) {
-               dev_info(&client->dev, "Device is locked. Some attributes "
-                        "will be read-only.\n");
-       } else {
-               /* Change permissions of standard attributes */
-               dme1737_chmod_group(client, &dme1737_lock_group,
-                                   S_IRUGO | S_IWUSR);
-
-               /* Change permissions of PWM attributes */
-               for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_lock_group); ix++) {
-                       if (data->has_pwm & (1 << ix)) {
-                               dme1737_chmod_group(client,
-                                               &dme1737_pwm_lock_group[ix],
-                                               S_IRUGO | S_IWUSR);
-                       }
-               }
-
-               /* Change permissions of pwm[1-3] if in manual mode */
-               for (ix = 0; ix < 3; ix++) {
-                       if ((data->has_pwm & (1 << ix)) &&
-                           (PWM_EN_FROM_REG(data->pwm_config[ix]) == 1)) {
-                               dme1737_chmod_file(client,
-                                                  dme1737_attr_pwm[ix],
-                                                  S_IRUGO | S_IWUSR);
-                       }
-               }
+       /* Create sysfs files */
+       if ((err = dme1737_create_files(dev))) {
+               dev_err(dev, "Failed to create sysfs files.\n");
+               goto exit_detach;
        }
 
        /* Register device */
-       data->class_dev = hwmon_device_register(&client->dev);
-       if (IS_ERR(data->class_dev)) {
-               err = PTR_ERR(data->class_dev);
+       data->hwmon_dev = hwmon_device_register(dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               dev_err(dev, "Failed to register device.\n");
+               err = PTR_ERR(data->hwmon_dev);
                goto exit_remove;
        }
 
-       dev_info(&adapter->dev, "Found a DME1737 chip at 0x%02x "
-                "(rev 0x%02x)\n", client->addr, verstep);
-
        return 0;
 
 exit_remove:
-       for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) {
-               if (data->has_fan & (1 << ix)) {
-                       sysfs_remove_group(&client->dev.kobj,
-                                          &dme1737_fan_group[ix]);
-               }
-       }
-       for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_group); ix++) {
-               if (data->has_pwm & (1 << ix)) {
-                       sysfs_remove_group(&client->dev.kobj,
-                                          &dme1737_pwm_group[ix]);
-               }
-       }
-       sysfs_remove_group(&client->dev.kobj, &dme1737_group);
+       dme1737_remove_files(dev);
 exit_detach:
        i2c_detach_client(client);
 exit_kfree:
@@ -2016,60 +2144,260 @@ exit:
        return err;
 }
 
-static int dme1737_attach_adapter(struct i2c_adapter *adapter)
+static int dme1737_i2c_attach_adapter(struct i2c_adapter *adapter)
 {
        if (!(adapter->class & I2C_CLASS_HWMON)) {
                return 0;
        }
 
-       return i2c_probe(adapter, &addr_data, dme1737_detect);
+       return i2c_probe(adapter, &addr_data, dme1737_i2c_detect);
 }
 
-static int dme1737_detach_client(struct i2c_client *client)
+static int dme1737_i2c_detach_client(struct i2c_client *client)
 {
        struct dme1737_data *data = i2c_get_clientdata(client);
-       int ix, err;
+       int err;
 
-       hwmon_device_unregister(data->class_dev);
+       hwmon_device_unregister(data->hwmon_dev);
+       dme1737_remove_files(&client->dev);
 
-       for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) {
-               if (data->has_fan & (1 << ix)) {
-                       sysfs_remove_group(&client->dev.kobj,
-                                          &dme1737_fan_group[ix]);
-               }
+       if ((err = i2c_detach_client(client))) {
+               return err;
        }
-       for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_group); ix++) {
-               if (data->has_pwm & (1 << ix)) {
-                       sysfs_remove_group(&client->dev.kobj,
-                                          &dme1737_pwm_group[ix]);
-               }
+
+       kfree(data);
+       return 0;
+}
+
+static struct i2c_driver dme1737_i2c_driver = {
+       .driver = {
+               .name = "dme1737",
+       },
+       .attach_adapter = dme1737_i2c_attach_adapter,
+       .detach_client = dme1737_i2c_detach_client,
+};
+
+/* ---------------------------------------------------------------------
+ * ISA device detection and registration
+ * --------------------------------------------------------------------- */
+
+static int __init dme1737_isa_detect(int sio_cip, unsigned short *addr)
+{
+       int err = 0, reg;
+       unsigned short base_addr;
+
+       dme1737_sio_enter(sio_cip);
+
+       /* Check device ID
+        * We currently know about SCH3112 (0x7c), SCH3114 (0x7d), and
+        * SCH3116 (0x7f). */
+       reg = dme1737_sio_inb(sio_cip, 0x20);
+       if (!(reg == 0x7c || reg == 0x7d || reg == 0x7f)) {
+               err = -ENODEV;
+               goto exit;
        }
-       sysfs_remove_group(&client->dev.kobj, &dme1737_group);
 
-       if ((err = i2c_detach_client(client))) {
-               return err;
+       /* Select logical device A (runtime registers) */
+       dme1737_sio_outb(sio_cip, 0x07, 0x0a);
+
+       /* Get the base address of the runtime registers */
+       if (!(base_addr = (dme1737_sio_inb(sio_cip, 0x60) << 8) |
+                          dme1737_sio_inb(sio_cip, 0x61))) {
+               printk(KERN_ERR "dme1737: Base address not set.\n");
+               err = -ENODEV;
+               goto exit;
+       }
+
+       /* Access to the hwmon registers is through an index/data register
+        * pair located at offset 0x70/0x71. */
+       *addr = base_addr + 0x70;
+
+exit:
+       dme1737_sio_exit(sio_cip);
+       return err;
+}
+
+static int __init dme1737_isa_device_add(unsigned short addr)
+{
+       struct resource res = {
+               .start  = addr,
+               .end    = addr + DME1737_EXTENT - 1,
+               .name   = "dme1737",
+               .flags  = IORESOURCE_IO,
+       };
+       int err;
+
+       if (!(pdev = platform_device_alloc("dme1737", addr))) {
+               printk(KERN_ERR "dme1737: Failed to allocate device.\n");
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       if ((err = platform_device_add_resources(pdev, &res, 1))) {
+               printk(KERN_ERR "dme1737: Failed to add device resource "
+                      "(err = %d).\n", err);
+               goto exit_device_put;
+       }
+
+       if ((err = platform_device_add(pdev))) {
+               printk(KERN_ERR "dme1737: Failed to add device (err = %d).\n",
+                      err);
+               goto exit_device_put;
+       }
+
+       return 0;
+
+exit_device_put:
+       platform_device_put(pdev);
+       pdev = NULL;
+exit:
+       return err;
+}
+
+static int __devinit dme1737_isa_probe(struct platform_device *pdev)
+{
+       u8 company, device;
+       struct resource *res;
+       struct i2c_client *client;
+       struct dme1737_data *data;
+       struct device *dev = &pdev->dev;
+       int err;
+
+       res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+       if (!request_region(res->start, DME1737_EXTENT, "dme1737")) {
+               dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n",
+                       (unsigned short)res->start,
+                       (unsigned short)res->start + DME1737_EXTENT - 1);
+                err = -EBUSY;
+                goto exit;
+        }
+
+       if (!(data = kzalloc(sizeof(struct dme1737_data), GFP_KERNEL))) {
+               err = -ENOMEM;
+               goto exit_release_region;
+       }
+
+       client = &data->client;
+       i2c_set_clientdata(client, data);
+       client->addr = res->start;
+       platform_set_drvdata(pdev, data);
+
+       company = dme1737_read(client, DME1737_REG_COMPANY);
+       device = dme1737_read(client, DME1737_REG_DEVICE);
+
+       if (!((company == DME1737_COMPANY_SMSC) &&
+             (device == SCH311X_DEVICE))) {
+               err = -ENODEV;
+               goto exit_kfree;
+       }
+
+       /* Fill in the remaining client fields and initialize the mutex */
+       strlcpy(client->name, "sch311x", I2C_NAME_SIZE);
+       mutex_init(&data->update_lock);
+
+       dev_info(dev, "Found a SCH311x chip at 0x%04x\n", client->addr);
+
+       /* Initialize the chip */
+       if ((err = dme1737_init_device(dev))) {
+               dev_err(dev, "Failed to initialize device.\n");
+               goto exit_kfree;
        }
 
+       /* Create sysfs files */
+       if ((err = dme1737_create_files(dev))) {
+               dev_err(dev, "Failed to create sysfs files.\n");
+               goto exit_kfree;
+       }
+
+       /* Register device */
+       data->hwmon_dev = hwmon_device_register(dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               dev_err(dev, "Failed to register device.\n");
+               err = PTR_ERR(data->hwmon_dev);
+               goto exit_remove_files;
+       }
+
+       return 0;
+
+exit_remove_files:
+       dme1737_remove_files(dev);
+exit_kfree:
+       platform_set_drvdata(pdev, NULL);
+       kfree(data);
+exit_release_region:
+       release_region(res->start, DME1737_EXTENT);
+exit:
+       return err;
+}
+
+static int __devexit dme1737_isa_remove(struct platform_device *pdev)
+{
+       struct dme1737_data *data = platform_get_drvdata(pdev);
+
+       hwmon_device_unregister(data->hwmon_dev);
+       dme1737_remove_files(&pdev->dev);
+       release_region(data->client.addr, DME1737_EXTENT);
+       platform_set_drvdata(pdev, NULL);
        kfree(data);
+
        return 0;
 }
 
-static struct i2c_driver dme1737_driver = {
+static struct platform_driver dme1737_isa_driver = {
        .driver = {
+               .owner = THIS_MODULE,
                .name = "dme1737",
        },
-       .attach_adapter = dme1737_attach_adapter,
-       .detach_client = dme1737_detach_client,
+       .probe = dme1737_isa_probe,
+       .remove = __devexit_p(dme1737_isa_remove),
 };
 
+/* ---------------------------------------------------------------------
+ * Module initialization and cleanup
+ * --------------------------------------------------------------------- */
+
 static int __init dme1737_init(void)
 {
-       return i2c_add_driver(&dme1737_driver);
+       int err;
+       unsigned short addr;
+
+       if ((err = i2c_add_driver(&dme1737_i2c_driver))) {
+               goto exit;
+       }
+
+       if (dme1737_isa_detect(0x2e, &addr) &&
+           dme1737_isa_detect(0x4e, &addr)) {
+               /* Return 0 if we didn't find an ISA device */
+               return 0;
+       }
+
+       if ((err = platform_driver_register(&dme1737_isa_driver))) {
+               goto exit_del_i2c_driver;
+       }
+
+       /* Sets global pdev as a side effect */
+       if ((err = dme1737_isa_device_add(addr))) {
+               goto exit_del_isa_driver;
+       }
+
+       return 0;
+
+exit_del_isa_driver:
+       platform_driver_unregister(&dme1737_isa_driver);
+exit_del_i2c_driver:
+       i2c_del_driver(&dme1737_i2c_driver);
+exit:
+       return err;
 }
 
 static void __exit dme1737_exit(void)
 {
-       i2c_del_driver(&dme1737_driver);
+       if (pdev) {
+               platform_device_unregister(pdev);
+               platform_driver_unregister(&dme1737_isa_driver);
+       }
+
+       i2c_del_driver(&dme1737_i2c_driver);
 }
 
 MODULE_AUTHOR("Juerg Haefliger <juergh@gmail.com>");
index 1212d6b..b7bd000 100644 (file)
@@ -73,7 +73,7 @@ static const u8 DS1621_REG_TEMP[3] = {
 /* Each client has this additional data */
 struct ds1621_data {
        struct i2c_client client;
-       struct class_device *class_dev;
+       struct device *hwmon_dev;
        struct mutex update_lock;
        char valid;                     /* !=0 if following fields are valid */
        unsigned long last_updated;     /* In jiffies */
@@ -151,7 +151,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da,
        struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
        struct i2c_client *client = to_i2c_client(dev);
        struct ds1621_data *data = ds1621_update_client(dev);
-       u16 val = LM75_TEMP_TO_REG(simple_strtoul(buf, NULL, 10));
+       u16 val = LM75_TEMP_TO_REG(simple_strtol(buf, NULL, 10));
 
        mutex_lock(&data->update_lock);
        data->temp[attr->index] = val;
@@ -266,9 +266,9 @@ static int ds1621_detect(struct i2c_adapter *adapter, int address,
        if ((err = sysfs_create_group(&client->dev.kobj, &ds1621_group)))
                goto exit_detach;
 
-       data->class_dev = hwmon_device_register(&client->dev);
-       if (IS_ERR(data->class_dev)) {
-               err = PTR_ERR(data->class_dev);
+       data->hwmon_dev = hwmon_device_register(&client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
                goto exit_remove_files;
        }
 
@@ -289,7 +289,7 @@ static int ds1621_detach_client(struct i2c_client *client)
        struct ds1621_data *data = i2c_get_clientdata(client);
        int err;
 
-       hwmon_device_unregister(data->class_dev);
+       hwmon_device_unregister(data->hwmon_dev);
        sysfs_remove_group(&client->dev.kobj, &ds1621_group);
 
        if ((err = i2c_detach_client(client)))
index 6f60715..5d9d5cc 100644 (file)
@@ -10,6 +10,9 @@
  * The F71872F/FG is almost the same, with two more voltages monitored,
  * and 6 VID inputs.
  *
+ * The F71806F/FG is essentially the same as the F71872F/FG. It even has
+ * the same chip ID, so the driver can't differentiate between.
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -159,7 +162,7 @@ struct f71805f_auto_point {
 struct f71805f_data {
        unsigned short addr;
        const char *name;
-       struct class_device *class_dev;
+       struct device *hwmon_dev;
 
        struct mutex update_lock;
        char valid;             /* !=0 if following fields are valid */
@@ -1378,9 +1381,9 @@ static int __devinit f71805f_probe(struct platform_device *pdev)
                }
        }
 
-       data->class_dev = hwmon_device_register(&pdev->dev);
-       if (IS_ERR(data->class_dev)) {
-               err = PTR_ERR(data->class_dev);
+       data->hwmon_dev = hwmon_device_register(&pdev->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
                dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
                goto exit_remove_files;
        }
@@ -1407,7 +1410,7 @@ static int __devexit f71805f_remove(struct platform_device *pdev)
        struct resource *res;
        int i;
 
-       hwmon_device_unregister(data->class_dev);
+       hwmon_device_unregister(data->hwmon_dev);
        sysfs_remove_group(&pdev->dev.kobj, &f71805f_group);
        for (i = 0; i < 4; i++)
                sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_optin[i]);
@@ -1485,7 +1488,7 @@ static int __init f71805f_find(int sioaddr, unsigned short *address,
 
        static const char *names[] = {
                "F71805F/FG",
-               "F71872F/FG",
+               "F71872F/FG or F71806F/FG",
        };
 
        superio_enter(sioaddr);
diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c
new file mode 100644 (file)
index 0000000..6db7443
--- /dev/null
@@ -0,0 +1,950 @@
+/***************************************************************************
+ *   Copyright (C) 2006 by Hans Edgington <hans@edgington.nl>              *
+ *   Copyright (C) 2007 by Hans de Goede  <j.w.r.degoede@hhs.nl>           *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <asm/io.h>
+
+#define DRVNAME "f71882fg"
+
+#define SIO_F71882FG_LD_HWM    0x04    /* Hardware monitor logical device*/
+#define SIO_UNLOCK_KEY         0x87    /* Key to enable Super-I/O */
+#define SIO_LOCK_KEY           0xAA    /* Key to diasble Super-I/O */
+
+#define SIO_REG_LDSEL          0x07    /* Logical device select */
+#define SIO_REG_DEVID          0x20    /* Device ID (2 bytes) */
+#define SIO_REG_DEVREV         0x22    /* Device revision */
+#define SIO_REG_MANID          0x23    /* Fintek ID (2 bytes) */
+#define SIO_REG_ENABLE         0x30    /* Logical device enable */
+#define SIO_REG_ADDR           0x60    /* Logical device address (2 bytes) */
+
+#define SIO_FINTEK_ID          0x1934  /* Manufacturers ID */
+#define SIO_F71882_ID          0x0541  /* Chipset ID */
+
+#define REGION_LENGTH          8
+#define ADDR_REG_OFFSET                5
+#define DATA_REG_OFFSET                6
+
+#define F71882FG_REG_PECI              0x0A
+
+#define F71882FG_REG_IN_STATUS         0x12
+#define F71882FG_REG_IN_BEEP           0x13
+#define F71882FG_REG_IN(nr)            (0x20  + (nr))
+#define F71882FG_REG_IN1_HIGH          0x32
+
+#define F71882FG_REG_FAN(nr)           (0xA0 + (16 * (nr)))
+#define F71882FG_REG_FAN_STATUS                0x92
+#define F71882FG_REG_FAN_BEEP          0x93
+
+#define F71882FG_REG_TEMP(nr)          (0x72 + 2 * (nr))
+#define F71882FG_REG_TEMP_OVT(nr)      (0x82 + 2 * (nr))
+#define F71882FG_REG_TEMP_HIGH(nr)     (0x83 + 2 * (nr))
+#define F71882FG_REG_TEMP_STATUS       0x62
+#define F71882FG_REG_TEMP_BEEP         0x63
+#define F71882FG_REG_TEMP_HYST1                0x6C
+#define F71882FG_REG_TEMP_HYST23       0x6D
+#define F71882FG_REG_TEMP_TYPE         0x6B
+#define F71882FG_REG_TEMP_DIODE_OPEN   0x6F
+
+#define        F71882FG_REG_START              0x01
+
+#define FAN_MIN_DETECT                 366 /* Lowest detectable fanspeed */
+
+static struct platform_device *f71882fg_pdev = NULL;
+
+/* Super-I/O Function prototypes */
+static inline int superio_inb(int base, int reg);
+static inline int superio_inw(int base, int reg);
+static inline void superio_enter(int base);
+static inline void superio_select(int base, int ld);
+static inline void superio_exit(int base);
+
+static inline u16 fan_from_reg ( u16 reg );
+
+struct f71882fg_data {
+       unsigned short addr;
+       struct device *hwmon_dev;
+
+       struct mutex update_lock;
+       char valid;                     /* !=0 if following fields are valid */
+       unsigned long last_updated;     /* In jiffies */
+       unsigned long last_limits;      /* In jiffies */
+
+       /* Register Values */
+       u8      in[9];
+       u8      in1_max;
+       u8      in_status;
+       u8      in_beep;
+       u16     fan[4];
+       u8      fan_status;
+       u8      fan_beep;
+       u8      temp[3];
+       u8      temp_ovt[3];
+       u8      temp_high[3];
+       u8      temp_hyst[3];
+       u8      temp_type[3];
+       u8      temp_status;
+       u8      temp_beep;
+       u8      temp_diode_open;
+};
+
+static u8 f71882fg_read8(struct f71882fg_data *data, u8 reg);
+static u16 f71882fg_read16(struct f71882fg_data *data, u8 reg);
+static void f71882fg_write8(struct f71882fg_data *data, u8 reg, u8 val);
+
+/* Sysfs in*/
+static ssize_t show_in(struct device *dev, struct device_attribute *devattr,
+       char *buf);
+static ssize_t show_in_max(struct device *dev, struct device_attribute
+       *devattr, char *buf);
+static ssize_t store_in_max(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count);
+static ssize_t show_in_beep(struct device *dev, struct device_attribute
+       *devattr, char *buf);
+static ssize_t store_in_beep(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count);
+static ssize_t show_in_alarm(struct device *dev, struct device_attribute
+       *devattr, char *buf);
+/* Sysfs Fan */
+static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
+       char *buf);
+static ssize_t show_fan_beep(struct device *dev, struct device_attribute
+       *devattr, char *buf);
+static ssize_t store_fan_beep(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count);
+static ssize_t show_fan_alarm(struct device *dev, struct device_attribute
+       *devattr, char *buf);
+/* Sysfs Temp */
+static ssize_t show_temp(struct device *dev, struct device_attribute
+       *devattr, char *buf);
+static ssize_t show_temp_max(struct device *dev, struct device_attribute
+       *devattr, char *buf);
+static ssize_t store_temp_max(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count);
+static ssize_t show_temp_max_hyst(struct device *dev, struct device_attribute
+       *devattr, char *buf);
+static ssize_t store_temp_max_hyst(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count);
+static ssize_t show_temp_crit(struct device *dev, struct device_attribute
+       *devattr, char *buf);
+static ssize_t store_temp_crit(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count);
+static ssize_t show_temp_crit_hyst(struct device *dev, struct device_attribute
+       *devattr, char *buf);
+static ssize_t show_temp_type(struct device *dev, struct device_attribute
+       *devattr, char *buf);
+static ssize_t show_temp_beep(struct device *dev, struct device_attribute
+       *devattr, char *buf);
+static ssize_t store_temp_beep(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count);
+static ssize_t show_temp_alarm(struct device *dev, struct device_attribute
+       *devattr, char *buf);
+static ssize_t show_temp_fault(struct device *dev, struct device_attribute
+       *devattr, char *buf);
+/* Sysfs misc */
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+       char *buf);
+
+static int __devinit f71882fg_probe(struct platform_device * pdev);
+static int __devexit f71882fg_remove(struct platform_device *pdev);
+static int __init f71882fg_init(void);
+static int __init f71882fg_find(int sioaddr, unsigned short *address);
+static int __init f71882fg_device_add(unsigned short address);
+static void __exit f71882fg_exit(void);
+
+static struct platform_driver f71882fg_driver = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = DRVNAME,
+       },
+       .probe          = f71882fg_probe,
+       .remove         = __devexit_p(f71882fg_remove),
+};
+
+static struct device_attribute f71882fg_dev_attr[] =
+{
+       __ATTR( name, S_IRUGO, show_name, NULL ),
+};
+
+static struct sensor_device_attribute f71882fg_in_temp_attr[] =
+{
+       SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0),
+       SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1),
+       SENSOR_ATTR(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max, 1),
+       SENSOR_ATTR(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep, 1),
+       SENSOR_ATTR(in1_alarm, S_IRUGO, show_in_alarm, NULL, 1),
+       SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2),
+       SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3),
+       SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4),
+       SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5),
+       SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6),
+       SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7),
+       SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8),
+       SENSOR_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0),
+       SENSOR_ATTR(temp1_max, S_IRUGO|S_IWUSR, show_temp_max,
+               store_temp_max, 0),
+       SENSOR_ATTR(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
+               store_temp_max_hyst, 0),
+       SENSOR_ATTR(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+               store_temp_crit, 0),
+       SENSOR_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 0),
+       SENSOR_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0),
+       SENSOR_ATTR(temp1_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+               store_temp_beep, 0),
+       SENSOR_ATTR(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0),
+       SENSOR_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0),
+       SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1),
+       SENSOR_ATTR(temp2_max, S_IRUGO|S_IWUSR, show_temp_max,
+               store_temp_max, 1),
+       SENSOR_ATTR(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
+               store_temp_max_hyst, 1),
+       SENSOR_ATTR(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+               store_temp_crit, 1),
+       SENSOR_ATTR(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 1),
+       SENSOR_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1),
+       SENSOR_ATTR(temp2_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+               store_temp_beep, 1),
+       SENSOR_ATTR(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 1),
+       SENSOR_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1),
+       SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2),
+       SENSOR_ATTR(temp3_max, S_IRUGO|S_IWUSR, show_temp_max,
+               store_temp_max, 2),
+       SENSOR_ATTR(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
+               store_temp_max_hyst, 2),
+       SENSOR_ATTR(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit,
+               store_temp_crit, 2),
+       SENSOR_ATTR(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 2),
+       SENSOR_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2),
+       SENSOR_ATTR(temp3_beep, S_IRUGO|S_IWUSR, show_temp_beep,
+               store_temp_beep, 2),
+       SENSOR_ATTR(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 2),
+       SENSOR_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2)
+};
+
+static struct sensor_device_attribute f71882fg_fan_attr[] =
+{
+       SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0),
+       SENSOR_ATTR(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+               store_fan_beep, 0),
+       SENSOR_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0),
+       SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1),
+       SENSOR_ATTR(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+               store_fan_beep, 1),
+       SENSOR_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1),
+       SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2),
+       SENSOR_ATTR(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+               store_fan_beep, 2),
+       SENSOR_ATTR(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 2),
+       SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3),
+       SENSOR_ATTR(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+               store_fan_beep, 3),
+       SENSOR_ATTR(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 3)
+};
+
+
+/* Super I/O functions */
+static inline int superio_inb(int base, int reg)
+{
+       outb(reg, base);
+       return inb(base + 1);
+}
+
+static int superio_inw(int base, int reg)
+{
+       int val;
+       outb(reg++, base);
+       val = inb(base + 1) << 8;
+       outb(reg, base);
+       val |= inb(base + 1);
+       return val;
+}
+
+static inline void superio_enter(int base)
+{
+       /* according to the datasheet the key must be send twice! */
+       outb( SIO_UNLOCK_KEY, base);
+       outb( SIO_UNLOCK_KEY, base);
+}
+
+static inline void superio_select( int base, int ld)
+{
+       outb(SIO_REG_LDSEL, base);
+       outb(ld, base + 1);
+}
+
+static inline void superio_exit(int base)
+{
+       outb(SIO_LOCK_KEY, base);
+}
+
+static inline u16 fan_from_reg(u16 reg)
+{
+       return reg ? (1500000 / reg) : 0;
+}
+
+static u8 f71882fg_read8(struct f71882fg_data *data, u8 reg)
+{
+       u8 val;
+
+       outb(reg, data->addr + ADDR_REG_OFFSET);
+       val = inb(data->addr + DATA_REG_OFFSET);
+
+       return val;
+}
+
+static u16 f71882fg_read16(struct f71882fg_data *data, u8 reg)
+{
+       u16 val;
+
+       outb(reg++, data->addr + ADDR_REG_OFFSET);
+       val = inb(data->addr + DATA_REG_OFFSET) << 8;
+       outb(reg, data->addr + ADDR_REG_OFFSET);
+       val |= inb(data->addr + DATA_REG_OFFSET);
+
+       return val;
+}
+
+static void f71882fg_write8(struct f71882fg_data *data, u8 reg, u8 val)
+{
+       outb(reg, data->addr + ADDR_REG_OFFSET);
+       outb(val, data->addr + DATA_REG_OFFSET);
+}
+
+static struct f71882fg_data *f71882fg_update_device(struct device * dev)
+{
+       struct f71882fg_data *data = dev_get_drvdata(dev);
+       int nr, reg, reg2;
+
+       mutex_lock(&data->update_lock);
+
+       /* Update once every 60 seconds */
+       if ( time_after(jiffies, data->last_limits + 60 * HZ ) ||
+                       !data->valid) {
+               data->in1_max = f71882fg_read8(data, F71882FG_REG_IN1_HIGH);
+               data->in_beep = f71882fg_read8(data, F71882FG_REG_IN_BEEP);
+
+               /* Get High & boundary temps*/
+               for (nr = 0; nr < 3; nr++) {
+                       data->temp_ovt[nr] = f71882fg_read8(data,
+                                               F71882FG_REG_TEMP_OVT(nr));
+                       data->temp_high[nr] = f71882fg_read8(data,
+                                               F71882FG_REG_TEMP_HIGH(nr));
+               }
+
+               /* Have to hardcode hyst*/
+               data->temp_hyst[0] = f71882fg_read8(data,
+                                               F71882FG_REG_TEMP_HYST1) >> 4;
+               /* Hyst temps 2 & 3 stored in same register */
+               reg = f71882fg_read8(data, F71882FG_REG_TEMP_HYST23);
+               data->temp_hyst[1] = reg & 0x0F;
+               data->temp_hyst[2] = reg >> 4;
+
+               /* Have to hardcode type, because temp1 is special */
+               reg  = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE);
+               reg2 = f71882fg_read8(data, F71882FG_REG_PECI);
+               if ((reg2 & 0x03) == 0x01)
+                       data->temp_type[0] = 6 /* PECI */;
+               else if ((reg2 & 0x03) == 0x02)
+                       data->temp_type[0] = 5 /* AMDSI */;
+               else
+                       data->temp_type[0] = (reg & 0x02) ? 2 : 4;
+
+               data->temp_type[1] = (reg & 0x04) ? 2 : 4;
+               data->temp_type[2] = (reg & 0x08) ? 2 : 4;
+
+               data->temp_beep = f71882fg_read8(data, F71882FG_REG_TEMP_BEEP);
+
+               data->fan_beep = f71882fg_read8(data, F71882FG_REG_FAN_BEEP);
+
+               data->last_limits = jiffies;
+       }
+
+       /* Update every second */
+       if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+               data->temp_status = f71882fg_read8(data,
+                                               F71882FG_REG_TEMP_STATUS);
+               data->temp_diode_open = f71882fg_read8(data,
+                                               F71882FG_REG_TEMP_DIODE_OPEN);
+               for (nr = 0; nr < 3; nr++)
+                       data->temp[nr] = f71882fg_read8(data,
+                                               F71882FG_REG_TEMP(nr));
+
+               data->fan_status = f71882fg_read8(data,
+                                               F71882FG_REG_FAN_STATUS);
+               for (nr = 0; nr < 4; nr++)
+                       data->fan[nr] = f71882fg_read16(data,
+                                               F71882FG_REG_FAN(nr));
+
+               data->in_status = f71882fg_read8(data,
+                                               F71882FG_REG_IN_STATUS);
+               for (nr = 0; nr < 9; nr++)
+                       data->in[nr] = f71882fg_read8(data,
+                                               F71882FG_REG_IN(nr));
+
+               data->last_updated = jiffies;
+               data->valid = 1;
+       }
+
+       mutex_unlock(&data->update_lock);
+
+       return data;
+}
+
+/* Sysfs Interface */
+static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
+       char *buf)
+{
+       struct f71882fg_data *data = f71882fg_update_device(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+       int speed = fan_from_reg(data->fan[nr]);
+
+       if (speed == FAN_MIN_DETECT)
+               speed = 0;
+
+       return sprintf(buf, "%d\n", speed);
+}
+
+static ssize_t show_fan_beep(struct device *dev, struct device_attribute
+       *devattr, char *buf)
+{
+       struct f71882fg_data *data = f71882fg_update_device(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+
+       if (data->fan_beep & (1 << nr))
+               return sprintf(buf, "1\n");
+       else
+               return sprintf(buf, "0\n");
+}
+
+static ssize_t store_fan_beep(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count)
+{
+       struct f71882fg_data *data = dev_get_drvdata(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+       int val = simple_strtoul(buf, NULL, 10);
+
+       mutex_lock(&data->update_lock);
+       if (val)
+               data->fan_beep |= 1 << nr;
+       else
+               data->fan_beep &= ~(1 << nr);
+
+       f71882fg_write8(data, F71882FG_REG_FAN_BEEP, data->fan_beep);
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t show_fan_alarm(struct device *dev, struct device_attribute
+       *devattr, char *buf)
+{
+       struct f71882fg_data *data = f71882fg_update_device(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+
+       if (data->fan_status & (1 << nr))
+               return sprintf(buf, "1\n");
+       else
+               return sprintf(buf, "0\n");
+}
+
+static ssize_t show_in(struct device *dev, struct device_attribute *devattr,
+       char *buf)
+{
+       struct f71882fg_data *data = f71882fg_update_device(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+
+       return sprintf(buf, "%d\n", data->in[nr] * 8);
+}
+
+static ssize_t show_in_max(struct device *dev, struct device_attribute
+       *devattr, char *buf)
+{
+       struct f71882fg_data *data = f71882fg_update_device(dev);
+
+       return sprintf(buf, "%d\n", data->in1_max * 8);
+}
+
+static ssize_t store_in_max(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count)
+{
+       struct f71882fg_data *data = dev_get_drvdata(dev);
+       int val = simple_strtoul(buf, NULL, 10) / 8;
+
+       if (val > 255)
+               val = 255;
+
+       mutex_lock(&data->update_lock);
+       f71882fg_write8(data, F71882FG_REG_IN1_HIGH, val);
+       data->in1_max = val;
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t show_in_beep(struct device *dev, struct device_attribute
+       *devattr, char *buf)
+{
+       struct f71882fg_data *data = f71882fg_update_device(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+
+       if (data->in_beep & (1 << nr))
+               return sprintf(buf, "1\n");
+       else
+               return sprintf(buf, "0\n");
+}
+
+static ssize_t store_in_beep(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count)
+{
+       struct f71882fg_data *data = dev_get_drvdata(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+       int val = simple_strtoul(buf, NULL, 10);
+
+       mutex_lock(&data->update_lock);
+       if (val)
+               data->in_beep |= 1 << nr;
+       else
+               data->in_beep &= ~(1 << nr);
+
+       f71882fg_write8(data, F71882FG_REG_IN_BEEP, data->in_beep);
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t show_in_alarm(struct device *dev, struct device_attribute
+       *devattr, char *buf)
+{
+       struct f71882fg_data *data = f71882fg_update_device(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+
+       if (data->in_status & (1 << nr))
+               return sprintf(buf, "1\n");
+       else
+               return sprintf(buf, "0\n");
+}
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
+       char *buf)
+{
+       struct f71882fg_data *data = f71882fg_update_device(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+
+       return sprintf(buf, "%d\n", data->temp[nr] * 1000);
+}
+
+static ssize_t show_temp_max(struct device *dev, struct device_attribute
+       *devattr, char *buf)
+{
+       struct f71882fg_data *data = f71882fg_update_device(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+
+       return sprintf(buf, "%d\n", data->temp_high[nr] * 1000);
+}
+
+static ssize_t store_temp_max(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count)
+{
+       struct f71882fg_data *data = dev_get_drvdata(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+       int val = simple_strtoul(buf, NULL, 10) / 1000;
+
+       if (val > 255)
+               val = 255;
+
+       mutex_lock(&data->update_lock);
+       f71882fg_write8(data, F71882FG_REG_TEMP_HIGH(nr), val);
+       data->temp_high[nr] = val;
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t show_temp_max_hyst(struct device *dev, struct device_attribute
+       *devattr, char *buf)
+{
+       struct f71882fg_data *data = f71882fg_update_device(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+
+       return sprintf(buf, "%d\n",
+               (data->temp_high[nr] - data->temp_hyst[nr]) * 1000);
+}
+
+static ssize_t store_temp_max_hyst(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count)
+{
+       struct f71882fg_data *data = dev_get_drvdata(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+       int val = simple_strtoul(buf, NULL, 10) / 1000;
+       ssize_t ret = count;
+
+       mutex_lock(&data->update_lock);
+
+       /* convert abs to relative and check */
+       val = data->temp_high[nr] - val;
+       if (val < 0 || val > 15) {
+               ret = -EINVAL;
+               goto store_temp_max_hyst_exit;
+       }
+
+       data->temp_hyst[nr] = val;
+
+       /* convert value to register contents */
+       switch (nr) {
+               case 0:
+                       val = val << 4;
+                       break;
+               case 1:
+                       val = val | (data->temp_hyst[2] << 4);
+                       break;
+               case 2:
+                       val = data->temp_hyst[1] | (val << 4);
+                       break;
+       }
+
+       f71882fg_write8(data, nr ? F71882FG_REG_TEMP_HYST23 :
+               F71882FG_REG_TEMP_HYST1, val);
+
+store_temp_max_hyst_exit:
+       mutex_unlock(&data->update_lock);
+       return ret;
+}
+
+static ssize_t show_temp_crit(struct device *dev, struct device_attribute
+       *devattr, char *buf)
+{
+       struct f71882fg_data *data = f71882fg_update_device(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+
+       return sprintf(buf, "%d\n", data->temp_ovt[nr] * 1000);
+}
+
+static ssize_t store_temp_crit(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count)
+{
+       struct f71882fg_data *data = dev_get_drvdata(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+       int val = simple_strtoul(buf, NULL, 10) / 1000;
+
+       if (val > 255)
+               val = 255;
+
+       mutex_lock(&data->update_lock);
+       f71882fg_write8(data, F71882FG_REG_TEMP_OVT(nr), val);
+       data->temp_ovt[nr] = val;
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t show_temp_crit_hyst(struct device *dev, struct device_attribute
+       *devattr, char *buf)
+{
+       struct f71882fg_data *data = f71882fg_update_device(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+
+       return sprintf(buf, "%d\n",
+               (data->temp_ovt[nr] - data->temp_hyst[nr]) * 1000);
+}
+
+static ssize_t show_temp_type(struct device *dev, struct device_attribute
+       *devattr, char *buf)
+{
+       struct f71882fg_data *data = f71882fg_update_device(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+
+       return sprintf(buf, "%d\n", data->temp_type[nr]);
+}
+
+static ssize_t show_temp_beep(struct device *dev, struct device_attribute
+       *devattr, char *buf)
+{
+       struct f71882fg_data *data = f71882fg_update_device(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+
+       if (data->temp_beep & (1 << (nr + 1)))
+               return sprintf(buf, "1\n");
+       else
+               return sprintf(buf, "0\n");
+}
+
+static ssize_t store_temp_beep(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count)
+{
+       struct f71882fg_data *data = dev_get_drvdata(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+       int val = simple_strtoul(buf, NULL, 10);
+
+       mutex_lock(&data->update_lock);
+       if (val)
+               data->temp_beep |= 1 << (nr + 1);
+       else
+               data->temp_beep &= ~(1 << (nr + 1));
+
+       f71882fg_write8(data, F71882FG_REG_TEMP_BEEP, data->temp_beep);
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t show_temp_alarm(struct device *dev, struct device_attribute
+       *devattr, char *buf)
+{
+       struct f71882fg_data *data = f71882fg_update_device(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+
+       if (data->temp_status & (1 << (nr + 1)))
+               return sprintf(buf, "1\n");
+       else
+               return sprintf(buf, "0\n");
+}
+
+static ssize_t show_temp_fault(struct device *dev, struct device_attribute
+       *devattr, char *buf)
+{
+       struct f71882fg_data *data = f71882fg_update_device(dev);
+       int nr = to_sensor_dev_attr(devattr)->index;
+
+       if (data->temp_diode_open & (1 << (nr + 1)))
+               return sprintf(buf, "1\n");
+       else
+               return sprintf(buf, "0\n");
+}
+
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+       char *buf)
+{
+       return sprintf(buf, DRVNAME "\n");
+}
+
+
+static int __devinit f71882fg_probe(struct platform_device * pdev)
+{
+       struct f71882fg_data *data;
+       int err, i;
+       u8 start_reg;
+
+       if (!(data = kzalloc(sizeof(struct f71882fg_data), GFP_KERNEL)))
+               return -ENOMEM;
+
+       data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start;
+       mutex_init(&data->update_lock);
+       platform_set_drvdata(pdev, data);
+
+       /* Register sysfs interface files */
+       for (i = 0; i < ARRAY_SIZE(f71882fg_dev_attr); i++) {
+               err = device_create_file(&pdev->dev, &f71882fg_dev_attr[i]);
+               if (err)
+                       goto exit_unregister_sysfs;
+       }
+
+       start_reg = f71882fg_read8(data, F71882FG_REG_START);
+       if (start_reg & 0x01) {
+               for (i = 0; i < ARRAY_SIZE(f71882fg_in_temp_attr); i++) {
+                       err = device_create_file(&pdev->dev,
+                                       &f71882fg_in_temp_attr[i].dev_attr);
+                       if (err)
+                               goto exit_unregister_sysfs;
+               }
+       }
+
+       if (start_reg & 0x02) {
+               for (i = 0; i < ARRAY_SIZE(f71882fg_fan_attr); i++) {
+                       err = device_create_file(&pdev->dev,
+                                       &f71882fg_fan_attr[i].dev_attr);
+                       if (err)
+                               goto exit_unregister_sysfs;
+               }
+       }
+
+       data->hwmon_dev = hwmon_device_register(&pdev->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
+               goto exit_unregister_sysfs;
+       }
+
+       return 0;
+
+exit_unregister_sysfs:
+       for (i = 0; i < ARRAY_SIZE(f71882fg_dev_attr); i++)
+               device_remove_file(&pdev->dev, &f71882fg_dev_attr[i]);
+
+       for (i = 0; i < ARRAY_SIZE(f71882fg_in_temp_attr); i++)
+               device_remove_file(&pdev->dev,
+                                       &f71882fg_in_temp_attr[i].dev_attr);
+
+       for (i = 0; i < ARRAY_SIZE(f71882fg_fan_attr); i++)
+               device_remove_file(&pdev->dev, &f71882fg_fan_attr[i].dev_attr);
+
+       kfree(data);
+
+       return err;
+}
+
+static int __devexit f71882fg_remove(struct platform_device *pdev)
+{
+       int i;
+       struct f71882fg_data *data = platform_get_drvdata(pdev);
+
+       platform_set_drvdata(pdev, NULL);
+       hwmon_device_unregister(data->hwmon_dev);
+
+       for (i = 0; i < ARRAY_SIZE(f71882fg_dev_attr); i++)
+               device_remove_file(&pdev->dev, &f71882fg_dev_attr[i]);
+
+       for (i = 0; i < ARRAY_SIZE(f71882fg_in_temp_attr); i++)
+               device_remove_file(&pdev->dev,
+                                       &f71882fg_in_temp_attr[i].dev_attr);
+
+       for (i = 0; i < ARRAY_SIZE(f71882fg_fan_attr); i++)
+               device_remove_file(&pdev->dev, &f71882fg_fan_attr[i].dev_attr);
+
+       kfree(data);
+
+       return 0;
+}
+
+static int __init f71882fg_find(int sioaddr, unsigned short *address)
+{
+       int err = -ENODEV;
+       u16 devid;
+       u8 start_reg;
+       struct f71882fg_data data;
+
+       superio_enter(sioaddr);
+
+       devid = superio_inw(sioaddr, SIO_REG_MANID);
+       if (devid != SIO_FINTEK_ID) {
+               printk(KERN_INFO DRVNAME ": Not a Fintek device\n");
+               goto exit;
+       }
+
+       devid = superio_inw(sioaddr, SIO_REG_DEVID);
+       if (devid != SIO_F71882_ID) {
+               printk(KERN_INFO DRVNAME ": Unsupported Fintek device\n");
+               goto exit;
+       }
+
+       superio_select(sioaddr, SIO_F71882FG_LD_HWM);
+       if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) {
+               printk(KERN_WARNING DRVNAME ": Device not activated\n");
+               goto exit;
+       }
+
+       *address = superio_inw(sioaddr, SIO_REG_ADDR);
+       if (*address == 0)
+       {
+               printk(KERN_WARNING DRVNAME ": Base address not set\n");
+               goto exit;
+       }
+       *address &= ~(REGION_LENGTH - 1);       /* Ignore 3 LSB */
+
+       data.addr = *address;
+       start_reg = f71882fg_read8(&data, F71882FG_REG_START);
+       if (!(start_reg & 0x03)) {
+               printk(KERN_WARNING DRVNAME
+                       ": Hardware monitoring not activated\n");
+               goto exit;
+       }
+
+       err = 0;
+       printk(KERN_INFO DRVNAME ": Found F71882FG chip at %#x, revision %d\n",
+               (unsigned int)*address,
+               (int)superio_inb(sioaddr, SIO_REG_DEVREV));
+exit:
+       superio_exit(sioaddr);
+       return err;
+}
+
+static int __init f71882fg_device_add(unsigned short address)
+{
+       struct resource res = {
+               .start  = address,
+               .end    = address + REGION_LENGTH - 1,
+               .flags  = IORESOURCE_IO,
+       };
+       int err;
+
+       f71882fg_pdev = platform_device_alloc(DRVNAME, address);
+       if (!f71882fg_pdev)
+               return -ENOMEM;
+
+       res.name = f71882fg_pdev->name;
+       err = platform_device_add_resources(f71882fg_pdev, &res, 1);
+       if (err) {
+               printk(KERN_ERR DRVNAME ": Device resource addition failed\n");
+               goto exit_device_put;
+       }
+
+       err = platform_device_add(f71882fg_pdev);
+       if (err) {
+               printk(KERN_ERR DRVNAME ": Device addition failed\n");
+               goto exit_device_put;
+       }
+
+       return 0;
+
+exit_device_put:
+       platform_device_put(f71882fg_pdev);
+
+       return err;
+}
+
+static int __init f71882fg_init(void)
+{
+       int err = -ENODEV;
+       unsigned short address;
+
+       if (f71882fg_find(0x2e, &address) && f71882fg_find(0x4e, &address))
+               goto exit;
+
+       if ((err = platform_driver_register(&f71882fg_driver)))
+               goto exit;
+
+       if ((err = f71882fg_device_add(address)))
+               goto exit_driver;
+
+       return 0;
+
+exit_driver:
+       platform_driver_unregister(&f71882fg_driver);
+exit:
+       return err;
+}
+
+static void __exit f71882fg_exit(void)
+{
+       platform_device_unregister(f71882fg_pdev);
+       platform_driver_unregister(&f71882fg_driver);
+}
+
+MODULE_DESCRIPTION("F71882FG Hardware Monitoring Driver");
+MODULE_AUTHOR("Hans Edgington (hans@edgington.nl)");
+MODULE_LICENSE("GPL");
+
+module_init(f71882fg_init);
+module_exit(f71882fg_exit);
diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c
new file mode 100644 (file)
index 0000000..13a0413
--- /dev/null
@@ -0,0 +1,691 @@
+/*
+ * f75375s.c - driver for the Fintek F75375/SP and F75373
+ *             hardware monitoring features
+ * Copyright (C) 2006-2007  Riku Voipio <riku.voipio@movial.fi>
+ *
+ * Datasheets available at:
+ *
+ * f75375:
+ * http://www.fintek.com.tw/files/productfiles/2005111152950.pdf
+ *
+ * f75373:
+ * http://www.fintek.com.tw/files/productfiles/2005111153128.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x2d, 0x2e, I2C_CLIENT_END };
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD_2(f75373, f75375);
+
+/* Fintek F75375 registers  */
+#define F75375_REG_CONFIG0             0x0
+#define F75375_REG_CONFIG1             0x1
+#define F75375_REG_CONFIG2             0x2
+#define F75375_REG_CONFIG3             0x3
+#define F75375_REG_ADDR                        0x4
+#define F75375_REG_INTR                        0x31
+#define F75375_CHIP_ID                 0x5A
+#define F75375_REG_VERSION             0x5C
+#define F75375_REG_VENDOR              0x5D
+#define F75375_REG_FAN_TIMER           0x60
+
+#define F75375_REG_VOLT(nr)            (0x10 + (nr))
+#define F75375_REG_VOLT_HIGH(nr)       (0x20 + (nr) * 2)
+#define F75375_REG_VOLT_LOW(nr)                (0x21 + (nr) * 2)
+
+#define F75375_REG_TEMP(nr)            (0x14 + (nr))
+#define F75375_REG_TEMP_HIGH(nr)       (0x28 + (nr) * 2)
+#define F75375_REG_TEMP_HYST(nr)       (0x29 + (nr) * 2)
+
+#define F75375_REG_FAN(nr)             (0x16 + (nr) * 2)
+#define F75375_REG_FAN_MIN(nr)         (0x2C + (nr) * 2)
+#define F75375_REG_FAN_FULL(nr)                (0x70 + (nr) * 0x10)
+#define F75375_REG_FAN_PWM_DUTY(nr)    (0x76 + (nr) * 0x10)
+#define F75375_REG_FAN_PWM_CLOCK(nr)   (0x7D + (nr) * 0x10)
+
+#define F75375_REG_FAN_EXP(nr)         (0x74 + (nr) * 0x10)
+#define F75375_REG_FAN_B_TEMP(nr, step)        ((0xA0 + (nr) * 0x10) + (step))
+#define F75375_REG_FAN_B_SPEED(nr, step) \
+       ((0xA5 + (nr) * 0x10) + (step) * 2)
+
+#define F75375_REG_PWM1_RAISE_DUTY     0x69
+#define F75375_REG_PWM2_RAISE_DUTY     0x6A
+#define F75375_REG_PWM1_DROP_DUTY      0x6B
+#define F75375_REG_PWM2_DROP_DUTY      0x6C
+
+#define FAN_CTRL_LINEAR(nr)            (4 + nr)
+#define FAN_CTRL_MODE(nr)              (5 + ((nr) * 2))
+
+/*
+ * Data structures and manipulation thereof
+ */
+
+struct f75375_data {
+       unsigned short addr;
+       struct i2c_client client;
+       struct device *hwmon_dev;
+
+       const char *name;
+       int kind;
+       struct mutex update_lock; /* protect register access */
+       char valid;
+       unsigned long last_updated;     /* In jiffies */
+       unsigned long last_limits;      /* In jiffies */
+
+       /* Register values */
+       u8 in[4];
+       u8 in_max[4];
+       u8 in_min[4];
+       u16 fan[2];
+       u16 fan_min[2];
+       u16 fan_full[2];
+       u16 fan_exp[2];
+       u8 fan_timer;
+       u8 pwm[2];
+       u8 pwm_mode[2];
+       u8 pwm_enable[2];
+       s8 temp[2];
+       s8 temp_high[2];
+       s8 temp_max_hyst[2];
+};
+
+static int f75375_attach_adapter(struct i2c_adapter *adapter);
+static int f75375_detect(struct i2c_adapter *adapter, int address, int kind);
+static int f75375_detach_client(struct i2c_client *client);
+
+static struct i2c_driver f75375_driver = {
+       .driver = {
+               .name = "f75375",
+       },
+       .attach_adapter = f75375_attach_adapter,
+       .detach_client = f75375_detach_client,
+};
+
+static inline int f75375_read8(struct i2c_client *client, u8 reg)
+{
+       return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* in most cases, should be called while holding update_lock */
+static inline u16 f75375_read16(struct i2c_client *client, u8 reg)
+{
+       return ((i2c_smbus_read_byte_data(client, reg) << 8)
+               | i2c_smbus_read_byte_data(client, reg + 1));
+}
+
+static inline void f75375_write8(struct i2c_client *client, u8 reg,
+               u8 value)
+{
+       i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static inline void f75375_write16(struct i2c_client *client, u8 reg,
+               u16 value)
+{
+       int err = i2c_smbus_write_byte_data(client, reg, (value << 8));
+       if (err)
+               return;
+       i2c_smbus_write_byte_data(client, reg + 1, (value & 0xFF));
+}
+
+static struct f75375_data *f75375_update_device(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct f75375_data *data = i2c_get_clientdata(client);
+       int nr;
+
+       mutex_lock(&data->update_lock);
+
+       /* Limit registers cache is refreshed after 60 seconds */
+       if (time_after(jiffies, data->last_limits + 60 * HZ)
+               || !data->valid) {
+               for (nr = 0; nr < 2; nr++) {
+                       data->temp_high[nr] =
+                               f75375_read8(client, F75375_REG_TEMP_HIGH(nr));
+                       data->temp_max_hyst[nr] =
+                               f75375_read8(client, F75375_REG_TEMP_HYST(nr));
+                       data->fan_full[nr] =
+                               f75375_read16(client, F75375_REG_FAN_FULL(nr));
+                       data->fan_min[nr] =
+                               f75375_read16(client, F75375_REG_FAN_MIN(nr));
+                       data->fan_exp[nr] =
+                               f75375_read16(client, F75375_REG_FAN_EXP(nr));
+                       data->pwm[nr] = f75375_read8(client,
+                               F75375_REG_FAN_PWM_DUTY(nr));
+
+               }
+               for (nr = 0; nr < 4; nr++) {
+                       data->in_max[nr] =
+                               f75375_read8(client, F75375_REG_VOLT_HIGH(nr));
+                       data->in_min[nr] =
+                               f75375_read8(client, F75375_REG_VOLT_LOW(nr));
+               }
+               data->fan_timer = f75375_read8(client, F75375_REG_FAN_TIMER);
+               data->last_limits = jiffies;
+       }
+
+       /* Measurement registers cache is refreshed after 2 second */
+       if (time_after(jiffies, data->last_updated + 2 * HZ)
+               || !data->valid) {
+               for (nr = 0; nr < 2; nr++) {
+                       data->temp[nr] =
+                               f75375_read8(client, F75375_REG_TEMP(nr));
+                       data->fan[nr] =
+                               f75375_read16(client, F75375_REG_FAN(nr));
+               }
+               for (nr = 0; nr < 4; nr++)
+                       data->in[nr] =
+                               f75375_read8(client, F75375_REG_VOLT(nr));
+
+               data->last_updated = jiffies;
+               data->valid = 1;
+       }
+
+       mutex_unlock(&data->update_lock);
+       return data;
+}
+
+static inline u16 rpm_from_reg(u16 reg)
+{
+       if (reg == 0 || reg == 0xffff)
+               return 0;
+       return (1500000 / reg);
+}
+
+static inline u16 rpm_to_reg(int rpm)
+{
+       if (rpm < 367 || rpm > 0xffff)
+               return 0xffff;
+       return (1500000 / rpm);
+}
+
+static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       int nr = to_sensor_dev_attr(attr)->index;
+       struct i2c_client *client = to_i2c_client(dev);
+       struct f75375_data *data = i2c_get_clientdata(client);
+       int val = simple_strtoul(buf, NULL, 10);
+
+       mutex_lock(&data->update_lock);
+       data->fan_min[nr] = rpm_to_reg(val);
+       f75375_write16(client, F75375_REG_FAN_MIN(nr), data->fan_min[nr]);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t set_fan_exp(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       int nr = to_sensor_dev_attr(attr)->index;
+       struct i2c_client *client = to_i2c_client(dev);
+       struct f75375_data *data = i2c_get_clientdata(client);
+       int val = simple_strtoul(buf, NULL, 10);
+
+       mutex_lock(&data->update_lock);
+       data->fan_exp[nr] = rpm_to_reg(val);
+       f75375_write16(client, F75375_REG_FAN_EXP(nr), data->fan_exp[nr]);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       int nr = to_sensor_dev_attr(attr)->index;
+       struct i2c_client *client = to_i2c_client(dev);
+       struct f75375_data *data = i2c_get_clientdata(client);
+       int val = simple_strtoul(buf, NULL, 10);
+
+       mutex_lock(&data->update_lock);
+       data->pwm[nr] = SENSORS_LIMIT(val, 0, 255);
+       f75375_write8(client, F75375_REG_FAN_PWM_DUTY(nr), data->pwm[nr]);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t show_pwm_enable(struct device *dev, struct device_attribute
+               *attr, char *buf)
+{
+       int nr = to_sensor_dev_attr(attr)->index;
+       struct f75375_data *data = f75375_update_device(dev);
+       return sprintf(buf, "%d\n", data->pwm_enable[nr]);
+}
+
+static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       int nr = to_sensor_dev_attr(attr)->index;
+       struct i2c_client *client = to_i2c_client(dev);
+       struct f75375_data *data = i2c_get_clientdata(client);
+       int val = simple_strtoul(buf, NULL, 10);
+       u8 fanmode;
+
+       if (val < 0 || val > 4)
+               return -EINVAL;
+
+       mutex_lock(&data->update_lock);
+       fanmode = f75375_read8(client, F75375_REG_FAN_TIMER);
+       fanmode = ~(3 << FAN_CTRL_MODE(nr));
+
+       switch (val) {
+       case 0: /* Full speed */
+               fanmode  |= (3 << FAN_CTRL_MODE(nr));
+               data->pwm[nr] = 255;
+               f75375_write8(client, F75375_REG_FAN_PWM_DUTY(nr),
+                               data->pwm[nr]);
+               break;
+       case 1: /* PWM */
+               fanmode  |= (3 << FAN_CTRL_MODE(nr));
+               break;
+       case 2: /* AUTOMATIC*/
+               fanmode  |= (2 << FAN_CTRL_MODE(nr));
+               break;
+       case 3: /* fan speed */
+               break;
+       }
+       f75375_write8(client, F75375_REG_FAN_TIMER, fanmode);
+       data->pwm_enable[nr] = val;
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t set_pwm_mode(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       int nr = to_sensor_dev_attr(attr)->index;
+       struct i2c_client *client = to_i2c_client(dev);
+       struct f75375_data *data = i2c_get_clientdata(client);
+       int val = simple_strtoul(buf, NULL, 10);
+       u8 conf = 0;
+
+       if (val != 0 || val != 1 || data->kind == f75373)
+               return -EINVAL;
+
+       mutex_lock(&data->update_lock);
+       conf = f75375_read8(client, F75375_REG_CONFIG1);
+       conf = ~(1 << FAN_CTRL_LINEAR(nr));
+
+       if (val == 0)
+               conf |= (1 << FAN_CTRL_LINEAR(nr)) ;
+
+       f75375_write8(client, F75375_REG_CONFIG1, conf);
+       data->pwm_mode[nr] = val;
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t show_pwm(struct device *dev, struct device_attribute
+               *attr, char *buf)
+{
+       int nr = to_sensor_dev_attr(attr)->index;
+       struct f75375_data *data = f75375_update_device(dev);
+       return sprintf(buf, "%d\n", data->pwm[nr]);
+}
+
+static ssize_t show_pwm_mode(struct device *dev, struct device_attribute
+               *attr, char *buf)
+{
+       int nr = to_sensor_dev_attr(attr)->index;
+       struct f75375_data *data = f75375_update_device(dev);
+       return sprintf(buf, "%d\n", data->pwm_mode[nr]);
+}
+
+#define VOLT_FROM_REG(val) ((val) * 8)
+#define VOLT_TO_REG(val) ((val) / 8)
+
+static ssize_t show_in(struct device *dev, struct device_attribute *attr,
+               char *buf)
+{
+       int nr = to_sensor_dev_attr(attr)->index;
+       struct f75375_data *data = f75375_update_device(dev);
+       return sprintf(buf, "%d\n", VOLT_FROM_REG(data->in[nr]));
+}
+
+static ssize_t show_in_max(struct device *dev, struct device_attribute *attr,
+               char *buf)
+{
+       int nr = to_sensor_dev_attr(attr)->index;
+       struct f75375_data *data = f75375_update_device(dev);
+       return sprintf(buf, "%d\n", VOLT_FROM_REG(data->in_max[nr]));
+}
+
+static ssize_t show_in_min(struct device *dev, struct device_attribute *attr,
+               char *buf)
+{
+       int nr = to_sensor_dev_attr(attr)->index;
+       struct f75375_data *data = f75375_update_device(dev);
+       return sprintf(buf, "%d\n", VOLT_FROM_REG(data->in_min[nr]));
+}
+
+static ssize_t set_in_max(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       int nr = to_sensor_dev_attr(attr)->index;
+       struct i2c_client *client = to_i2c_client(dev);
+       struct f75375_data *data = i2c_get_clientdata(client);
+       int val = simple_strtoul(buf, NULL, 10);
+       val = SENSORS_LIMIT(VOLT_TO_REG(val), 0, 0xff);
+       mutex_lock(&data->update_lock);
+       data->in_max[nr] = val;
+       f75375_write8(client, F75375_REG_VOLT_HIGH(nr), data->in_max[nr]);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t set_in_min(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       int nr = to_sensor_dev_attr(attr)->index;
+       struct i2c_client *client = to_i2c_client(dev);
+       struct f75375_data *data = i2c_get_clientdata(client);
+       int val = simple_strtoul(buf, NULL, 10);
+       val = SENSORS_LIMIT(VOLT_TO_REG(val), 0, 0xff);
+       mutex_lock(&data->update_lock);
+       data->in_min[nr] = val;
+       f75375_write8(client, F75375_REG_VOLT_LOW(nr), data->in_min[nr]);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+#define TEMP_FROM_REG(val) ((val) * 1000)
+#define TEMP_TO_REG(val) ((val) / 1000)
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
+               char *buf)
+{
+       int nr = to_sensor_dev_attr(attr)->index;
+       struct f75375_data *data = f75375_update_device(dev);
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr]));
+}
+
+static ssize_t show_temp_max(struct device *dev, struct device_attribute *attr,
+               char *buf)
+{
+       int nr = to_sensor_dev_attr(attr)->index;
+       struct f75375_data *data = f75375_update_device(dev);
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[nr]));
+}
+
+static ssize_t show_temp_max_hyst(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       int nr = to_sensor_dev_attr(attr)->index;
+       struct f75375_data *data = f75375_update_device(dev);
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max_hyst[nr]));
+}
+
+static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       int nr = to_sensor_dev_attr(attr)->index;
+       struct i2c_client *client = to_i2c_client(dev);
+       struct f75375_data *data = i2c_get_clientdata(client);
+       int val = simple_strtol(buf, NULL, 10);
+       val = SENSORS_LIMIT(TEMP_TO_REG(val), 0, 127);
+       mutex_lock(&data->update_lock);
+       data->temp_high[nr] = val;
+       f75375_write8(client, F75375_REG_TEMP_HIGH(nr), data->temp_high[nr]);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+static ssize_t set_temp_max_hyst(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       int nr = to_sensor_dev_attr(attr)->index;
+       struct i2c_client *client = to_i2c_client(dev);
+       struct f75375_data *data = i2c_get_clientdata(client);
+       int val = simple_strtol(buf, NULL, 10);
+       val = SENSORS_LIMIT(TEMP_TO_REG(val), 0, 127);
+       mutex_lock(&data->update_lock);
+       data->temp_max_hyst[nr] = val;
+       f75375_write8(client, F75375_REG_TEMP_HYST(nr),
+               data->temp_max_hyst[nr]);
+       mutex_unlock(&data->update_lock);
+       return count;
+}
+
+#define show_fan(thing) \
+static ssize_t show_##thing(struct device *dev, struct device_attribute *attr, \
+                       char *buf)\
+{\
+       int nr = to_sensor_dev_attr(attr)->index;\
+       struct f75375_data *data = f75375_update_device(dev); \
+       return sprintf(buf, "%d\n", rpm_from_reg(data->thing[nr])); \
+}
+
+show_fan(fan);
+show_fan(fan_min);
+show_fan(fan_full);
+show_fan(fan_exp);
+
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0);
+static SENSOR_DEVICE_ATTR(in0_max, S_IRUGO|S_IWUSR,
+       show_in_max, set_in_max, 0);
+static SENSOR_DEVICE_ATTR(in0_min, S_IRUGO|S_IWUSR,
+       show_in_min, set_in_min, 0);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1);
+static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO|S_IWUSR,
+       show_in_max, set_in_max, 1);
+static SENSOR_DEVICE_ATTR(in1_min, S_IRUGO|S_IWUSR,
+       show_in_min, set_in_min, 1);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2);
+static SENSOR_DEVICE_ATTR(in2_max, S_IRUGO|S_IWUSR,
+       show_in_max, set_in_max, 2);
+static SENSOR_DEVICE_ATTR(in2_min, S_IRUGO|S_IWUSR,
+       show_in_min, set_in_min, 2);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3);
+static SENSOR_DEVICE_ATTR(in3_max, S_IRUGO|S_IWUSR,
+       show_in_max, set_in_max, 3);
+static SENSOR_DEVICE_ATTR(in3_min, S_IRUGO|S_IWUSR,
+       show_in_min, set_in_min, 3);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO|S_IWUSR,
+       show_temp_max_hyst, set_temp_max_hyst, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO|S_IWUSR,
+       show_temp_max, set_temp_max, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IRUGO|S_IWUSR,
+       show_temp_max_hyst, set_temp_max_hyst, 1);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO|S_IWUSR,
+       show_temp_max, set_temp_max, 1);
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan1_full, S_IRUGO, show_fan_full, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO|S_IWUSR,
+       show_fan_min, set_fan_min, 0);
+static SENSOR_DEVICE_ATTR(fan1_exp, S_IRUGO|S_IWUSR,
+       show_fan_exp, set_fan_exp, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan2_full, S_IRUGO, show_fan_full, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO|S_IWUSR,
+       show_fan_min, set_fan_min, 1);
+static SENSOR_DEVICE_ATTR(fan2_exp, S_IRUGO|S_IWUSR,
+       show_fan_exp, set_fan_exp, 1);
+static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO|S_IWUSR,
+       show_pwm, set_pwm, 0);
+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO|S_IWUSR,
+       show_pwm_enable, set_pwm_enable, 0);
+static SENSOR_DEVICE_ATTR(pwm1_mode, S_IRUGO|S_IWUSR,
+       show_pwm_mode, set_pwm_mode, 0);
+static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR,
+       show_pwm, set_pwm, 1);
+static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO|S_IWUSR,
+       show_pwm_enable, set_pwm_enable, 1);
+static SENSOR_DEVICE_ATTR(pwm2_mode, S_IRUGO|S_IWUSR,
+       show_pwm_mode, set_pwm_mode, 1);
+
+static struct attribute *f75375_attributes[] = {
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp1_max.dev_attr.attr,
+       &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp2_input.dev_attr.attr,
+       &sensor_dev_attr_temp2_max.dev_attr.attr,
+       &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+       &sensor_dev_attr_fan1_input.dev_attr.attr,
+       &sensor_dev_attr_fan1_full.dev_attr.attr,
+       &sensor_dev_attr_fan1_min.dev_attr.attr,
+       &sensor_dev_attr_fan1_exp.dev_attr.attr,
+       &sensor_dev_attr_fan2_input.dev_attr.attr,
+       &sensor_dev_attr_fan2_full.dev_attr.attr,
+       &sensor_dev_attr_fan2_min.dev_attr.attr,
+       &sensor_dev_attr_fan2_exp.dev_attr.attr,
+       &sensor_dev_attr_pwm1.dev_attr.attr,
+       &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+       &sensor_dev_attr_pwm1_mode.dev_attr.attr,
+       &sensor_dev_attr_pwm2.dev_attr.attr,
+       &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+       &sensor_dev_attr_pwm2_mode.dev_attr.attr,
+       &sensor_dev_attr_in0_input.dev_attr.attr,
+       &sensor_dev_attr_in0_max.dev_attr.attr,
+       &sensor_dev_attr_in0_min.dev_attr.attr,
+       &sensor_dev_attr_in1_input.dev_attr.attr,
+       &sensor_dev_attr_in1_max.dev_attr.attr,
+       &sensor_dev_attr_in1_min.dev_attr.attr,
+       &sensor_dev_attr_in2_input.dev_attr.attr,
+       &sensor_dev_attr_in2_max.dev_attr.attr,
+       &sensor_dev_attr_in2_min.dev_attr.attr,
+       &sensor_dev_attr_in3_input.dev_attr.attr,
+       &sensor_dev_attr_in3_max.dev_attr.attr,
+       &sensor_dev_attr_in3_min.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group f75375_group = {
+       .attrs = f75375_attributes,
+};
+
+static int f75375_detach_client(struct i2c_client *client)
+{
+       struct f75375_data *data = i2c_get_clientdata(client);
+       int err;
+
+       hwmon_device_unregister(data->hwmon_dev);
+       sysfs_remove_group(&client->dev.kobj, &f75375_group);
+
+       err = i2c_detach_client(client);
+       if (err) {
+               dev_err(&client->dev,
+                       "Client deregistration failed, "
+                       "client not detached.\n");
+               return err;
+       }
+       kfree(data);
+       return 0;
+}
+
+static int f75375_attach_adapter(struct i2c_adapter *adapter)
+{
+       if (!(adapter->class & I2C_CLASS_HWMON))
+               return 0;
+       return i2c_probe(adapter, &addr_data, f75375_detect);
+}
+
+/* This function is called by i2c_probe */
+static int f75375_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+       struct i2c_client *client;
+       struct f75375_data *data;
+       u8 version = 0;
+       int err = 0;
+       const char *name = "";
+
+       if (!(data = kzalloc(sizeof(struct f75375_data), GFP_KERNEL))) {
+               err = -ENOMEM;
+               goto exit;
+       }
+       client = &data->client;
+       i2c_set_clientdata(client, data);
+       client->addr = address;
+       client->adapter = adapter;
+       client->driver = &f75375_driver;
+
+       if (kind < 0) {
+               u16 vendid = f75375_read16(client, F75375_REG_VENDOR);
+               u16 chipid = f75375_read16(client, F75375_CHIP_ID);
+               version = f75375_read8(client, F75375_REG_VERSION);
+               if (chipid == 0x0306 && vendid == 0x1934) {
+                       kind = f75375;
+               } else if (chipid == 0x0204 && vendid == 0x1934) {
+                       kind = f75373;
+               } else {
+                       dev_err(&adapter->dev,
+                               "failed,%02X,%02X,%02X\n",
+                               chipid, version, vendid);
+                       goto exit_free;
+               }
+       }
+
+       if (kind == f75375) {
+               name = "f75375";
+       } else if (kind == f75373) {
+               name = "f75373";
+       }
+
+       dev_info(&adapter->dev, "found %s version: %02X\n", name, version);
+       strlcpy(client->name, name, I2C_NAME_SIZE);
+       data->kind = kind;
+       mutex_init(&data->update_lock);
+       if ((err = i2c_attach_client(client)))
+               goto exit_free;
+
+       if ((err = sysfs_create_group(&client->dev.kobj, &f75375_group)))
+               goto exit_detach;
+
+       data->hwmon_dev = hwmon_device_register(&client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
+               goto exit_remove;
+       }
+
+       return 0;
+
+exit_remove:
+       sysfs_remove_group(&client->dev.kobj, &f75375_group);
+exit_detach:
+       i2c_detach_client(client);
+exit_free:
+       kfree(data);
+exit:
+       return err;
+}
+
+static int __init sensors_f75375_init(void)
+{
+       return i2c_add_driver(&f75375_driver);
+}
+
+static void __exit sensors_f75375_exit(void)
+{
+       i2c_del_driver(&f75375_driver);
+}
+
+MODULE_AUTHOR("Riku Voipio <riku.voipio@movial.fi>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("F75373/F75375 hardware monitoring driver");
+
+module_init(sensors_f75375_init);
+module_exit(sensors_f75375_exit);
index b34b546..e67c369 100644 (file)
@@ -134,7 +134,7 @@ static struct i2c_driver fscher_driver = {
 
 struct fscher_data {
        struct i2c_client client;
-       struct class_device *class_dev;
+       struct device *hwmon_dev;
        struct mutex update_lock;
        char valid; /* zero until following fields are valid */
        unsigned long last_updated; /* in jiffies */
@@ -344,9 +344,9 @@ static int fscher_detect(struct i2c_adapter *adapter, int address, int kind)
        if ((err = sysfs_create_group(&new_client->dev.kobj, &fscher_group)))
                goto exit_detach;
 
-       data->class_dev = hwmon_device_register(&new_client->dev);
-       if (IS_ERR(data->class_dev)) {
-               err = PTR_ERR(data->class_dev);
+       data->hwmon_dev = hwmon_device_register(&new_client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
                goto exit_remove_files;
        }
 
@@ -367,7 +367,7 @@ static int fscher_detach_client(struct i2c_client *client)
        struct fscher_data *data = i2c_get_clientdata(client);
        int err;
 
-       hwmon_device_unregister(data->class_dev);
+       hwmon_device_unregister(data->hwmon_dev);
        sysfs_remove_group(&client->dev.kobj, &fscher_group);
 
        if ((err = i2c_detach_client(client)))
diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c
new file mode 100644 (file)
index 0000000..63a4df0
--- /dev/null
@@ -0,0 +1,778 @@
+/* fschmd.c
+ *
+ * Copyright (C) 2007 Hans de Goede <j.w.r.degoede@hhs.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ *  Merged Fujitsu Siemens hwmon driver, supporting the Poseidon, Hermes,
+ *  Scylla, Heracles and Heimdall chips
+ *
+ *  Based on the original 2.4 fscscy, 2.6 fscpos, 2.6 fscher and 2.6
+ *  (candidate) fschmd drivers:
+ *  Copyright (C) 2006 Thilo Cestonaro
+ *                     <thilo.cestonaro.external@fujitsu-siemens.com>
+ *  Copyright (C) 2004, 2005 Stefan Ott <stefan@desire.ch>
+ *  Copyright (C) 2003, 2004 Reinhard Nissl <rnissl@gmx.de>
+ *  Copyright (c) 2001 Martin Knoblauch <mkn@teraport.de, knobi@knobisoft.de>
+ *  Copyright (C) 2000 Hermann Jung <hej@odn.de>
+ */
+
+#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>
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd);
+
+/*
+ * The FSCHMD registers and other defines
+ */
+
+/* chip identification */
+#define FSCHMD_REG_IDENT_0             0x00
+#define FSCHMD_REG_IDENT_1             0x01
+#define FSCHMD_REG_IDENT_2             0x02
+#define FSCHMD_REG_REVISION            0x03
+
+/* global control and status */
+#define FSCHMD_REG_EVENT_STATE         0x04
+#define FSCHMD_REG_CONTROL             0x05
+
+#define FSCHMD_CONTROL_ALERT_LED_MASK  0x01
+
+/* watchdog (support to be implemented) */
+#define FSCHMD_REG_WDOG_PRESET         0x28
+#define FSCHMD_REG_WDOG_STATE          0x23
+#define FSCHMD_REG_WDOG_CONTROL                0x21
+
+/* voltages, weird order is to keep the same order as the old drivers */
+static const u8 FSCHMD_REG_VOLT[3] = { 0x45, 0x42, 0x48 };
+
+/* minimum pwm at which the fan is driven (pwm can by increased depending on
+   the temp. Notice that for the scy some fans share there minimum speed.
+   Also notice that with the scy the sensor order is different then with the
+   other chips, this order was in the 2.4 driver and kept for consistency. */
+static const u8 FSCHMD_REG_FAN_MIN[5][6] = {
+       { 0x55, 0x65 },                                 /* pos */
+       { 0x55, 0x65, 0xb5 },                           /* her */
+       { 0x65, 0x65, 0x55, 0xa5, 0x55, 0xa5 },         /* scy */
+       { 0x55, 0x65, 0xa5, 0xb5 },                     /* hrc */
+       { 0x55, 0x65, 0xa5, 0xb5, 0xc5 },               /* hmd */
+};
+
+/* actual fan speed */
+static const u8 FSCHMD_REG_FAN_ACT[5][6] = {
+       { 0x0e, 0x6b, 0xab },                           /* pos */
+       { 0x0e, 0x6b, 0xbb },                           /* her */
+       { 0x6b, 0x6c, 0x0e, 0xab, 0x5c, 0xbb },         /* scy */
+       { 0x0e, 0x6b, 0xab, 0xbb },                     /* hrc */
+       { 0x5b, 0x6b, 0xab, 0xbb, 0xcb },               /* hmd */
+};
+
+/* fan status registers */
+static const u8 FSCHMD_REG_FAN_STATE[5][6] = {
+       { 0x0d, 0x62, 0xa2 },                           /* pos */
+       { 0x0d, 0x62, 0xb2 },                           /* her */
+       { 0x62, 0x61, 0x0d, 0xa2, 0x52, 0xb2 },         /* scy */
+       { 0x0d, 0x62, 0xa2, 0xb2 },                     /* hrc */
+       { 0x52, 0x62, 0xa2, 0xb2, 0xc2 },               /* hmd */
+};
+
+/* fan ripple / divider registers */
+static const u8 FSCHMD_REG_FAN_RIPPLE[5][6] = {
+       { 0x0f, 0x6f, 0xaf },                           /* pos */
+       { 0x0f, 0x6f, 0xbf },                           /* her */
+       { 0x6f, 0x6f, 0x0f, 0xaf, 0x0f, 0xbf },         /* scy */
+       { 0x0f, 0x6f, 0xaf, 0xbf },                     /* hrc */
+       { 0x5f, 0x6f, 0xaf, 0xbf, 0xcf },               /* hmd */
+};
+
+static const int FSCHMD_NO_FAN_SENSORS[5] = { 3, 3, 6, 4, 5 };
+
+/* Fan status register bitmasks */
+#define FSCHMD_FAN_ALARM_MASK          0x04 /* called fault by FSC! */
+#define FSCHMD_FAN_NOT_PRESENT_MASK    0x08 /* not documented */
+
+
+/* actual temperature registers */
+static const u8 FSCHMD_REG_TEMP_ACT[5][5] = {
+       { 0x64, 0x32, 0x35 },                           /* pos */
+       { 0x64, 0x32, 0x35 },                           /* her */
+       { 0x64, 0xD0, 0x32, 0x35 },                     /* scy */
+       { 0x64, 0x32, 0x35 },                           /* hrc */
+       { 0x70, 0x80, 0x90, 0xd0, 0xe0 },               /* hmd */
+};
+
+/* temperature state registers */
+static const u8 FSCHMD_REG_TEMP_STATE[5][5] = {
+       { 0x71, 0x81, 0x91 },                           /* pos */
+       { 0x71, 0x81, 0x91 },                           /* her */
+       { 0x71, 0xd1, 0x81, 0x91 },                     /* scy */
+       { 0x71, 0x81, 0x91 },                           /* hrc */
+       { 0x71, 0x81, 0x91, 0xd1, 0xe1 },               /* hmd */
+};
+
+/* temperature high limit registers, FSC does not document these. Proven to be
+   there with field testing on the fscher and fschrc, already supported / used
+   in the fscscy 2.4 driver. FSC has confirmed that the fschmd has registers
+   at these addresses, but doesn't want to confirm they are the same as with
+   the fscher?? */
+static const u8 FSCHMD_REG_TEMP_LIMIT[5][5] = {
+       { 0, 0, 0 },                                    /* pos */
+       { 0x76, 0x86, 0x96 },                           /* her */
+       { 0x76, 0xd6, 0x86, 0x96 },                     /* scy */
+       { 0x76, 0x86, 0x96 },                           /* hrc */
+       { 0x76, 0x86, 0x96, 0xd6, 0xe6 },               /* hmd */
+};
+
+/* These were found through experimenting with an fscher, currently they are
+   not used, but we keep them around for future reference.
+static const u8 FSCHER_REG_TEMP_AUTOP1[] =     { 0x73, 0x83, 0x93 };
+static const u8 FSCHER_REG_TEMP_AUTOP2[] =     { 0x75, 0x85, 0x95 }; */
+
+static const int FSCHMD_NO_TEMP_SENSORS[5] = { 3, 3, 4, 3, 5 };
+
+/* temp status register bitmasks */
+#define FSCHMD_TEMP_WORKING_MASK       0x01
+#define FSCHMD_TEMP_ALERT_MASK         0x02
+/* there only really is an alarm if the sensor is working and alert == 1 */
+#define FSCHMD_TEMP_ALARM_MASK \
+       (FSCHMD_TEMP_WORKING_MASK | FSCHMD_TEMP_ALERT_MASK)
+
+/* our driver name */
+#define FSCHMD_NAME "fschmd"
+
+/*
+ * Functions declarations
+ */
+
+static int fschmd_attach_adapter(struct i2c_adapter *adapter);
+static int fschmd_detach_client(struct i2c_client *client);
+static struct fschmd_data *fschmd_update_device(struct device *dev);
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static struct i2c_driver fschmd_driver = {
+       .driver = {
+               .name   = FSCHMD_NAME,
+       },
+       .attach_adapter = fschmd_attach_adapter,
+       .detach_client  = fschmd_detach_client,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct fschmd_data {
+       struct i2c_client client;
+       struct device *hwmon_dev;
+       struct mutex update_lock;
+       int kind;
+       char valid; /* zero until following fields are valid */
+       unsigned long last_updated; /* in jiffies */
+
+       /* register values */
+       u8 global_control;      /* global control register */
+       u8 volt[3];             /* 12, 5, battery voltage */
+       u8 temp_act[5];         /* temperature */
+       u8 temp_status[5];      /* status of sensor */
+       u8 temp_max[5];         /* high temp limit, notice: undocumented! */
+       u8 fan_act[6];          /* fans revolutions per second */
+       u8 fan_status[6];       /* fan status */
+       u8 fan_min[6];          /* fan min value for rps */
+       u8 fan_ripple[6];       /* divider for rps */
+};
+
+/*
+ * Sysfs attr show / store functions
+ */
+
+static ssize_t show_in_value(struct device *dev,
+       struct device_attribute *devattr, char *buf)
+{
+       const int max_reading[3] = { 14200, 6600, 3300 };
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct fschmd_data *data = fschmd_update_device(dev);
+
+       return sprintf(buf, "%d\n", (data->volt[index] *
+               max_reading[index] + 128) / 255);
+}
+
+
+#define TEMP_FROM_REG(val)     (((val) - 128) * 1000)
+
+static ssize_t show_temp_value(struct device *dev,
+       struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct fschmd_data *data = fschmd_update_device(dev);
+
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[index]));
+}
+
+static ssize_t show_temp_max(struct device *dev,
+       struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct fschmd_data *data = fschmd_update_device(dev);
+
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[index]));
+}
+
+static ssize_t store_temp_max(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct fschmd_data *data = dev_get_drvdata(dev);
+       long v = simple_strtol(buf, NULL, 10) / 1000;
+
+       v = SENSORS_LIMIT(v, -128, 127) + 128;
+
+       mutex_lock(&data->update_lock);
+       i2c_smbus_write_byte_data(&data->client,
+               FSCHMD_REG_TEMP_LIMIT[data->kind][index], v);
+       data->temp_max[index] = v;
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t show_temp_fault(struct device *dev,
+       struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct fschmd_data *data = fschmd_update_device(dev);
+
+       /* bit 0 set means sensor working ok, so no fault! */
+       if (data->temp_status[index] & FSCHMD_TEMP_WORKING_MASK)
+               return sprintf(buf, "0\n");
+       else
+               return sprintf(buf, "1\n");
+}
+
+static ssize_t show_temp_alarm(struct device *dev,
+       struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct fschmd_data *data = fschmd_update_device(dev);
+
+       if ((data->temp_status[index] & FSCHMD_TEMP_ALARM_MASK) ==
+                       FSCHMD_TEMP_ALARM_MASK)
+               return sprintf(buf, "1\n");
+       else
+               return sprintf(buf, "0\n");
+}
+
+
+#define RPM_FROM_REG(val)      ((val) * 60)
+
+static ssize_t show_fan_value(struct device *dev,
+       struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct fschmd_data *data = fschmd_update_device(dev);
+
+       return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[index]));
+}
+
+static ssize_t show_fan_div(struct device *dev,
+       struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct fschmd_data *data = fschmd_update_device(dev);
+
+       /* bits 2..7 reserved => mask with 3 */
+       return sprintf(buf, "%d\n", 1 << (data->fan_ripple[index] & 3));
+}
+
+static ssize_t store_fan_div(struct device *dev, struct device_attribute
+       *devattr, const char *buf, size_t count)
+{
+       u8 reg;
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct fschmd_data *data = dev_get_drvdata(dev);
+       /* supported values: 2, 4, 8 */
+       unsigned long v = simple_strtoul(buf, NULL, 10);
+
+       switch (v) {
+       case 2: v = 1; break;
+       case 4: v = 2; break;
+       case 8: v = 3; break;
+       default:
+               dev_err(dev, "fan_div value %lu not supported. "
+                       "Choose one of 2, 4 or 8!\n", v);
+               return -EINVAL;
+       }
+
+       mutex_lock(&data->update_lock);
+
+       reg = i2c_smbus_read_byte_data(&data->client,
+               FSCHMD_REG_FAN_RIPPLE[data->kind][index]);
+
+       /* bits 2..7 reserved => mask with 0x03 */
+       reg &= ~0x03;
+       reg |= v;
+
+       i2c_smbus_write_byte_data(&data->client,
+               FSCHMD_REG_FAN_RIPPLE[data->kind][index], reg);
+
+       data->fan_ripple[index] = reg;
+
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t show_fan_alarm(struct device *dev,
+       struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct fschmd_data *data = fschmd_update_device(dev);
+
+       if (data->fan_status[index] & FSCHMD_FAN_ALARM_MASK)
+               return sprintf(buf, "1\n");
+       else
+               return sprintf(buf, "0\n");
+}
+
+static ssize_t show_fan_fault(struct device *dev,
+       struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct fschmd_data *data = fschmd_update_device(dev);
+
+       if (data->fan_status[index] & FSCHMD_FAN_NOT_PRESENT_MASK)
+               return sprintf(buf, "1\n");
+       else
+               return sprintf(buf, "0\n");
+}
+
+
+static ssize_t show_pwm_auto_point1_pwm(struct device *dev,
+       struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       int val = fschmd_update_device(dev)->fan_min[index];
+
+       /* 0 = allow turning off, 1-255 = 50-100% */
+       if (val)
+               val = val / 2 + 128;
+
+       return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t store_pwm_auto_point1_pwm(struct device *dev,
+       struct device_attribute *devattr, const char *buf, size_t count)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct fschmd_data *data = dev_get_drvdata(dev);
+       unsigned long v = simple_strtoul(buf, NULL, 10);
+
+       /* register: 0 = allow turning off, 1-255 = 50-100% */
+       if (v) {
+               v = SENSORS_LIMIT(v, 128, 255);
+               v = (v - 128) * 2 + 1;
+       }
+
+       mutex_lock(&data->update_lock);
+
+       i2c_smbus_write_byte_data(&data->client,
+               FSCHMD_REG_FAN_MIN[data->kind][index], v);
+       data->fan_min[index] = v;
+
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+
+/* The FSC hwmon family has the ability to force an attached alert led to flash
+   from software, we export this as an alert_led sysfs attr */
+static ssize_t show_alert_led(struct device *dev,
+       struct device_attribute *devattr, char *buf)
+{
+       struct fschmd_data *data = fschmd_update_device(dev);
+
+       if (data->global_control & FSCHMD_CONTROL_ALERT_LED_MASK)
+               return sprintf(buf, "1\n");
+       else
+               return sprintf(buf, "0\n");
+}
+
+static ssize_t store_alert_led(struct device *dev,
+       struct device_attribute *devattr, const char *buf, size_t count)
+{
+       u8 reg;
+       struct fschmd_data *data = dev_get_drvdata(dev);
+       unsigned long v = simple_strtoul(buf, NULL, 10);
+
+       mutex_lock(&data->update_lock);
+
+       reg = i2c_smbus_read_byte_data(&data->client, FSCHMD_REG_CONTROL);
+
+       if (v)
+               reg |= FSCHMD_CONTROL_ALERT_LED_MASK;
+       else
+               reg &= ~FSCHMD_CONTROL_ALERT_LED_MASK;
+
+       i2c_smbus_write_byte_data(&data->client, FSCHMD_REG_CONTROL, reg);
+
+       data->global_control = reg;
+
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static struct sensor_device_attribute fschmd_attr[] = {
+       SENSOR_ATTR(in0_input, 0444, show_in_value, NULL, 0),
+       SENSOR_ATTR(in1_input, 0444, show_in_value, NULL, 1),
+       SENSOR_ATTR(in2_input, 0444, show_in_value, NULL, 2),
+       SENSOR_ATTR(alert_led, 0644, show_alert_led, store_alert_led, 0),
+};
+
+static struct sensor_device_attribute fschmd_temp_attr[] = {
+       SENSOR_ATTR(temp1_input, 0444, show_temp_value, NULL, 0),
+       SENSOR_ATTR(temp1_max,   0644, show_temp_max, store_temp_max, 0),
+       SENSOR_ATTR(temp1_fault, 0444, show_temp_fault, NULL, 0),
+       SENSOR_ATTR(temp1_alarm, 0444, show_temp_alarm, NULL, 0),
+       SENSOR_ATTR(temp2_input, 0444, show_temp_value, NULL, 1),
+       SENSOR_ATTR(temp2_max,   0644, show_temp_max, store_temp_max, 1),
+       SENSOR_ATTR(temp2_fault, 0444, show_temp_fault, NULL, 1),
+       SENSOR_ATTR(temp2_alarm, 0444, show_temp_alarm, NULL, 1),
+       SENSOR_ATTR(temp3_input, 0444, show_temp_value, NULL, 2),
+       SENSOR_ATTR(temp3_max,   0644, show_temp_max, store_temp_max, 2),
+       SENSOR_ATTR(temp3_fault, 0444, show_temp_fault, NULL, 2),
+       SENSOR_ATTR(temp3_alarm, 0444, show_temp_alarm, NULL, 2),
+       SENSOR_ATTR(temp4_input, 0444, show_temp_value, NULL, 3),
+       SENSOR_ATTR(temp4_max,   0644, show_temp_max, store_temp_max, 3),
+       SENSOR_ATTR(temp4_fault, 0444, show_temp_fault, NULL, 3),
+       SENSOR_ATTR(temp4_alarm, 0444, show_temp_alarm, NULL, 3),
+       SENSOR_ATTR(temp5_input, 0444, show_temp_value, NULL, 4),
+       SENSOR_ATTR(temp5_max,   0644, show_temp_max, store_temp_max, 4),
+       SENSOR_ATTR(temp5_fault, 0444, show_temp_fault, NULL, 4),
+       SENSOR_ATTR(temp5_alarm, 0444, show_temp_alarm, NULL, 4),
+};
+
+static struct sensor_device_attribute fschmd_fan_attr[] = {
+       SENSOR_ATTR(fan1_input, 0444, show_fan_value, NULL, 0),
+       SENSOR_ATTR(fan1_div,   0644, show_fan_div, store_fan_div, 0),
+       SENSOR_ATTR(fan1_alarm, 0444, show_fan_alarm, NULL, 0),
+       SENSOR_ATTR(fan1_fault, 0444, show_fan_fault, NULL, 0),
+       SENSOR_ATTR(pwm1_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm,
+               store_pwm_auto_point1_pwm, 0),
+       SENSOR_ATTR(fan2_input, 0444, show_fan_value, NULL, 1),
+       SENSOR_ATTR(fan2_div,   0644, show_fan_div, store_fan_div, 1),
+       SENSOR_ATTR(fan2_alarm, 0444, show_fan_alarm, NULL, 1),
+       SENSOR_ATTR(fan2_fault, 0444, show_fan_fault, NULL, 1),
+       SENSOR_ATTR(pwm2_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm,
+               store_pwm_auto_point1_pwm, 1),
+       SENSOR_ATTR(fan3_input, 0444, show_fan_value, NULL, 2),
+       SENSOR_ATTR(fan3_div,   0644, show_fan_div, store_fan_div, 2),
+       SENSOR_ATTR(fan3_alarm, 0444, show_fan_alarm, NULL, 2),
+       SENSOR_ATTR(fan3_fault, 0444, show_fan_fault, NULL, 2),
+       SENSOR_ATTR(pwm3_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm,
+               store_pwm_auto_point1_pwm, 2),
+       SENSOR_ATTR(fan4_input, 0444, show_fan_value, NULL, 3),
+       SENSOR_ATTR(fan4_div,   0644, show_fan_div, store_fan_div, 3),
+       SENSOR_ATTR(fan4_alarm, 0444, show_fan_alarm, NULL, 3),
+       SENSOR_ATTR(fan4_fault, 0444, show_fan_fault, NULL, 3),
+       SENSOR_ATTR(pwm4_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm,
+               store_pwm_auto_point1_pwm, 3),
+       SENSOR_ATTR(fan5_input, 0444, show_fan_value, NULL, 4),
+       SENSOR_ATTR(fan5_div,   0644, show_fan_div, store_fan_div, 4),
+       SENSOR_ATTR(fan5_alarm, 0444, show_fan_alarm, NULL, 4),
+       SENSOR_ATTR(fan5_fault, 0444, show_fan_fault, NULL, 4),
+       SENSOR_ATTR(pwm5_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm,
+               store_pwm_auto_point1_pwm, 4),
+       SENSOR_ATTR(fan6_input, 0444, show_fan_value, NULL, 5),
+       SENSOR_ATTR(fan6_div,   0644, show_fan_div, store_fan_div, 5),
+       SENSOR_ATTR(fan6_alarm, 0444, show_fan_alarm, NULL, 5),
+       SENSOR_ATTR(fan6_fault, 0444, show_fan_fault, NULL, 5),
+       SENSOR_ATTR(pwm6_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm,
+               store_pwm_auto_point1_pwm, 5),
+};
+
+
+/*
+ * Real code
+ */
+
+static int fschmd_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+       struct i2c_client *client;
+       struct fschmd_data *data;
+       u8 revision;
+       const char * const names[5] = { "Poseidon", "Hermes", "Scylla",
+                                       "Heracles", "Heimdall" };
+       const char * const client_names[5] = { "fscpos", "fscher", "fscscy",
+                                               "fschrc", "fschmd" };
+       int i, err = 0;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return 0;
+
+       /* OK. For now, we presume we have a valid client. We now create the
+        * client structure, even though we cannot fill it completely yet.
+        * But it allows us to access i2c_smbus_read_byte_data. */
+       if (!(data = kzalloc(sizeof(struct fschmd_data), GFP_KERNEL)))
+               return -ENOMEM;
+
+       client = &data->client;
+       i2c_set_clientdata(client, data);
+       client->addr = address;
+       client->adapter = adapter;
+       client->driver = &fschmd_driver;
+       mutex_init(&data->update_lock);
+
+       /* Detect & Identify the chip */
+       if (kind <= 0) {
+               char id[4];
+
+               id[0] = i2c_smbus_read_byte_data(client,
+                               FSCHMD_REG_IDENT_0);
+               id[1] = i2c_smbus_read_byte_data(client,
+                               FSCHMD_REG_IDENT_1);
+               id[2] = i2c_smbus_read_byte_data(client,
+                               FSCHMD_REG_IDENT_2);
+               id[3] = '\0';
+
+               if (!strcmp(id, "PEG"))
+                       kind = fscpos;
+               else if (!strcmp(id, "HER"))
+                       kind = fscher;
+               else if (!strcmp(id, "SCY"))
+                       kind = fscscy;
+               else if (!strcmp(id, "HRC"))
+                       kind = fschrc;
+               else if (!strcmp(id, "HMD"))
+                       kind = fschmd;
+               else
+                       goto exit_free;
+       }
+
+       if (kind == fscpos) {
+               /* The Poseidon has hardwired temp limits, fill these
+                  in for the alarm resetting code */
+               data->temp_max[0] = 70 + 128;
+               data->temp_max[1] = 50 + 128;
+               data->temp_max[2] = 50 + 128;
+       }
+
+       /* i2c kind goes from 1-5, we want from 0-4 to address arrays */
+       data->kind = kind - 1;
+       strlcpy(client->name, client_names[data->kind], I2C_NAME_SIZE);
+
+       /* Tell the I2C layer a new client has arrived */
+       if ((err = i2c_attach_client(client)))
+               goto exit_free;
+
+       for (i = 0; i < ARRAY_SIZE(fschmd_attr); i++) {
+               err = device_create_file(&client->dev,
+                                       &fschmd_attr[i].dev_attr);
+               if (err)
+                       goto exit_detach;
+       }
+
+       for (i = 0; i < (FSCHMD_NO_TEMP_SENSORS[data->kind] * 4); i++) {
+               /* Poseidon doesn't have TEMP_LIMIT registers */
+               if (kind == fscpos && fschmd_temp_attr[i].dev_attr.show ==
+                               show_temp_max)
+                       continue;
+
+               err = device_create_file(&client->dev,
+                                       &fschmd_temp_attr[i].dev_attr);
+               if (err)
+                       goto exit_detach;
+       }
+
+       for (i = 0; i < (FSCHMD_NO_FAN_SENSORS[data->kind] * 5); i++) {
+               /* Poseidon doesn't have a FAN_MIN register for its 3rd fan */
+               if (kind == fscpos &&
+                               !strcmp(fschmd_fan_attr[i].dev_attr.attr.name,
+                                       "pwm3_auto_point1_pwm"))
+                       continue;
+
+               err = device_create_file(&client->dev,
+                                       &fschmd_fan_attr[i].dev_attr);
+               if (err)
+                       goto exit_detach;
+       }
+
+       data->hwmon_dev = hwmon_device_register(&client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
+               data->hwmon_dev = NULL;
+               goto exit_detach;
+       }
+
+       revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION);
+       printk(KERN_INFO FSCHMD_NAME ": Detected FSC %s chip, revision: %d\n",
+               names[data->kind], (int) revision);
+
+       return 0;
+
+exit_detach:
+       fschmd_detach_client(client); /* will also free data for us */
+       return err;
+
+exit_free:
+       kfree(data);
+       return err;
+}
+
+static int fschmd_attach_adapter(struct i2c_adapter *adapter)
+{
+       if (!(adapter->class & I2C_CLASS_HWMON))
+               return 0;
+       return i2c_probe(adapter, &addr_data, fschmd_detect);
+}
+
+static int fschmd_detach_client(struct i2c_client *client)
+{
+       struct fschmd_data *data = i2c_get_clientdata(client);
+       int i, err;
+
+       /* Check if registered in case we're called from fschmd_detect
+          to cleanup after an error */
+       if (data->hwmon_dev)
+               hwmon_device_unregister(data->hwmon_dev);
+
+       for (i = 0; i < ARRAY_SIZE(fschmd_attr); i++)
+               device_remove_file(&client->dev, &fschmd_attr[i].dev_attr);
+       for (i = 0; i < (FSCHMD_NO_TEMP_SENSORS[data->kind] * 4); i++)
+               device_remove_file(&client->dev,
+                                       &fschmd_temp_attr[i].dev_attr);
+       for (i = 0; i < (FSCHMD_NO_FAN_SENSORS[data->kind] * 5); i++)
+               device_remove_file(&client->dev,
+                                       &fschmd_fan_attr[i].dev_attr);
+
+       if ((err = i2c_detach_client(client)))
+               return err;
+
+       kfree(data);
+       return 0;
+}
+
+static struct fschmd_data *fschmd_update_device(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct fschmd_data *data = i2c_get_clientdata(client);
+       int i;
+
+       mutex_lock(&data->update_lock);
+
+       if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) {
+
+               for (i = 0; i < FSCHMD_NO_TEMP_SENSORS[data->kind]; i++) {
+                       data->temp_act[i] = i2c_smbus_read_byte_data(client,
+                                       FSCHMD_REG_TEMP_ACT[data->kind][i]);
+                       data->temp_status[i] = i2c_smbus_read_byte_data(client,
+                                       FSCHMD_REG_TEMP_STATE[data->kind][i]);
+
+                       /* The fscpos doesn't have TEMP_LIMIT registers */
+                       if (FSCHMD_REG_TEMP_LIMIT[data->kind][i])
+                               data->temp_max[i] = i2c_smbus_read_byte_data(
+                                       client,
+                                       FSCHMD_REG_TEMP_LIMIT[data->kind][i]);
+
+                       /* reset alarm if the alarm condition is gone,
+                          the chip doesn't do this itself */
+                       if ((data->temp_status[i] & FSCHMD_TEMP_ALARM_MASK) ==
+                                       FSCHMD_TEMP_ALARM_MASK &&
+                                       data->temp_act[i] < data->temp_max[i])
+                               i2c_smbus_write_byte_data(client,
+                                       FSCHMD_REG_TEMP_STATE[data->kind][i],
+                                       FSCHMD_TEMP_ALERT_MASK);
+               }
+
+               for (i = 0; i < FSCHMD_NO_FAN_SENSORS[data->kind]; i++) {
+                       data->fan_act[i] = i2c_smbus_read_byte_data(client,
+                                       FSCHMD_REG_FAN_ACT[data->kind][i]);
+                       data->fan_status[i] = i2c_smbus_read_byte_data(client,
+                                       FSCHMD_REG_FAN_STATE[data->kind][i]);
+                       data->fan_ripple[i] = i2c_smbus_read_byte_data(client,
+                                       FSCHMD_REG_FAN_RIPPLE[data->kind][i]);
+
+                       /* The fscpos third fan doesn't have a fan_min */
+                       if (FSCHMD_REG_FAN_MIN[data->kind][i])
+                               data->fan_min[i] = i2c_smbus_read_byte_data(
+                                       client,
+                                       FSCHMD_REG_FAN_MIN[data->kind][i]);
+
+                       /* reset fan status if speed is back to > 0 */
+                       if ((data->fan_status[i] & FSCHMD_FAN_ALARM_MASK) &&
+                                       data->fan_act[i])
+                               i2c_smbus_write_byte_data(client,
+                                       FSCHMD_REG_FAN_STATE[data->kind][i],
+                                       FSCHMD_FAN_ALARM_MASK);
+               }
+
+               for (i = 0; i < 3; i++)
+                       data->volt[i] = i2c_smbus_read_byte_data(client,
+                                               FSCHMD_REG_VOLT[i]);
+
+               data->global_control = i2c_smbus_read_byte_data(client,
+                                               FSCHMD_REG_CONTROL);
+
+               /* To be implemented in the future
+               data->watchdog[0] = i2c_smbus_read_byte_data(client,
+                                               FSCHMD_REG_WDOG_PRESET);
+               data->watchdog[1] = i2c_smbus_read_byte_data(client,
+                                               FSCHMD_REG_WDOG_STATE);
+               data->watchdog[2] = i2c_smbus_read_byte_data(client,
+                                               FSCHMD_REG_WDOG_CONTROL); */
+
+               data->last_updated = jiffies;
+               data->valid = 1;
+       }
+
+       mutex_unlock(&data->update_lock);
+
+       return data;
+}
+
+static int __init fschmd_init(void)
+{
+       return i2c_add_driver(&fschmd_driver);
+}
+
+static void __exit fschmd_exit(void)
+{
+       i2c_del_driver(&fschmd_driver);
+}
+
+MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>");
+MODULE_DESCRIPTION("FSC Poseidon, Hermes, Scylla, Heracles and "
+                       "Heimdall driver");
+MODULE_LICENSE("GPL");
+
+module_init(fschmd_init);
+module_exit(fschmd_exit);
index ea506a7..92c9703 100644 (file)
@@ -115,7 +115,7 @@ static struct i2c_driver fscpos_driver = {
  */
 struct fscpos_data {
        struct i2c_client client;
-       struct class_device *class_dev;
+       struct device *hwmon_dev;
        struct mutex update_lock;
        char valid;             /* 0 until following fields are valid */
        unsigned long last_updated;     /* In jiffies */
@@ -539,9 +539,9 @@ static int fscpos_detect(struct i2c_adapter *adapter, int address, int kind)
        if ((err = sysfs_create_group(&new_client->dev.kobj, &fscpos_group)))
                goto exit_detach;
 
-       data->class_dev = hwmon_device_register(&new_client->dev);
-       if (IS_ERR(data->class_dev)) {
-               err = PTR_ERR(data->class_dev);
+       data->hwmon_dev = hwmon_device_register(&new_client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
                goto exit_remove_files;
        }
 
@@ -562,7 +562,7 @@ static int fscpos_detach_client(struct i2c_client *client)
        struct fscpos_data *data = i2c_get_clientdata(client);
        int err;
 
-       hwmon_device_unregister(data->class_dev);
+       hwmon_device_unregister(data->hwmon_dev);
        sysfs_remove_group(&client->dev.kobj, &fscpos_group);
 
        if ((err = i2c_detach_client(client)))
index c103640..bb58d98 100644 (file)
@@ -119,7 +119,7 @@ static inline u8 FAN_TO_REG(long rpm, int div)
 /* Each client has this additional data */
 struct gl518_data {
        struct i2c_client client;
-       struct class_device *class_dev;
+       struct device *hwmon_dev;
        enum chips type;
 
        struct mutex update_lock;
@@ -460,9 +460,9 @@ static int gl518_detect(struct i2c_adapter *adapter, int address, int kind)
        if ((err = sysfs_create_group(&new_client->dev.kobj, &gl518_group)))
                goto exit_detach;
 
-       data->class_dev = hwmon_device_register(&new_client->dev);
-       if (IS_ERR(data->class_dev)) {
-               err = PTR_ERR(data->class_dev);
+       data->hwmon_dev = hwmon_device_register(&new_client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
                goto exit_remove_files;
        }
 
@@ -502,7 +502,7 @@ static int gl518_detach_client(struct i2c_client *client)
        struct gl518_data *data = i2c_get_clientdata(client);
        int err;
 
-       hwmon_device_unregister(data->class_dev);
+       hwmon_device_unregister(data->hwmon_dev);
        sysfs_remove_group(&client->dev.kobj, &gl518_group);
 
        if ((err = i2c_detach_client(client)))
index ebe7b9a..a3b56c8 100644 (file)
@@ -122,7 +122,7 @@ static struct i2c_driver gl520_driver = {
 /* Client data */
 struct gl520_data {
        struct i2c_client client;
-       struct class_device *class_dev;
+       struct device *hwmon_dev;
        struct mutex update_lock;
        char valid;             /* zero until the following fields are valid */
        unsigned long last_updated;     /* in jiffies */
@@ -622,9 +622,9 @@ static int gl520_detect(struct i2c_adapter *adapter, int address, int kind)
        }
 
 
-       data->class_dev = hwmon_device_register(&new_client->dev);
-       if (IS_ERR(data->class_dev)) {
-               err = PTR_ERR(data->class_dev);
+       data->hwmon_dev = hwmon_device_register(&new_client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
                goto exit_remove_files;
        }
 
@@ -685,7 +685,7 @@ static int gl520_detach_client(struct i2c_client *client)
        struct gl520_data *data = i2c_get_clientdata(client);
        int err;
 
-       hwmon_device_unregister(data->class_dev);
+       hwmon_device_unregister(data->hwmon_dev);
        sysfs_remove_group(&client->dev.kobj, &gl520_group);
        sysfs_remove_group(&client->dev.kobj, &gl520_group_opt);
 
index affcc00..3db2845 100644 (file)
@@ -28,17 +28,17 @@ static DEFINE_IDR(hwmon_idr);
 static DEFINE_SPINLOCK(idr_lock);
 
 /**
- * hwmon_device_register - register w/ hwmon sysfs class
+ * hwmon_device_register - register w/ hwmon
  * @dev: the device to register
  *
- * hwmon_device_unregister() must be called when the class device is no
+ * hwmon_device_unregister() must be called when the device is no
  * longer needed.
  *
- * Returns the pointer to the new struct class device.
+ * Returns the pointer to the new device.
  */
-struct class_device *hwmon_device_register(struct device *dev)
+struct device *hwmon_device_register(struct device *dev)
 {
-       struct class_device *cdev;
+       struct device *hwdev;
        int id, err;
 
 again:
@@ -55,34 +55,33 @@ again:
                return ERR_PTR(err);
 
        id = id & MAX_ID_MASK;
-       cdev = class_device_create(hwmon_class, NULL, MKDEV(0,0), dev,
-                                       HWMON_ID_FORMAT, id);
+       hwdev = device_create(hwmon_class, dev, MKDEV(0,0), HWMON_ID_FORMAT, id);
 
-       if (IS_ERR(cdev)) {
+       if (IS_ERR(hwdev)) {
                spin_lock(&idr_lock);
                idr_remove(&hwmon_idr, id);
                spin_unlock(&idr_lock);
        }
 
-       return cdev;
+       return hwdev;
 }
 
 /**
  * hwmon_device_unregister - removes the previously registered class device
  *
- * @cdev: the class device to destroy
+ * @dev: the class device to destroy
  */
-void hwmon_device_unregister(struct class_device *cdev)
+void hwmon_device_unregister(struct device *dev)
 {
        int id;
 
-       if (likely(sscanf(cdev->class_id, HWMON_ID_FORMAT, &id) == 1)) {
-               class_device_unregister(cdev);
+       if (likely(sscanf(dev->bus_id, HWMON_ID_FORMAT, &id) == 1)) {
+               device_unregister(dev);
                spin_lock(&idr_lock);
                idr_remove(&hwmon_idr, id);
                spin_unlock(&idr_lock);
        } else
-               dev_dbg(cdev->dev,
+               dev_dbg(dev->parent,
                        "hwmon_device_unregister() failed: bad class ID!\n");
 }
 
diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c
new file mode 100644 (file)
index 0000000..c462824
--- /dev/null
@@ -0,0 +1,607 @@
+/*
+ * A hwmon driver for the IBM PowerExecutive temperature/power sensors
+ * Copyright (C) 2007 IBM
+ *
+ * Author: Darrick J. Wong <djwong@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/ipmi.h>
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+
+#define REFRESH_INTERVAL       (2 * HZ)
+#define DRVNAME                        "ibmpex"
+
+#define PEX_GET_VERSION                1
+#define PEX_GET_SENSOR_COUNT   2
+#define PEX_GET_SENSOR_NAME    3
+#define PEX_RESET_HIGH_LOW     4
+#define PEX_GET_SENSOR_DATA    6
+
+#define PEX_NET_FUNCTION       0x3A
+#define PEX_COMMAND            0x3C
+
+static inline u16 extract_value(const char *data, int offset)
+{
+       return be16_to_cpup((u16 *)&data[offset]);
+}
+
+#define TEMP_SENSOR            1
+#define POWER_SENSOR           2
+
+#define PEX_SENSOR_TYPE_LEN    3
+static u8 const power_sensor_sig[] = {0x70, 0x77, 0x72};
+static u8 const temp_sensor_sig[]  = {0x74, 0x65, 0x6D};
+
+#define PEX_MULT_LEN           2
+static u8 const watt_sensor_sig[]  = {0x41, 0x43};
+
+#define PEX_NUM_SENSOR_FUNCS   3
+static char const * const power_sensor_name_templates[] = {
+       "%s%d_average",
+       "%s%d_average_lowest",
+       "%s%d_average_highest"
+};
+static char const * const temp_sensor_name_templates[] = {
+       "%s%d_input",
+       "%s%d_input_lowest",
+       "%s%d_input_highest"
+};
+
+static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
+static void ibmpex_register_bmc(int iface, struct device *dev);
+static void ibmpex_bmc_gone(int iface);
+
+struct ibmpex_sensor_data {
+       int                     in_use;
+       s16                     values[PEX_NUM_SENSOR_FUNCS];
+       int                     multiplier;
+
+       struct sensor_device_attribute_2        attr[PEX_NUM_SENSOR_FUNCS];
+};
+
+struct ibmpex_bmc_data {
+       struct list_head        list;
+       struct device           *hwmon_dev;
+       struct device           *bmc_device;
+       struct mutex            lock;
+       char                    valid;
+       unsigned long           last_updated;   /* In jiffies */
+
+       struct ipmi_addr        address;
+       struct completion       read_complete;
+       ipmi_user_t             user;
+       int                     interface;
+
+       struct kernel_ipmi_msg  tx_message;
+       unsigned char           tx_msg_data[IPMI_MAX_MSG_LENGTH];
+       long                    tx_msgid;
+
+       unsigned char           rx_msg_data[IPMI_MAX_MSG_LENGTH];
+       unsigned long           rx_msg_len;
+       unsigned char           rx_result;
+       int                     rx_recv_type;
+
+       unsigned char           sensor_major;
+       unsigned char           sensor_minor;
+
+       unsigned char           num_sensors;
+       struct ibmpex_sensor_data       *sensors;
+};
+
+struct ibmpex_driver_data {
+       struct list_head        bmc_data;
+       struct ipmi_smi_watcher bmc_events;
+       struct ipmi_user_hndl   ipmi_hndlrs;
+};
+
+static struct ibmpex_driver_data driver_data = {
+       .bmc_data = LIST_HEAD_INIT(driver_data.bmc_data),
+       .bmc_events = {
+               .owner = THIS_MODULE,
+               .new_smi = ibmpex_register_bmc,
+               .smi_gone = ibmpex_bmc_gone,
+       },
+       .ipmi_hndlrs = {
+               .ipmi_recv_hndl = ibmpex_msg_handler,
+       },
+};
+
+static int ibmpex_send_message(struct ibmpex_bmc_data *data)
+{
+       int err;
+
+       err = ipmi_validate_addr(&data->address, sizeof(data->address));
+       if (err)
+               goto out;
+
+       data->tx_msgid++;
+       err = ipmi_request_settime(data->user, &data->address, data->tx_msgid,
+                                  &data->tx_message, data, 0, 0, 0);
+       if (err)
+               goto out1;
+
+       return 0;
+out1:
+       printk(KERN_ERR "%s: request_settime=%x\n", __FUNCTION__, err);
+       return err;
+out:
+       printk(KERN_ERR "%s: validate_addr=%x\n", __FUNCTION__, err);
+       return err;
+}
+
+static int ibmpex_ver_check(struct ibmpex_bmc_data *data)
+{
+       data->tx_msg_data[0] = PEX_GET_VERSION;
+       data->tx_message.data_len = 1;
+       ibmpex_send_message(data);
+
+       wait_for_completion(&data->read_complete);
+
+       if (data->rx_result || data->rx_msg_len != 6)
+               return -ENOENT;
+
+       data->sensor_major = data->rx_msg_data[0];
+       data->sensor_minor = data->rx_msg_data[1];
+
+       printk(KERN_INFO DRVNAME ": 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),
+              data->rx_msg_data[4],
+              data->rx_msg_data[5],
+              data->interface);
+
+       return 0;
+}
+
+static int ibmpex_query_sensor_count(struct ibmpex_bmc_data *data)
+{
+       data->tx_msg_data[0] = PEX_GET_SENSOR_COUNT;
+       data->tx_message.data_len = 1;
+       ibmpex_send_message(data);
+
+       wait_for_completion(&data->read_complete);
+
+       if (data->rx_result || data->rx_msg_len != 1)
+               return -ENOENT;
+
+       return data->rx_msg_data[0];
+}
+
+static int ibmpex_query_sensor_name(struct ibmpex_bmc_data *data, int sensor)
+{
+       data->tx_msg_data[0] = PEX_GET_SENSOR_NAME;
+       data->tx_msg_data[1] = sensor;
+       data->tx_message.data_len = 2;
+       ibmpex_send_message(data);
+
+       wait_for_completion(&data->read_complete);
+
+       if (data->rx_result || data->rx_msg_len < 1)
+               return -ENOENT;
+
+       return 0;
+}
+
+static int ibmpex_query_sensor_data(struct ibmpex_bmc_data *data, int sensor)
+{
+       data->tx_msg_data[0] = PEX_GET_SENSOR_DATA;
+       data->tx_msg_data[1] = sensor;
+       data->tx_message.data_len = 2;
+       ibmpex_send_message(data);
+
+       wait_for_completion(&data->read_complete);
+
+       if (data->rx_result || data->rx_msg_len < 26) {
+               printk(KERN_ERR "Error reading sensor %d, please check.\n",
+                      sensor);
+               return -ENOENT;
+       }
+
+       return 0;
+}
+
+static int ibmpex_reset_high_low_data(struct ibmpex_bmc_data *data)
+{
+       data->tx_msg_data[0] = PEX_RESET_HIGH_LOW;
+       data->tx_message.data_len = 1;
+       ibmpex_send_message(data);
+
+       wait_for_completion(&data->read_complete);
+
+       return 0;
+}
+
+static void ibmpex_update_device(struct ibmpex_bmc_data *data)
+{
+       int i, err;
+
+       mutex_lock(&data->lock);
+       if (time_before(jiffies, data->last_updated + REFRESH_INTERVAL) &&
+           data->valid)
+               goto out;
+
+       for (i = 0; i < data->num_sensors; i++) {
+               if (!data->sensors[i].in_use)
+                       continue;
+               err = ibmpex_query_sensor_data(data, i);
+               if (err)
+                       continue;
+               data->sensors[i].values[0] =
+                       extract_value(data->rx_msg_data, 16);
+               data->sensors[i].values[1] =
+                       extract_value(data->rx_msg_data, 18);
+               data->sensors[i].values[2] =
+                       extract_value(data->rx_msg_data, 20);
+       }
+
+       data->last_updated = jiffies;
+       data->valid = 1;
+
+out:
+       mutex_unlock(&data->lock);
+}
+
+static struct ibmpex_bmc_data *get_bmc_data(int iface)
+{
+       struct ibmpex_bmc_data *p, *next;
+
+       list_for_each_entry_safe(p, next, &driver_data.bmc_data, list)
+               if (p->interface == iface)
+                       return p;
+
+       return NULL;
+}
+
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+                        char *buf)
+{
+       return sprintf(buf, "%s\n", DRVNAME);
+}
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+
+static ssize_t ibmpex_show_sensor(struct device *dev,
+                                 struct device_attribute *devattr,
+                                 char *buf)
+{
+       struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+       struct ibmpex_bmc_data *data = dev_get_drvdata(dev);
+       int mult = data->sensors[attr->index].multiplier;
+       ibmpex_update_device(data);
+
+       return sprintf(buf, "%d\n",
+                      data->sensors[attr->index].values[attr->nr] * mult);
+}
+
+static ssize_t ibmpex_reset_high_low(struct device *dev,
+                                    struct device_attribute *devattr,
+                                    const char *buf,
+                                    size_t count)
+{
+       struct ibmpex_bmc_data *data = dev_get_drvdata(dev);
+
+       ibmpex_reset_high_low_data(data);
+
+       return count;
+}
+
+static SENSOR_DEVICE_ATTR(reset_high_low, S_IWUSR, NULL,
+                         ibmpex_reset_high_low, 0);
+
+static int is_power_sensor(const char *sensor_id, int len)
+{
+       if (len < PEX_SENSOR_TYPE_LEN)