Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groec...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 30 Jul 2011 18:57:57 +0000 (08:57 -1000)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 30 Jul 2011 18:57:57 +0000 (08:57 -1000)
* 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/staging: (24 commits)
  hwmon: (lm90) Refactor reading of config2 register
  hwmon: (lm90) Make SA56004 detection more robust
  hwmon: (lm90) Simplify handling of extended local temp register
  hwmon: (pmbus) Add client driver for LM25066, LM5064, and LM5066
  hwmon: (max34440) Add support for peak attributes
  hwmon: (max8688) Add support for peak attributes
  hwmon: (max16064) Add support for peak attributes
  hwmon: (adm1275) Add support for peak attributes
  hwmon: (pmbus) Add support for peak attributes
  hwmon: Add new attributes to sysfs ABI
  hwmon: (pmbus) Strengthen check for status register existence
  hwmon: (pmbus) Add support for virtual pages
  hwmon: (pmbus) Support reading and writing of word registers in device specific code
  hwmon: (pmbus) Increase attribute name size
  hwmon: (pmbus) Add ADP4000, NCP4200 and NCP4208 to list of supported devices
  hwmon: (pmbus) Add support for VID output voltage mode
  hwmon: (pmbus) Move PMBus drivers to drivers/hwmon/pmbus
  hwmon: (coretemp) Add core/pkg threshold support to Coretemp
  hwmon: (lm95241) Add support for LM95231
  hwmon: LM95245 driver
  ...

34 files changed:
Documentation/hwmon/adm1275
Documentation/hwmon/coretemp
Documentation/hwmon/lm25066 [new file with mode: 0644]
Documentation/hwmon/lm90
Documentation/hwmon/lm95245 [new file with mode: 0644]
Documentation/hwmon/max16064
Documentation/hwmon/max1668 [new file with mode: 0644]
Documentation/hwmon/max34440
Documentation/hwmon/max8688
Documentation/hwmon/ntc_thermistor [new file with mode: 0644]
Documentation/hwmon/pmbus
Documentation/hwmon/sysfs-interface
MAINTAINERS
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/coretemp.c
drivers/hwmon/lm90.c
drivers/hwmon/lm95241.c
drivers/hwmon/lm95245.c [new file with mode: 0644]
drivers/hwmon/max1668.c [new file with mode: 0644]
drivers/hwmon/ntc_thermistor.c [new file with mode: 0644]
drivers/hwmon/pmbus/Kconfig [new file with mode: 0644]
drivers/hwmon/pmbus/Makefile [new file with mode: 0644]
drivers/hwmon/pmbus/adm1275.c [moved from drivers/hwmon/adm1275.c with 67% similarity]
drivers/hwmon/pmbus/lm25066.c [new file with mode: 0644]
drivers/hwmon/pmbus/max16064.c [moved from drivers/hwmon/max16064.c with 66% similarity]
drivers/hwmon/pmbus/max34440.c [moved from drivers/hwmon/max34440.c with 76% similarity]
drivers/hwmon/pmbus/max8688.c [moved from drivers/hwmon/max8688.c with 72% similarity]
drivers/hwmon/pmbus/pmbus.c [moved from drivers/hwmon/pmbus.c with 89% similarity]
drivers/hwmon/pmbus/pmbus.h [moved from drivers/hwmon/pmbus.h with 79% similarity]
drivers/hwmon/pmbus/pmbus_core.c [moved from drivers/hwmon/pmbus_core.c with 84% similarity]
drivers/hwmon/pmbus/ucd9000.c [moved from drivers/hwmon/ucd9000.c with 100% similarity]
drivers/hwmon/pmbus/ucd9200.c [moved from drivers/hwmon/ucd9200.c with 100% similarity]
include/linux/platform_data/ntc_thermistor.h [new file with mode: 0644]

index 6a3a647..097b3cc 100644 (file)
@@ -43,8 +43,8 @@ Documentation/hwmon/pmbus for details.
 Sysfs entries
 -------------
 
-The following attributes are supported. Limits are read-write; all other
-attributes are read-only.
+The following attributes are supported. Limits are read-write, history reset
+attributes are write-only, all other attributes are read-only.
 
 in1_label              "vin1" or "vout1" depending on chip variant and
                        configuration.
@@ -53,8 +53,12 @@ in1_min                      Minumum Voltage. From VOUT_UV_WARN_LIMIT register.
 in1_max                        Maximum voltage. From VOUT_OV_WARN_LIMIT register.
 in1_min_alarm          Voltage low alarm. From VOLTAGE_UV_WARNING status.
 in1_max_alarm          Voltage high alarm. From VOLTAGE_OV_WARNING status.
+in1_highest            Historical maximum voltage.
+in1_reset_history      Write any value to reset history.
 
 curr1_label            "iout1"
 curr1_input            Measured current. From READ_IOUT register.
 curr1_max              Maximum current. From IOUT_OC_WARN_LIMIT register.
 curr1_max_alarm                Current high alarm. From IOUT_OC_WARN_LIMIT register.
+curr1_highest          Historical maximum current.
+curr1_reset_history    Write any value to reset history.
index f85e913..fa8776a 100644 (file)
@@ -35,6 +35,13 @@ the Out-Of-Spec bit. Following table summarizes the exported sysfs files:
 All Sysfs entries are named with their core_id (represented here by 'X').
 tempX_input     - Core temperature (in millidegrees Celsius).
 tempX_max       - All cooling devices should be turned on (on Core2).
+                  Initialized with IA32_THERM_INTERRUPT. When the CPU
+                  temperature reaches this temperature, an interrupt is
+                  generated and tempX_max_alarm is set.
+tempX_max_hyst   - If the CPU temperature falls below than temperature,
+                  an interrupt is generated and tempX_max_alarm is reset.
+tempX_max_alarm  - Set if the temperature reaches or exceeds tempX_max.
+                  Reset if the temperature drops to or below tempX_max_hyst.
 tempX_crit      - Maximum junction temperature (in millidegrees Celsius).
 tempX_crit_alarm - Set when Out-of-spec bit is set, never clears.
                   Correct CPU operation is no longer guaranteed.
diff --git a/Documentation/hwmon/lm25066 b/Documentation/hwmon/lm25066
new file mode 100644 (file)
index 0000000..a21db81
--- /dev/null
@@ -0,0 +1,90 @@
+Kernel driver max8688
+=====================
+
+Supported chips:
+  * National Semiconductor LM25066
+    Prefix: 'lm25066'
+    Addresses scanned: -
+    Datasheets:
+       http://www.national.com/pf/LM/LM25066.html
+       http://www.national.com/pf/LM/LM25066A.html
+  * National Semiconductor LM5064
+    Prefix: 'lm5064'
+    Addresses scanned: -
+    Datasheet:
+       http://www.national.com/pf/LM/LM5064.html
+  * National Semiconductor LM5066
+    Prefix: 'lm5066'
+    Addresses scanned: -
+    Datasheet:
+       http://www.national.com/pf/LM/LM5066.html
+
+Author: Guenter Roeck <guenter.roeck@ericsson.com>
+
+
+Description
+-----------
+
+This driver supports hardware montoring for National Semiconductor LM25066,
+LM5064, and LM5064 Power Management, Monitoring, Control, and Protection ICs.
+
+The driver is a client driver to the core PMBus driver. Please see
+Documentation/hwmon/pmbus for details on PMBus client drivers.
+
+
+Usage Notes
+-----------
+
+This driver does not auto-detect devices. You will have to instantiate the
+devices explicitly. Please see Documentation/i2c/instantiating-devices for
+details.
+
+
+Platform data support
+---------------------
+
+The driver supports standard PMBus driver platform data.
+
+
+Sysfs entries
+-------------
+
+The following attributes are supported. Limits are read-write; all other
+attributes are read-only.
+
+in1_label              "vin"
+in1_input              Measured input voltage.
+in1_average            Average measured input voltage.
+in1_min                        Minimum input voltage.
+in1_max                        Maximum input voltage.
+in1_min_alarm          Input voltage low alarm.
+in1_max_alarm          Input voltage high alarm.
+
+in2_label              "vout1"
+in2_input              Measured output voltage.
+in2_average            Average measured output voltage.
+in2_min                        Minimum output voltage.
+in2_min_alarm          Output voltage low alarm.
+
+in3_label              "vout2"
+in3_input              Measured voltage on vaux pin
+
+curr1_label            "iin"
+curr1_input            Measured input current.
+curr1_average          Average measured input current.
+curr1_max              Maximum input current.
+curr1_max_alarm                Input current high alarm.
+
+power1_label           "pin"
+power1_input           Measured input power.
+power1_average         Average measured input power.
+power1_max             Maximum input power limit.
+power1_alarm           Input power alarm
+power1_input_highest   Historical maximum power.
+power1_reset_history   Write any value to reset maximum power history.
+
+temp1_input            Measured temperature.
+temp1_max              Maximum temperature.
+temp1_crit             Critical high temperature.
+temp1_max_alarm                Chip temperature high alarm.
+temp1_crit_alarm       Chip temperature critical high alarm.
index f3efd18..9cd14cf 100644 (file)
@@ -113,7 +113,11 @@ Supported chips:
     Prefix: 'w83l771'
     Addresses scanned: I2C 0x4c
     Datasheet: Not publicly available, can be requested from Nuvoton
-
+  * Philips/NXP SA56004X
+    Prefix: 'sa56004'
+    Addresses scanned: I2C 0x48 through 0x4F
+    Datasheet: Publicly available at NXP website
+               http://ics.nxp.com/products/interface/datasheet/sa56004x.pdf
 
 Author: Jean Delvare <khali@linux-fr.org>
 
@@ -193,6 +197,9 @@ W83L771AWG/ASG
   * The AWG and ASG variants only differ in package format.
   * Diode ideality factor configuration (remote sensor) at 0xE3
 
+SA56004X:
+  * Better local resolution
+
 All temperature values are given in degrees Celsius. Resolution
 is 1.0 degree for the local temperature, 0.125 degree for the remote
 temperature, except for the MAX6657, MAX6658 and MAX6659 which have a
diff --git a/Documentation/hwmon/lm95245 b/Documentation/hwmon/lm95245
new file mode 100644 (file)
index 0000000..cbd8aea
--- /dev/null
@@ -0,0 +1,33 @@
+Kernel driver lm95245
+==================
+
+Supported chips:
+  * National Semiconductor LM95245
+    Addresses scanned: I2C 0x18, 0x19, 0x29, 0x4c, 0x4d
+    Datasheet: Publicly available at the National Semiconductor website
+               http://www.national.com/mpf/LM/LM95245.html
+
+
+Author: Alexander Stein <alexander.stein@systec-electronic.com>
+
+Description
+-----------
+
+The LM95245 is an 11-bit digital temperature sensor with a 2-wire System
+Management Bus (SMBus) interface and TruTherm technology that can monitor
+the temperature of a remote diode as well as its own temperature.
+The LM95245 can be used to very accurately monitor the temperature of
+external devices such as microprocessors.
+
+All temperature values are given in millidegrees Celsius. Local temperature
+is given within a range of -127 to +127.875 degrees. Remote temperatures are
+given within a range of -127 to +255 degrees. Resolution depends on
+temperature input and range.
+
+Each sensor has its own critical limit, but the hysteresis is common to all
+two channels.
+
+The lm95245 driver can change its update interval to a fixed set of values.
+It will round up to the next selectable interval. See the datasheet for exact
+values. Reading sensor values more often will do no harm, but will return
+'old' values.
index 4172899..f6e8bcb 100644 (file)
@@ -50,6 +50,8 @@ in[1-4]_min_alarm     Voltage low alarm. From VOLTAGE_UV_WARNING status.
 in[1-4]_max_alarm      Voltage high alarm. From VOLTAGE_OV_WARNING status.
 in[1-4]_lcrit_alarm    Voltage critical low alarm. From VOLTAGE_UV_FAULT status.
 in[1-4]_crit_alarm     Voltage critical high alarm. From VOLTAGE_OV_FAULT status.
+in[1-4]_highest                Historical maximum voltage.
+in[1-4]_reset_history  Write any value to reset history.
 
 temp1_input            Measured temperature. From READ_TEMPERATURE_1 register.
 temp1_max              Maximum temperature. From OT_WARN_LIMIT register.
@@ -60,3 +62,5 @@ temp1_max_alarm               Chip temperature high alarm. Set by comparing
 temp1_crit_alarm       Chip temperature critical high alarm. Set by comparing
                        READ_TEMPERATURE_1 with OT_FAULT_LIMIT if TEMP_OT_FAULT
                        status is set.
+temp1_highest          Historical maximum temperature.
+temp1_reset_history    Write any value to reset history.
diff --git a/Documentation/hwmon/max1668 b/Documentation/hwmon/max1668
new file mode 100644 (file)
index 0000000..0616ed9
--- /dev/null
@@ -0,0 +1,60 @@
+Kernel driver max1668
+=====================
+
+Supported chips:
+  * Maxim MAX1668, MAX1805 and MAX1989
+    Prefix: 'max1668'
+    Addresses scanned: I2C 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e
+    Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX1668-MAX1989.pdf
+
+Author:
+    David George <david.george@ska.ac.za>
+
+Description
+-----------
+
+This driver implements support for the Maxim MAX1668, MAX1805 and MAX1989
+chips.
+
+The three devices are very similar, but the MAX1805 has a reduced feature
+set; only two remote temperature inputs vs the four avaible on the other
+two ICs.
+
+The driver is able to distinguish between the devices and creates sysfs
+entries as follows:
+
+MAX1805, MAX1668 and MAX1989:
+
+temp1_input     ro local (ambient) temperature
+temp1_max       rw local temperature maximum threshold for alarm
+temp1_max_alarm ro local temperature maximum threshold alarm
+temp1_min       rw local temperature minimum threshold for alarm
+temp1_min_alarm ro local temperature minimum threshold alarm
+temp2_input     ro remote temperature 1
+temp2_max       rw remote temperature 1 maximum threshold for alarm
+temp2_max_alarm ro remote temperature 1 maximum threshold alarm
+temp2_min       rw remote temperature 1 minimum threshold for alarm
+temp2_min_alarm ro remote temperature 1 minimum threshold alarm
+temp3_input     ro remote temperature 2
+temp3_max       rw remote temperature 2 maximum threshold for alarm
+temp3_max_alarm ro remote temperature 2 maximum threshold alarm
+temp3_min       rw remote temperature 2 minimum threshold for alarm
+temp3_min_alarm ro remote temperature 2 minimum threshold alarm
+
+MAX1668 and MAX1989 only:
+temp4_input     ro remote temperature 3
+temp4_max       rw remote temperature 3 maximum threshold for alarm
+temp4_max_alarm ro remote temperature 3 maximum threshold alarm
+temp4_min       rw remote temperature 3 minimum threshold for alarm
+temp4_min_alarm ro remote temperature 3 minimum threshold alarm
+temp5_input     ro remote temperature 4
+temp5_max       rw remote temperature 4 maximum threshold for alarm
+temp5_max_alarm ro remote temperature 4 maximum threshold alarm
+temp5_min       rw remote temperature 4 minimum threshold for alarm
+temp5_min_alarm ro remote temperature 4 minimum threshold alarm
+
+Module Parameters
+-----------------
+
+* read_only: int
+  Set to non-zero if you wish to prevent write access to alarm thresholds.
index 6c525dd..8ab5153 100644 (file)
@@ -56,6 +56,8 @@ in[1-6]_min_alarm     Voltage low alarm. From VOLTAGE_UV_WARNING status.
 in[1-6]_max_alarm      Voltage high alarm. From VOLTAGE_OV_WARNING status.
 in[1-6]_lcrit_alarm    Voltage critical low alarm. From VOLTAGE_UV_FAULT status.
 in[1-6]_crit_alarm     Voltage critical high alarm. From VOLTAGE_OV_FAULT status.
+in[1-6]_highest                Historical maximum voltage.
+in[1-6]_reset_history  Write any value to reset history.
 
 curr[1-6]_label                "iout[1-6]".
 curr[1-6]_input                Measured current. From READ_IOUT register.
@@ -63,6 +65,8 @@ curr[1-6]_max         Maximum current. From IOUT_OC_WARN_LIMIT register.
 curr[1-6]_crit         Critical maximum current. From IOUT_OC_FAULT_LIMIT register.
 curr[1-6]_max_alarm    Current high alarm. From IOUT_OC_WARNING status.
 curr[1-6]_crit_alarm   Current critical high alarm. From IOUT_OC_FAULT status.
+curr[1-6]_highest      Historical maximum current.
+curr[1-6]_reset_history        Write any value to reset history.
 
                        in6 and curr6 attributes only exist for MAX34440.
 
@@ -75,5 +79,7 @@ temp[1-8]_max         Maximum temperature. From OT_WARN_LIMIT register.
 temp[1-8]_crit         Critical high temperature. From OT_FAULT_LIMIT register.
 temp[1-8]_max_alarm    Temperature high alarm.
 temp[1-8]_crit_alarm   Temperature critical high alarm.
+temp[1-8]_highest      Historical maximum temperature.
+temp[1-8]_reset_history        Write any value to reset history.
 
                        temp7 and temp8 attributes only exist for MAX34440.
index 0ddd3a4..71ed10a 100644 (file)
@@ -50,6 +50,8 @@ in1_min_alarm         Voltage low alarm. From VOLTAGE_UV_WARNING status.
 in1_max_alarm          Voltage high alarm. From VOLTAGE_OV_WARNING status.
 in1_lcrit_alarm                Voltage critical low alarm. From VOLTAGE_UV_FAULT status.
 in1_crit_alarm         Voltage critical high alarm. From VOLTAGE_OV_FAULT status.
+in1_highest            Historical maximum voltage.
+in1_reset_history      Write any value to reset history.
 
 curr1_label            "iout1"
 curr1_input            Measured current. From READ_IOUT register.
@@ -57,6 +59,8 @@ curr1_max             Maximum current. From IOUT_OC_WARN_LIMIT register.
 curr1_crit             Critical maximum current. From IOUT_OC_FAULT_LIMIT register.
 curr1_max_alarm                Current high alarm. From IOUT_OC_WARN_LIMIT register.
 curr1_crit_alarm       Current critical high alarm. From IOUT_OC_FAULT status.
+curr1_highest          Historical maximum current.
+curr1_reset_history    Write any value to reset history.
 
 temp1_input            Measured temperature. From READ_TEMPERATURE_1 register.
 temp1_max              Maximum temperature. From OT_WARN_LIMIT register.
@@ -67,3 +71,5 @@ temp1_max_alarm               Chip temperature high alarm. Set by comparing
 temp1_crit_alarm       Chip temperature critical high alarm. Set by comparing
                        READ_TEMPERATURE_1 with OT_FAULT_LIMIT if TEMP_OT_FAULT
                        status is set.
+temp1_highest          Historical maximum temperature.
+temp1_reset_history    Write any value to reset history.
diff --git a/Documentation/hwmon/ntc_thermistor b/Documentation/hwmon/ntc_thermistor
new file mode 100644 (file)
index 0000000..3bfda94
--- /dev/null
@@ -0,0 +1,93 @@
+Kernel driver ntc_thermistor
+=================
+
+Supported thermistors:
+* Murata NTC Thermistors NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, NCP15WL333
+  Prefixes: 'ncp15wb473', 'ncp18wb473', 'ncp21wb473', 'ncp03wb473', 'ncp15wl333'
+  Datasheet: Publicly available at Murata
+
+Other NTC thermistors can be supported simply by adding compensation
+tables; e.g., NCP15WL333 support is added by the table ncpXXwl333.
+
+Authors:
+       MyungJoo Ham <myungjoo.ham@samsung.com>
+
+Description
+-----------
+
+The NTC thermistor is a simple thermistor that requires users to provide the
+resistance and lookup the corresponding compensation table to get the
+temperature input.
+
+The NTC driver provides lookup tables with a linear approximation function
+and four circuit models with an option not to use any of the four models.
+
+The four circuit models provided are:
+
+       $: resister, [TH]: the thermistor
+
+ 1. connect = NTC_CONNECTED_POSITIVE, pullup_ohm > 0
+
+   [pullup_uV]
+       |    |
+      [TH]  $ (pullup_ohm)
+       |    |
+       +----+-----------------------[read_uV]
+       |
+       $ (pulldown_ohm)
+       |
+      --- (ground)
+
+ 2. connect = NTC_CONNECTED_POSITIVE, pullup_ohm = 0 (not-connected)
+
+   [pullup_uV]
+       |
+      [TH]
+       |
+       +----------------------------[read_uV]
+       |
+       $ (pulldown_ohm)
+       |
+      --- (ground)
+
+ 3. connect = NTC_CONNECTED_GROUND, pulldown_ohm > 0
+
+   [pullup_uV]
+       |
+       $ (pullup_ohm)
+       |
+       +----+-----------------------[read_uV]
+       |    |
+      [TH]  $ (pulldown_ohm)
+       |    |
+      -------- (ground)
+
+ 4. connect = NTC_CONNECTED_GROUND, pulldown_ohm = 0 (not-connected)
+
+   [pullup_uV]
+       |
+       $ (pullup_ohm)
+       |
+       +----------------------------[read_uV]
+       |
+      [TH]
+       |
+      --- (ground)
+
+When one of the four circuit models is used, read_uV, pullup_uV, pullup_ohm,
+pulldown_ohm, and connect should be provided. When none of the four models
+are suitable or the user can get the resistance directly, the user should
+provide read_ohm and _not_ provide the others.
+
+Sysfs Interface
+---------------
+name           the mandatory global attribute, the thermistor name.
+
+temp1_type     always 4 (thermistor)
+               RO
+
+temp1_input    measure the temperature and provide the measured value.
+               (reading this file initiates the reading procedure.)
+               RO
+
+Note that each NTC thermistor has only _one_ thermistor; thus, only temp1 exists.
index 5e462fc..c36c1c1 100644 (file)
@@ -13,6 +13,13 @@ Supported chips:
     Prefix: 'ltc2978'
     Addresses scanned: -
     Datasheet: http://cds.linear.com/docs/Datasheet/2978fa.pdf
+  * ON Semiconductor ADP4000, NCP4200, NCP4208
+    Prefixes: 'adp4000', 'ncp4200', 'ncp4208'
+    Addresses scanned: -
+    Datasheets:
+       http://www.onsemi.com/pub_link/Collateral/ADP4000-D.PDF
+       http://www.onsemi.com/pub_link/Collateral/NCP4200-D.PDF
+       http://www.onsemi.com/pub_link/Collateral/JUNE%202009-%20REV.%200.PDF
   * Generic PMBus devices
     Prefix: 'pmbus'
     Addresses scanned: -
index 8f63c24..a4aa8f6 100644 (file)
@@ -139,6 +139,29 @@ in[0-*]_input      Voltage input value.
                thumb: drivers should report the voltage values at the
                "pins" of the chip.
 
+in[0-*]_average
+               Average voltage
+               Unit: millivolt
+               RO
+
+in[0-*]_lowest
+               Historical minimum voltage
+               Unit: millivolt
+               RO
+
+in[0-*]_highest
+               Historical maximum voltage
+               Unit: millivolt
+               RO
+
+in[0-*]_reset_history
+               Reset inX_lowest and inX_highest
+               WO
+
+in_reset_history
+               Reset inX_lowest and inX_highest for all sensors
+               WO
+
 in[0-*]_label  Suggested voltage channel label.
                Text string
                Should only be created if the driver has hints about what
@@ -407,6 +430,29 @@ curr[1-*]_input    Current input value
                Unit: milliampere
                RO
 
+curr[1-*]_average
+               Average current use
+               Unit: milliampere
+               RO
+
+curr[1-*]_lowest
+               Historical minimum current
+               Unit: milliampere
+               RO
+
+curr[1-*]_highest
+               Historical maximum current
+               Unit: milliampere
+               RO
+
+curr[1-*]_reset_history
+               Reset currX_lowest and currX_highest
+               WO
+
+curr_reset_history
+               Reset currX_lowest and currX_highest for all sensors
+               WO
+
 Also see the Alarms section for status flags associated with currents.
 
 *********
index 1d2e79d..cf5e8a7 100644 (file)
@@ -4150,6 +4150,13 @@ S:       Orphan
 F:     drivers/video/matrox/matroxfb_*
 F:     include/linux/matroxfb.h
 
+MAX1668 TEMPERATURE SENSOR DRIVER
+M:     "David George" <david.george@ska.ac.za>
+L:     lm-sensors@lm-sensors.org
+S:     Maintained
+F:     Documentation/hwmon/max1668
+F:     drivers/hwmon/max1668.c
+
 MAX6650 HARDWARE MONITOR AND FAN CONTROLLER DRIVER
 M:     "Hans J. Koch" <hjk@hansjkoch.de>
 L:     lm-sensors@lm-sensors.org
@@ -4999,6 +5006,17 @@ F:       drivers/i2c/busses/i2c-puv3.c
 F:     drivers/video/fb-puv3.c
 F:     drivers/rtc/rtc-puv3.c
 
+PMBUS HARDWARE MONITORING DRIVERS
+M:     Guenter Roeck <guenter.roeck@ericsson.com>
+L:     lm-sensors@lm-sensors.org
+W:     http://www.lm-sensors.org/
+W:     http://www.roeck-us.net/linux/drivers/
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git
+S:     Maintained
+F:     Documentation/hwmon/pmbus
+F:     drivers/hwmon/pmbus/
+F:     include/linux/i2c/pmbus.h
+
 PMC SIERRA MaxRAID DRIVER
 M:     Anil Ravindranath <anil_ravindranath@pmc-sierra.com>
 L:     linux-scsi@vger.kernel.org
index 0598cd2..0b62c3c 100644 (file)
@@ -623,7 +623,7 @@ config SENSORS_LM90
          LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A,
          Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659,
          MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008,
-         and Winbond/Nuvoton W83L771W/G/AWG/ASG sensor chips.
+         Winbond/Nuvoton W83L771W/G/AWG/ASG and Philips SA56004 sensor chips.
 
          This driver can also be built as a module.  If so, the module
          will be called lm90.
@@ -694,14 +694,24 @@ config SENSORS_LTC4261
          be called ltc4261.
 
 config SENSORS_LM95241
-       tristate "National Semiconductor LM95241 sensor chip"
+       tristate "National Semiconductor LM95241 and compatibles"
        depends on I2C
        help
-         If you say yes here you get support for LM95241 sensor chip.
+         If you say yes here you get support for LM95231 and LM95241 sensor
+         chips.
 
          This driver can also be built as a module.  If so, the module
          will be called lm95241.
 
+config SENSORS_LM95245
+       tristate "National Semiconductor LM95245 sensor chip"
+       depends on I2C && EXPERIMENTAL
+       help
+         If you say yes here you get support for LM95245 sensor chip.
+
+         This driver can also be built as a module.  If so, the module
+         will be called lm95245.
+
 config SENSORS_MAX1111
        tristate "Maxim MAX1111 Multichannel, Serial 8-bit ADC chip"
        depends on SPI_MASTER
@@ -736,6 +746,16 @@ config SENSORS_MAX1619
          This driver can also be built as a module.  If so, the module
          will be called max1619.
 
+config SENSORS_MAX1668
+       tristate "Maxim MAX1668 and compatibles"
+       depends on I2C && EXPERIMENTAL
+       help
+         If you say yes here you get support for MAX1668, MAX1989 and
+         MAX1805 chips.
+
+         This driver can also be built as a module.  If so, the module
+         will be called max1668.
+
 config SENSORS_MAX6639
        tristate "Maxim MAX6639 sensor chip"
        depends on I2C && EXPERIMENTAL
@@ -767,6 +787,20 @@ config SENSORS_MAX6650
          This driver can also be built as a module.  If so, the module
          will be called max6650.
 
+config SENSORS_NTC_THERMISTOR
+       tristate "NTC thermistor support"
+       depends on EXPERIMENTAL
+       help
+         This driver supports NTC thermistors sensor reading and its
+         interpretation. The driver can also monitor the temperature and
+         send notifications about the temperature.
+
+         Currently, this driver supports
+         NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, and NCP15WL333.
+
+         This driver can also be built as a module.  If so, the module
+         will be called ntc-thermistor.
+
 config SENSORS_PC87360
        tristate "National Semiconductor PC87360 family"
        select HWMON_VID
@@ -807,92 +841,7 @@ config SENSORS_PCF8591
          These devices are hard to detect and rarely found on mainstream
          hardware.  If unsure, say N.
 
-config PMBUS
-       tristate "PMBus support"
-       depends on I2C && EXPERIMENTAL
-       default n
-       help
-         Say yes here if you want to enable PMBus support.
-
-         This driver can also be built as a module. If so, the module will
-         be called pmbus_core.
-
-if PMBUS
-
-config SENSORS_PMBUS
-       tristate "Generic PMBus devices"
-       default n
-       help
-         If you say yes here you get hardware monitoring support for generic
-         PMBus devices, including but not limited to BMR450, BMR451, BMR453,
-         BMR454, and LTC2978.
-
-         This driver can also be built as a module. If so, the module will
-         be called pmbus.
-
-config SENSORS_ADM1275
-       tristate "Analog Devices ADM1275"
-       default n
-       help
-         If you say yes here you get hardware monitoring support for Analog
-         Devices ADM1275 Hot-Swap Controller and Digital Power Monitor.
-
-         This driver can also be built as a module. If so, the module will
-         be called adm1275.
-
-config SENSORS_MAX16064
-       tristate "Maxim MAX16064"
-       default n
-       help
-         If you say yes here you get hardware monitoring support for Maxim
-         MAX16064.
-
-         This driver can also be built as a module. If so, the module will
-         be called max16064.
-
-config SENSORS_MAX34440
-       tristate "Maxim MAX34440/MAX34441"
-       default n
-       help
-         If you say yes here you get hardware monitoring support for Maxim
-         MAX34440 and MAX34441.
-
-         This driver can also be built as a module. If so, the module will
-         be called max34440.
-
-config SENSORS_MAX8688
-       tristate "Maxim MAX8688"
-       default n
-       help
-         If you say yes here you get hardware monitoring support for Maxim
-         MAX8688.
-
-         This driver can also be built as a module. If so, the module will
-         be called max8688.
-
-config SENSORS_UCD9000
-       tristate "TI UCD90120, UCD90124, UCD9090, UCD90910"
-       default n
-       help
-         If you say yes here you get hardware monitoring support for TI
-         UCD90120, UCD90124, UCD9090, UCD90910 Sequencer and System Health
-         Controllers.
-
-         This driver can also be built as a module. If so, the module will
-         be called ucd9000.
-
-config SENSORS_UCD9200
-       tristate "TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, UCD9248"
-       default n
-       help
-         If you say yes here you get hardware monitoring support for TI
-         UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and UCD9248
-         Digital PWM System Controllers.
-
-         This driver can also be built as a module. If so, the module will
-         be called ucd9200.
-
-endif # PMBUS
+source drivers/hwmon/pmbus/Kconfig
 
 config SENSORS_SHT15
        tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
index d7995a1..3c9ccef 100644 (file)
@@ -80,6 +80,7 @@ obj-$(CONFIG_SENSORS_LM90)    += lm90.o
 obj-$(CONFIG_SENSORS_LM92)     += lm92.o
 obj-$(CONFIG_SENSORS_LM93)     += lm93.o
 obj-$(CONFIG_SENSORS_LM95241)  += lm95241.o
+obj-$(CONFIG_SENSORS_LM95245)  += lm95245.o
 obj-$(CONFIG_SENSORS_LTC4151)  += ltc4151.o
 obj-$(CONFIG_SENSORS_LTC4215)  += ltc4215.o
 obj-$(CONFIG_SENSORS_LTC4245)  += ltc4245.o
@@ -87,10 +88,12 @@ obj-$(CONFIG_SENSORS_LTC4261)       += ltc4261.o
 obj-$(CONFIG_SENSORS_MAX1111)  += max1111.o
 obj-$(CONFIG_SENSORS_MAX16065) += max16065.o
 obj-$(CONFIG_SENSORS_MAX1619)  += max1619.o
+obj-$(CONFIG_SENSORS_MAX1668)  += max1668.o
 obj-$(CONFIG_SENSORS_MAX6639)  += max6639.o
 obj-$(CONFIG_SENSORS_MAX6642)  += max6642.o
 obj-$(CONFIG_SENSORS_MAX6650)  += max6650.o
 obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
+obj-$(CONFIG_SENSORS_NTC_THERMISTOR)   += ntc_thermistor.o
 obj-$(CONFIG_SENSORS_PC87360)  += pc87360.o
 obj-$(CONFIG_SENSORS_PC87427)  += pc87427.o
 obj-$(CONFIG_SENSORS_PCF8591)  += pcf8591.o
@@ -121,15 +124,7 @@ obj-$(CONFIG_SENSORS_W83L786NG)    += w83l786ng.o
 obj-$(CONFIG_SENSORS_WM831X)   += wm831x-hwmon.o
 obj-$(CONFIG_SENSORS_WM8350)   += wm8350-hwmon.o
 
-# PMBus drivers
-obj-$(CONFIG_PMBUS)            += pmbus_core.o
-obj-$(CONFIG_SENSORS_PMBUS)    += pmbus.o
-obj-$(CONFIG_SENSORS_ADM1275)  += adm1275.o
-obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
-obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
-obj-$(CONFIG_SENSORS_MAX8688)  += max8688.o
-obj-$(CONFIG_SENSORS_UCD9000)  += ucd9000.o
-obj-$(CONFIG_SENSORS_UCD9200)  += ucd9200.o
+obj-$(CONFIG_PMBUS)            += pmbus/
 
 ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG
 
index 0070d54..59d83e8 100644 (file)
@@ -44,7 +44,9 @@
 #define BASE_SYSFS_ATTR_NO     2       /* Sysfs Base attr no for coretemp */
 #define NUM_REAL_CORES         16      /* Number of Real cores per cpu */
 #define CORETEMP_NAME_LENGTH   17      /* String Length of attrs */
-#define MAX_ATTRS              5       /* Maximum no of per-core attrs */
+#define MAX_CORE_ATTRS         4       /* Maximum no of basic attrs */
+#define MAX_THRESH_ATTRS       3       /* Maximum no of Threshold attrs */
+#define TOTAL_ATTRS            (MAX_CORE_ATTRS + MAX_THRESH_ATTRS)
 #define MAX_CORE_DATA          (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO)
 
 #ifdef CONFIG_SMP
@@ -67,6 +69,9 @@
  *             This value is passed as "id" field to rdmsr/wrmsr functions.
  * @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS,
  *             from where the temperature values should be read.
+ * @intrpt_reg: One of IA32_THERM_INTERRUPT or IA32_PACKAGE_THERM_INTERRUPT,
+ *             from where the thresholds are read.
+ * @attr_size:  Total number of pre-core attrs displayed in the sysfs.
  * @is_pkg_data: If this is 1, the temp_data holds pkgtemp data.
  *             Otherwise, temp_data holds coretemp data.
  * @valid: If this is 1, the current temperature is valid.
 struct temp_data {
        int temp;
        int ttarget;
+       int tmin;
        int tjmax;
        unsigned long last_updated;
        unsigned int cpu;
        u32 cpu_core_id;
        u32 status_reg;
+       u32 intrpt_reg;
+       int attr_size;
        bool is_pkg_data;
        bool valid;
-       struct sensor_device_attribute sd_attrs[MAX_ATTRS];
-       char attr_name[MAX_ATTRS][CORETEMP_NAME_LENGTH];
+       struct sensor_device_attribute sd_attrs[TOTAL_ATTRS];
+       char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH];
        struct mutex update_lock;
 };
 
@@ -135,6 +143,19 @@ static ssize_t show_crit_alarm(struct device *dev,
        return sprintf(buf, "%d\n", (eax >> 5) & 1);
 }
 
+static ssize_t show_max_alarm(struct device *dev,
+                               struct device_attribute *devattr, char *buf)
+{
+       u32 eax, edx;
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct platform_data *pdata = dev_get_drvdata(dev);
+       struct temp_data *tdata = pdata->core_data[attr->index];
+
+       rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx);
+
+       return sprintf(buf, "%d\n", !!(eax & THERM_STATUS_THRESHOLD1));
+}
+
 static ssize_t show_tjmax(struct device *dev,
                        struct device_attribute *devattr, char *buf)
 {
@@ -153,6 +174,83 @@ static ssize_t show_ttarget(struct device *dev,
        return sprintf(buf, "%d\n", pdata->core_data[attr->index]->ttarget);
 }
 
+static ssize_t store_ttarget(struct device *dev,
+                               struct device_attribute *devattr,
+                               const char *buf, size_t count)
+{
+       struct platform_data *pdata = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct temp_data *tdata = pdata->core_data[attr->index];
+       u32 eax, edx;
+       unsigned long val;
+       int diff;
+
+       if (strict_strtoul(buf, 10, &val))
+               return -EINVAL;
+
+       /*
+        * THERM_MASK_THRESHOLD1 is 7 bits wide. Values are entered in terms
+        * of milli degree celsius. Hence don't accept val > (127 * 1000)
+        */
+       if (val > tdata->tjmax || val > 127000)
+               return -EINVAL;
+
+       diff = (tdata->tjmax - val) / 1000;
+
+       mutex_lock(&tdata->update_lock);
+       rdmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, &eax, &edx);
+       eax = (eax & ~THERM_MASK_THRESHOLD1) |
+                               (diff << THERM_SHIFT_THRESHOLD1);
+       wrmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, eax, edx);
+       tdata->ttarget = val;
+       mutex_unlock(&tdata->update_lock);
+
+       return count;
+}
+
+static ssize_t show_tmin(struct device *dev,
+                       struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct platform_data *pdata = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", pdata->core_data[attr->index]->tmin);
+}
+
+static ssize_t store_tmin(struct device *dev,
+                               struct device_attribute *devattr,
+                               const char *buf, size_t count)
+{
+       struct platform_data *pdata = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct temp_data *tdata = pdata->core_data[attr->index];
+       u32 eax, edx;
+       unsigned long val;
+       int diff;
+
+       if (strict_strtoul(buf, 10, &val))
+               return -EINVAL;
+
+       /*
+        * THERM_MASK_THRESHOLD0 is 7 bits wide. Values are entered in terms
+        * of milli degree celsius. Hence don't accept val > (127 * 1000)
+        */
+       if (val > tdata->tjmax || val > 127000)
+               return -EINVAL;
+
+       diff = (tdata->tjmax - val) / 1000;
+
+       mutex_lock(&tdata->update_lock);
+       rdmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, &eax, &edx);
+       eax = (eax & ~THERM_MASK_THRESHOLD0) |
+                               (diff << THERM_SHIFT_THRESHOLD0);
+       wrmsr_on_cpu(tdata->cpu, tdata->intrpt_reg, eax, edx);
+       tdata->tmin = val;
+       mutex_unlock(&tdata->update_lock);
+
+       return count;
+}
+
 static ssize_t show_temp(struct device *dev,
                        struct device_attribute *devattr, char *buf)
 {
@@ -344,23 +442,31 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev,
                                int attr_no)
 {
        int err, i;
-       static ssize_t (*rd_ptr[MAX_ATTRS]) (struct device *dev,
+       static ssize_t (*rd_ptr[TOTAL_ATTRS]) (struct device *dev,
                        struct device_attribute *devattr, char *buf) = {
-                       show_label, show_crit_alarm, show_ttarget,
-                       show_temp, show_tjmax };
-       static const char *names[MAX_ATTRS] = {
+                       show_label, show_crit_alarm, show_temp, show_tjmax,
+                       show_max_alarm, show_ttarget, show_tmin };
+       static ssize_t (*rw_ptr[TOTAL_ATTRS]) (struct device *dev,
+                       struct device_attribute *devattr, const char *buf,
+                       size_t count) = { NULL, NULL, NULL, NULL, NULL,
+                                       store_ttarget, store_tmin };
+       static const char *names[TOTAL_ATTRS] = {
                                        "temp%d_label", "temp%d_crit_alarm",
-                                       "temp%d_max", "temp%d_input",
-                                       "temp%d_crit" };
+                                       "temp%d_input", "temp%d_crit",
+                                       "temp%d_max_alarm", "temp%d_max",
+                                       "temp%d_max_hyst" };
 
-       for (i = 0; i < MAX_ATTRS; i++) {
+       for (i = 0; i < tdata->attr_size; i++) {
                snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i],
                        attr_no);
                sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr);
                tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i];
                tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO;
+               if (rw_ptr[i]) {
+                       tdata->sd_attrs[i].dev_attr.attr.mode |= S_IWUSR;
+                       tdata->sd_attrs[i].dev_attr.store = rw_ptr[i];
+               }
                tdata->sd_attrs[i].dev_attr.show = rd_ptr[i];
-               tdata->sd_attrs[i].dev_attr.store = NULL;
                tdata->sd_attrs[i].index = attr_no;
                err = device_create_file(dev, &tdata->sd_attrs[i].dev_attr);
                if (err)
@@ -374,38 +480,6 @@ exit_free:
        return err;
 }
 
-static void update_ttarget(__u8 cpu_model, struct temp_data *tdata,
-                               struct device *dev)
-{
-       int err;
-       u32 eax, edx;
-
-       /*
-        * Initialize ttarget value. Eventually this will be
-        * initialized with the value from MSR_IA32_THERM_INTERRUPT
-        * register. If IA32_TEMPERATURE_TARGET is supported, this
-        * value will be over written below.
-        * To Do: Patch to initialize ttarget from MSR_IA32_THERM_INTERRUPT
-        */
-       tdata->ttarget = tdata->tjmax - 20000;
-
-       /*
-        * Read the still undocumented IA32_TEMPERATURE_TARGET. It exists
-        * on older CPUs but not in this register,
-        * Atoms don't have it either.
-        */
-       if (cpu_model > 0xe && cpu_model != 0x1c) {
-               err = rdmsr_safe_on_cpu(tdata->cpu,
-                               MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
-               if (err) {
-                       dev_warn(dev,
-                       "Unable to read IA32_TEMPERATURE_TARGET MSR\n");
-               } else {
-                       tdata->ttarget = tdata->tjmax -
-                                       ((eax >> 8) & 0xff) * 1000;
-               }
-       }
-}
 
 static int __devinit chk_ucode_version(struct platform_device *pdev)
 {
@@ -464,9 +538,12 @@ static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag)
 
        tdata->status_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_STATUS :
                                                        MSR_IA32_THERM_STATUS;
+       tdata->intrpt_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_INTERRUPT :
+                                               MSR_IA32_THERM_INTERRUPT;
        tdata->is_pkg_data = pkg_flag;
        tdata->cpu = cpu;
        tdata->cpu_core_id = TO_CORE_ID(cpu);
+       tdata->attr_size = MAX_CORE_ATTRS;
        mutex_init(&tdata->update_lock);
        return tdata;
 }
@@ -516,7 +593,17 @@ static int create_core_data(struct platform_data *pdata,
        else
                tdata->tjmax = get_tjmax(c, cpu, &pdev->dev);
 
-       update_ttarget(c->x86_model, tdata, &pdev->dev);
+       /*
+        * Test if we can access the intrpt register. If so, increase the
+        * 'size' enough to have ttarget/tmin/max_alarm interfaces.
+        * Initialize ttarget with bits 16:22 of MSR_IA32_THERM_INTERRUPT
+        */
+       err = rdmsr_safe_on_cpu(cpu, tdata->intrpt_reg, &eax, &edx);
+       if (!err) {
+               tdata->attr_size += MAX_THRESH_ATTRS;
+               tdata->ttarget = tdata->tjmax - ((eax >> 16) & 0x7f) * 1000;
+       }
+
        pdata->core_data[attr_no] = tdata;
 
        /* Create sysfs interfaces */
@@ -553,7 +640,7 @@ static void coretemp_remove_core(struct platform_data *pdata,
        struct temp_data *tdata = pdata->core_data[indx];
 
        /* Remove the sysfs attributes */
-       for (i = 0; i < MAX_ATTRS; i++)
+       for (i = 0; i < tdata->attr_size; i++)
                device_remove_file(dev, &tdata->sd_attrs[i].dev_attr);
 
        kfree(pdata->core_data[indx]);
index 2f94f95..90ddb87 100644 (file)
@@ -54,6 +54,9 @@
  * and extended mode. They are mostly compatible with LM90 except for a data
  * format difference for the temperature value registers.
  *
+ * This driver also supports the SA56004 from Philips. This device is
+ * pin-compatible with the LM86, the ED/EDP parts are also address-compatible.
+ *
  * Since the LM90 was the first chipset supported by this driver, most
  * comments will refer to this chipset, but are actually general and
  * concern all supported chipsets, unless mentioned otherwise.
  * MAX6659 can have address 0x4c, 0x4d or 0x4e.
  * MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b,
  * 0x4c, 0x4d or 0x4e.
+ * SA56004 can have address 0x48 through 0x4F.
  */
 
 static const unsigned short normal_i2c[] = {
-       0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
+       0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
+       0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
 
 enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,
-       max6646, w83l771, max6696 };
+       max6646, w83l771, max6696, sa56004 };
 
 /*
  * The LM90 registers
@@ -152,6 +157,10 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,
 #define MAX6659_REG_R_LOCAL_EMERG      0x17
 #define MAX6659_REG_W_LOCAL_EMERG      0x17
 
+/*  SA56004 registers */
+
+#define SA56004_REG_R_LOCAL_TEMPL 0x22
+
 #define LM90_DEF_CONVRATE_RVAL 6       /* Def conversion rate register value */
 #define LM90_MAX_CONVRATE_MS   16000   /* Maximum conversion rate in ms */
 
@@ -161,7 +170,6 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,
 #define LM90_FLAG_ADT7461_EXT  (1 << 0) /* ADT7461 extended mode       */
 /* Device features */
 #define LM90_HAVE_OFFSET       (1 << 1) /* temperature offset register */
-#define LM90_HAVE_LOCAL_EXT    (1 << 2) /* extended local temperature  */
 #define LM90_HAVE_REM_LIMIT_EXT        (1 << 3) /* extended remote limit       */
 #define LM90_HAVE_EMERGENCY    (1 << 4) /* 3rd upper (emergency) limit */
 #define LM90_HAVE_EMERGENCY_ALARM (1 << 5)/* emergency alarm           */
@@ -192,6 +200,7 @@ static const struct i2c_device_id lm90_id[] = {
        { "max6696", max6696 },
        { "nct1008", adt7461 },
        { "w83l771", w83l771 },
+       { "sa56004", sa56004 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, lm90_id);
@@ -204,6 +213,7 @@ struct lm90_params {
        u16 alert_alarms;       /* Which alarm bits trigger ALERT# */
                                /* Upper 8 bits for max6695/96 */
        u8 max_convrate;        /* Maximum conversion rate register value */
+       u8 reg_local_ext;       /* Extended local temp register (optional) */
 };
 
 static const struct lm90_params lm90_params[] = {
@@ -235,19 +245,20 @@ static const struct lm90_params lm90_params[] = {
                .max_convrate = 9,
        },
        [max6646] = {
-               .flags = LM90_HAVE_LOCAL_EXT,
                .alert_alarms = 0x7c,
                .max_convrate = 6,
+               .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
        },
        [max6657] = {
-               .flags = LM90_HAVE_LOCAL_EXT,
                .alert_alarms = 0x7c,
                .max_convrate = 8,
+               .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
        },
        [max6659] = {
-               .flags = LM90_HAVE_LOCAL_EXT | LM90_HAVE_EMERGENCY,
+               .flags = LM90_HAVE_EMERGENCY,
                .alert_alarms = 0x7c,
                .max_convrate = 8,
+               .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
        },
        [max6680] = {
                .flags = LM90_HAVE_OFFSET,
@@ -255,16 +266,23 @@ static const struct lm90_params lm90_params[] = {
                .max_convrate = 7,
        },
        [max6696] = {
-               .flags = LM90_HAVE_LOCAL_EXT | LM90_HAVE_EMERGENCY
+               .flags = LM90_HAVE_EMERGENCY
                  | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3,
                .alert_alarms = 0x187c,
                .max_convrate = 6,
+               .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL,
        },
        [w83l771] = {
                .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT,
                .alert_alarms = 0x7c,
                .max_convrate = 8,
        },
+       [sa56004] = {
+               .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT,
+               .alert_alarms = 0x7b,
+               .max_convrate = 9,
+               .reg_local_ext = SA56004_REG_R_LOCAL_TEMPL,
+       },
 };
 
 /*
@@ -286,6 +304,7 @@ struct lm90_data {
        u16 alert_alarms;       /* Which alarm bits trigger ALERT# */
                                /* Upper 8 bits for max6695/96 */
        u8 max_convrate;        /* Maximum conversion rate */
+       u8 reg_local_ext;       /* local extension register offset */
 
        /* registers values */
        s8 temp8[8];    /* 0: local low limit
@@ -452,9 +471,9 @@ static struct lm90_data *lm90_update_device(struct device *dev)
                lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]);
                lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst);
 
-               if (data->flags & LM90_HAVE_LOCAL_EXT) {
+               if (data->reg_local_ext) {
                        lm90_read16(client, LM90_REG_R_LOCAL_TEMP,
-                                   MAX6657_REG_R_LOCAL_TEMPL,
+                                   data->reg_local_ext,
                                    &data->temp11[4]);
                } else {
                        if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP,
@@ -1092,7 +1111,7 @@ static int lm90_detect(struct i2c_client *new_client,
        struct i2c_adapter *adapter = new_client->adapter;
        int address = new_client->addr;
        const char *name = NULL;
-       int man_id, chip_id, reg_config1, reg_convrate;
+       int man_id, chip_id, reg_config1, reg_config2, reg_convrate;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
@@ -1108,15 +1127,16 @@ static int lm90_detect(struct i2c_client *new_client,
                                                LM90_REG_R_CONVRATE)) < 0)
                return -ENODEV;
 
-       if ((address == 0x4C || address == 0x4D)
-        && man_id == 0x01) { /* National Semiconductor */
-               int reg_config2;
-
+       if (man_id == 0x01 || man_id == 0x5C || man_id == 0x41) {
                reg_config2 = i2c_smbus_read_byte_data(new_client,
                                                LM90_REG_R_CONFIG2);
                if (reg_config2 < 0)
                        return -ENODEV;
+       } else
+               reg_config2 = 0;        /* Make compiler happy */
 
+       if ((address == 0x4C || address == 0x4D)
+        && man_id == 0x01) { /* National Semiconductor */
                if ((reg_config1 & 0x2A) == 0x00
                 && (reg_config2 & 0xF8) == 0x00
                 && reg_convrate <= 0x09) {
@@ -1245,13 +1265,6 @@ static int lm90_detect(struct i2c_client *new_client,
        } else
        if (address == 0x4C
         && man_id == 0x5C) { /* Winbond/Nuvoton */
-               int reg_config2;
-
-               reg_config2 = i2c_smbus_read_byte_data(new_client,
-                                               LM90_REG_R_CONFIG2);
-               if (reg_config2 < 0)
-                       return -ENODEV;
-
                if ((reg_config1 & 0x2A) == 0x00
                 && (reg_config2 & 0xF8) == 0x00) {
                        if (chip_id == 0x01 /* W83L771W/G */
@@ -1263,6 +1276,15 @@ static int lm90_detect(struct i2c_client *new_client,
                                name = "w83l771";
                        }
                }
+       } else
+       if (address >= 0x48 && address <= 0x4F
+        && man_id == 0xA1) { /*  NXP Semiconductor/Philips */
+               if (chip_id == 0x00
+                && (reg_config1 & 0x2A) == 0x00
+                && (reg_config2 & 0xFE) == 0x00
+                && reg_convrate <= 0x09) {
+                       name = "sa56004";
+               }
        }
 
        if (!name) { /* identification failed */
@@ -1368,6 +1390,7 @@ static int lm90_probe(struct i2c_client *new_client,
 
        /* Set chip capabilities */
        data->flags = lm90_params[data->kind].flags;
+       data->reg_local_ext = lm90_params[data->kind].reg_local_ext;
 
        /* Set maximum conversion rate */
        data->max_convrate = lm90_params[data->kind].max_convrate;
index d3b464b..513901d 100644 (file)
@@ -74,8 +74,9 @@ static const unsigned short normal_i2c[] = {
 #define TT_OFF 0
 #define TT_ON 1
 #define TT_MASK 7
-#define MANUFACTURER_ID 0x01
-#define DEFAULT_REVISION 0xA4
+#define NATSEMI_MAN_ID 0x01
+#define LM95231_CHIP_ID        0xA1
+#define LM95241_CHIP_ID        0xA4
 
 static const u8 lm95241_reg_address[] = {
        LM95241_REG_R_LOCAL_TEMPH,
@@ -338,20 +339,25 @@ static int lm95241_detect(struct i2c_client *new_client,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = new_client->adapter;
-       int address = new_client->addr;
        const char *name;
+       int mfg_id, chip_id;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
-       if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID)
-            == MANUFACTURER_ID)
-           && (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID)
-               == DEFAULT_REVISION)) {
-               name = DEVNAME;
-       } else {
-               dev_dbg(&adapter->dev, "LM95241 detection failed at 0x%02x\n",
-                       address);
+       mfg_id = i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID);
+       if (mfg_id != NATSEMI_MAN_ID)
+               return -ENODEV;
+
+       chip_id = i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID);
+       switch (chip_id) {
+       case LM95231_CHIP_ID:
+               name = "lm95231";
+               break;
+       case LM95241_CHIP_ID:
+               name = "lm95241";
+               break;
+       default:
                return -ENODEV;
        }
 
@@ -431,7 +437,8 @@ static int lm95241_remove(struct i2c_client *client)
 
 /* Driver data (common to all clients) */
 static const struct i2c_device_id lm95241_id[] = {
-       { DEVNAME, 0 },
+       { "lm95231", 0 },
+       { "lm95241", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, lm95241_id);
diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c
new file mode 100644 (file)
index 0000000..dce9e68
--- /dev/null
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2011 Alexander Stein <alexander.stein@systec-electronic.com>
+ *
+ * The LM95245 is a sensor chip made by National Semiconductors.
+ * It reports up to two temperatures (its own plus an external one).
+ * Complete datasheet can be obtained from National's website at:
+ *   http://www.national.com/ds.cgi/LM/LM95245.pdf
+ *
+ * This driver is based on lm95241.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+
+#define DEVNAME "lm95245"
+
+static const unsigned short normal_i2c[] = {
+       0x18, 0x19, 0x29, 0x4c, 0x4d, I2C_CLIENT_END };
+
+/* LM95245 registers */
+/* general registers */
+#define LM95245_REG_RW_CONFIG1         0x03
+#define LM95245_REG_RW_CONVERS_RATE    0x04
+#define LM95245_REG_W_ONE_SHOT         0x0F
+
+/* diode configuration */
+#define LM95245_REG_RW_CONFIG2         0xBF
+#define LM95245_REG_RW_REMOTE_OFFH     0x11
+#define LM95245_REG_RW_REMOTE_OFFL     0x12
+
+/* status registers */
+#define LM95245_REG_R_STATUS1          0x02
+#define LM95245_REG_R_STATUS2          0x33
+
+/* limit registers */
+#define LM95245_REG_RW_REMOTE_OS_LIMIT         0x07
+#define LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT    0x20
+#define LM95245_REG_RW_REMOTE_TCRIT_LIMIT      0x19
+#define LM95245_REG_RW_COMMON_HYSTERESIS       0x21
+
+/* temperature signed */
+#define LM95245_REG_R_LOCAL_TEMPH_S    0x00
+#define LM95245_REG_R_LOCAL_TEMPL_S    0x30
+#define LM95245_REG_R_REMOTE_TEMPH_S   0x01
+#define LM95245_REG_R_REMOTE_TEMPL_S   0x10
+/* temperature unsigned */
+#define LM95245_REG_R_REMOTE_TEMPH_U   0x31
+#define LM95245_REG_R_REMOTE_TEMPL_U   0x32
+
+/* id registers */
+#define LM95245_REG_R_MAN_ID           0xFE
+#define LM95245_REG_R_CHIP_ID          0xFF
+
+/* LM95245 specific bitfields */
+#define CFG_STOP               0x40
+#define CFG_REMOTE_TCRIT_MASK  0x10
+#define CFG_REMOTE_OS_MASK     0x08
+#define CFG_LOCAL_TCRIT_MASK   0x04
+#define CFG_LOCAL_OS_MASK      0x02
+
+#define CFG2_OS_A0             0x40
+#define CFG2_DIODE_FAULT_OS    0x20
+#define CFG2_DIODE_FAULT_TCRIT 0x10
+#define CFG2_REMOTE_TT         0x08
+#define CFG2_REMOTE_FILTER_DIS 0x00
+#define CFG2_REMOTE_FILTER_EN  0x06
+
+/* conversation rate in ms */
+#define RATE_CR0063    0x00
+#define RATE_CR0364    0x01
+#define RATE_CR1000    0x02
+#define RATE_CR2500    0x03
+
+#define STATUS1_DIODE_FAULT    0x04
+#define STATUS1_RTCRIT         0x02
+#define STATUS1_LOC            0x01
+
+#define MANUFACTURER_ID                0x01
+#define DEFAULT_REVISION       0xB3
+
+static const u8 lm95245_reg_address[] = {
+       LM95245_REG_R_LOCAL_TEMPH_S,
+       LM95245_REG_R_LOCAL_TEMPL_S,
+       LM95245_REG_R_REMOTE_TEMPH_S,
+       LM95245_REG_R_REMOTE_TEMPL_S,
+       LM95245_REG_R_REMOTE_TEMPH_U,
+       LM95245_REG_R_REMOTE_TEMPL_U,
+       LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT,
+       LM95245_REG_RW_REMOTE_TCRIT_LIMIT,
+       LM95245_REG_RW_COMMON_HYSTERESIS,
+       LM95245_REG_R_STATUS1,
+};
+
+/* Client data (each client gets its own) */
+struct lm95245_data {
+       struct device *hwmon_dev;
+       struct mutex update_lock;
+       unsigned long last_updated;     /* in jiffies */
+       unsigned long interval; /* in msecs */
+       bool valid;             /* zero until following fields are valid */
+       /* registers values */
+       u8 regs[ARRAY_SIZE(lm95245_reg_address)];
+       u8 config1, config2;
+};
+
+/* Conversions */
+static int temp_from_reg_unsigned(u8 val_h, u8 val_l)
+{
+       return val_h * 1000 + val_l * 1000 / 256;
+}
+
+static int temp_from_reg_signed(u8 val_h, u8 val_l)
+{
+       if (val_h & 0x80)
+               return (val_h - 0x100) * 1000;
+       return temp_from_reg_unsigned(val_h, val_l);
+}
+
+static struct lm95245_data *lm95245_update_device(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95245_data *data = i2c_get_clientdata(client);
+
+       mutex_lock(&data->update_lock);
+
+       if (time_after(jiffies, data->last_updated
+               + msecs_to_jiffies(data->interval)) || !data->valid) {
+               int i;
+
+               dev_dbg(&client->dev, "Updating lm95245 data.\n");
+               for (i = 0; i < ARRAY_SIZE(lm95245_reg_address); i++)
+                       data->regs[i]
+                         = i2c_smbus_read_byte_data(client,
+                                                    lm95245_reg_address[i]);
+               data->last_updated = jiffies;
+               data->valid = 1;
+       }
+
+       mutex_unlock(&data->update_lock);
+
+       return data;
+}
+
+static unsigned long lm95245_read_conversion_rate(struct i2c_client *client)
+{
+       int rate;
+       unsigned long interval;
+
+       rate = i2c_smbus_read_byte_data(client, LM95245_REG_RW_CONVERS_RATE);
+
+       switch (rate) {
+       case RATE_CR0063:
+               interval = 63;
+               break;
+       case RATE_CR0364:
+               interval = 364;
+               break;
+       case RATE_CR1000:
+               interval = 1000;
+               break;
+       case RATE_CR2500:
+       default:
+               interval = 2500;
+               break;
+       }
+
+       return interval;
+}
+
+static unsigned long lm95245_set_conversion_rate(struct i2c_client *client,
+                       unsigned long interval)
+{
+       int rate;
+
+       if (interval <= 63) {
+               interval = 63;
+               rate = RATE_CR0063;
+       } else if (interval <= 364) {
+               interval = 364;
+               rate = RATE_CR0364;
+       } else if (interval <= 1000) {
+               interval = 1000;
+               rate = RATE_CR1000;
+       } else {
+               interval = 2500;
+               rate = RATE_CR2500;
+       }
+
+       i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONVERS_RATE, rate);
+
+       return interval;
+}
+
+/* Sysfs stuff */
+static ssize_t show_input(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       struct lm95245_data *data = lm95245_update_device(dev);
+       int temp;
+       int index = to_sensor_dev_attr(attr)->index;
+
+       /*
+        * Index 0 (Local temp) is always signed
+        * Index 2 (Remote temp) has both signed and unsigned data
+        * use signed calculation for remote if signed bit is set
+        */
+       if (index == 0 || data->regs[index] & 0x80)
+               temp = temp_from_reg_signed(data->regs[index],
+                           data->regs[index + 1]);
+       else
+               temp = temp_from_reg_unsigned(data->regs[index + 2],
+                           data->regs[index + 3]);
+
+       return snprintf(buf, PAGE_SIZE - 1, "%d\n", temp);
+}
+
+static ssize_t show_limit(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       struct lm95245_data *data = lm95245_update_device(dev);
+       int index = to_sensor_dev_attr(attr)->index;
+
+       return snprintf(buf, PAGE_SIZE - 1, "%d\n",
+                       data->regs[index] * 1000);
+}
+
+static ssize_t set_limit(struct device *dev, struct device_attribute *attr,
+                       const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95245_data *data = i2c_get_clientdata(client);
+       int index = to_sensor_dev_attr(attr)->index;
+       unsigned long val;
+
+       if (strict_strtoul(buf, 10, &val) < 0)
+               return -EINVAL;
+
+       val /= 1000;
+
+       val = SENSORS_LIMIT(val, 0, (index == 6 ? 127 : 255));
+
+       mutex_lock(&data->update_lock);
+
+       data->valid = 0;
+
+       i2c_smbus_write_byte_data(client, lm95245_reg_address[index], val);
+
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t set_crit_hyst(struct device *dev, struct device_attribute *attr,
+                       const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95245_data *data = i2c_get_clientdata(client);
+       unsigned long val;
+
+       if (strict_strtoul(buf, 10, &val) < 0)
+               return -EINVAL;
+
+       val /= 1000;
+
+       val = SENSORS_LIMIT(val, 0, 31);
+
+       mutex_lock(&data->update_lock);
+
+       data->valid = 0;
+
+       /* shared crit hysteresis */
+       i2c_smbus_write_byte_data(client, LM95245_REG_RW_COMMON_HYSTERESIS,
+               val);
+
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t show_type(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95245_data *data = i2c_get_clientdata(client);
+
+       return snprintf(buf, PAGE_SIZE - 1,
+               data->config2 & CFG2_REMOTE_TT ? "1\n" : "2\n");
+}
+
+static ssize_t set_type(struct device *dev, struct device_attribute *attr,
+                       const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95245_data *data = i2c_get_clientdata(client);
+       unsigned long val;
+
+       if (strict_strtoul(buf, 10, &val) < 0)
+               return -EINVAL;
+       if (val != 1 && val != 2)
+               return -EINVAL;
+
+       mutex_lock(&data->update_lock);
+
+       if (val == 1)
+               data->config2 |= CFG2_REMOTE_TT;
+       else
+               data->config2 &= ~CFG2_REMOTE_TT;
+
+       data->valid = 0;
+
+       i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG2,
+                                 data->config2);
+
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       struct lm95245_data *data = lm95245_update_device(dev);
+       int index = to_sensor_dev_attr(attr)->index;
+
+       return snprintf(buf, PAGE_SIZE - 1, "%d\n",
+                       !!(data->regs[9] & index));
+}
+
+static ssize_t show_interval(struct device *dev, struct device_attribute *attr,
+                            char *buf)
+{
+       struct lm95245_data *data = lm95245_update_device(dev);
+
+       return snprintf(buf, PAGE_SIZE - 1, "%lu\n", data->interval);
+}
+
+static ssize_t set_interval(struct device *dev, struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm95245_data *data = i2c_get_clientdata(client);
+       unsigned long val;
+
+       if (strict_strtoul(buf, 10, &val) < 0)
+               return -EINVAL;
+
+       mutex_lock(&data->update_lock);
+
+       data->interval = lm95245_set_conversion_rate(client, val);
+
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_limit,
+               set_limit, 6);
+static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_limit,
+               set_crit_hyst, 8);
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL,
+               STATUS1_LOC);
+
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_limit,
+               set_limit, 7);
+static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_limit,
+               set_crit_hyst, 8);
+static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL,
+               STATUS1_RTCRIT);
+static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type,
+               set_type, 0);
+static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL,
+               STATUS1_DIODE_FAULT);
+
+static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval,
+               set_interval);
+
+static struct attribute *lm95245_attributes[] = {
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp1_crit.dev_attr.attr,
+       &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_input.dev_attr.attr,
+       &sensor_dev_attr_temp2_crit.dev_attr.attr,
+       &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_type.dev_attr.attr,
+       &sensor_dev_attr_temp2_fault.dev_attr.attr,
+       &dev_attr_update_interval.attr,
+       NULL
+};
+
+static const struct attribute_group lm95245_group = {
+       .attrs = lm95245_attributes,
+};
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int lm95245_detect(struct i2c_client *new_client,
+                         struct i2c_board_info *info)
+{
+       struct i2c_adapter *adapter = new_client->adapter;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return -ENODEV;
+
+       if (i2c_smbus_read_byte_data(new_client, LM95245_REG_R_MAN_ID)
+                       != MANUFACTURER_ID
+               || i2c_smbus_read_byte_data(new_client, LM95245_REG_R_CHIP_ID)
+                       != DEFAULT_REVISION)
+               return -ENODEV;
+
+       strlcpy(info->type, DEVNAME, I2C_NAME_SIZE);
+       return 0;
+}
+
+static void lm95245_init_client(struct i2c_client *client)
+{
+       struct lm95245_data *data = i2c_get_clientdata(client);
+
+       data->valid = 0;
+       data->interval = lm95245_read_conversion_rate(client);
+
+       data->config1 = i2c_smbus_read_byte_data(client,
+               LM95245_REG_RW_CONFIG1);
+       data->config2 = i2c_smbus_read_byte_data(client,
+               LM95245_REG_RW_CONFIG2);
+
+       if (data->config1 & CFG_STOP) {
+               /* Clear the standby bit */
+               data->config1 &= ~CFG_STOP;
+               i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG1,
+                       data->config1);
+       }
+}
+
+static int lm95245_probe(struct i2c_client *new_client,
+                        const struct i2c_device_id *id)
+{
+       struct lm95245_data *data;
+       int err;
+
+       data = kzalloc(sizeof(struct lm95245_data), GFP_KERNEL);
+       if (!data) {
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       i2c_set_clientdata(new_client, data);
+       mutex_init(&data->update_lock);
+
+       /* Initialize the LM95245 chip */
+       lm95245_init_client(new_client);
+
+       /* Register sysfs hooks */
+       err = sysfs_create_group(&new_client->dev.kobj, &lm95245_group);
+       if (err)
+               goto exit_free;
+
+       data->hwmon_dev = hwmon_device_register(&new_client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
+               goto exit_remove_files;
+       }
+
+       return 0;
+
+exit_remove_files:
+       sysfs_remove_group(&new_client->dev.kobj, &lm95245_group);
+exit_free:
+       kfree(data);
+exit:
+       return err;
+}
+
+static int lm95245_remove(struct i2c_client *client)
+{
+       struct lm95245_data *data = i2c_get_clientdata(client);
+
+       hwmon_device_unregister(data->hwmon_dev);
+       sysfs_remove_group(&client->dev.kobj, &lm95245_group);
+
+       kfree(data);
+       return 0;
+}
+
+/* Driver data (common to all clients) */
+static const struct i2c_device_id lm95245_id[] = {
+       { DEVNAME, 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, lm95245_id);
+
+static struct i2c_driver lm95245_driver = {
+       .class          = I2C_CLASS_HWMON,
+       .driver = {
+               .name   = DEVNAME,
+       },
+       .probe          = lm95245_probe,
+       .remove         = lm95245_remove,
+       .id_table       = lm95245_id,
+       .detect         = lm95245_detect,
+       .address_list   = normal_i2c,
+};
+
+static int __init sensors_lm95245_init(void)
+{
+       return i2c_add_driver(&lm95245_driver);
+}
+
+static void __exit sensors_lm95245_exit(void)
+{
+       i2c_del_driver(&lm95245_driver);
+}
+
+MODULE_AUTHOR("Alexander Stein <alexander.stein@systec-electronic.com>");
+MODULE_DESCRIPTION("LM95245 sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_lm95245_init);
+module_exit(sensors_lm95245_exit);
diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c
new file mode 100644 (file)
index 0000000..20d1b2d
--- /dev/null
@@ -0,0 +1,502 @@
+/*
+    Copyright (c) 2011 David George <david.george@ska.ac.za>
+
+    based on adm1021.c
+    some credit to Christoph Scheurer, but largely a rewrite
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/* Addresses to scan */
+static unsigned short max1668_addr_list[] = {
+       0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
+
+/* max1668 registers */
+
+#define MAX1668_REG_TEMP(nr)   (nr)
+#define MAX1668_REG_STAT1      0x05
+#define MAX1668_REG_STAT2      0x06
+#define MAX1668_REG_MAN_ID     0xfe
+#define MAX1668_REG_DEV_ID     0xff
+
+/* limits */
+
+/* write high limits */
+#define MAX1668_REG_LIMH_WR(nr)        (0x13 + 2 * (nr))
+/* write low limits */
+#define MAX1668_REG_LIML_WR(nr)        (0x14 + 2 * (nr))
+/* read high limits */
+#define MAX1668_REG_LIMH_RD(nr)        (0x08 + 2 * (nr))
+/* read low limits */
+#define MAX1668_REG_LIML_RD(nr)        (0x09 + 2 * (nr))
+
+/* manufacturer and device ID Constants */
+#define MAN_ID_MAXIM           0x4d
+#define DEV_ID_MAX1668         0x3
+#define DEV_ID_MAX1805         0x5
+#define DEV_ID_MAX1989         0xb
+
+/* read only mode module parameter */
+static int read_only;
+module_param(read_only, bool, 0);
+MODULE_PARM_DESC(read_only, "Don't set any values, read only mode");
+
+enum chips { max1668, max1805, max1989 };
+
+struct max1668_data {
+       struct device *hwmon_dev;
+       enum chips type;
+
+       struct mutex update_lock;
+       char valid;             /* !=0 if following fields are valid */
+       unsigned long last_updated;     /* In jiffies */
+
+       /* 1x local and 4x remote */
+       s8 temp_max[5];
+       s8 temp_min[5];
+       s8 temp[5];
+       u16 alarms;
+};
+
+static struct max1668_data *max1668_update_device(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct max1668_data *data = i2c_get_clientdata(client);
+       struct max1668_data *ret = data;
+       s32 val;
+       int i;
+
+       mutex_lock(&data->update_lock);
+
+       if (data->valid && !time_after(jiffies,
+                       data->last_updated + HZ + HZ / 2))
+               goto abort;
+
+       for (i = 0; i < 5; i++) {
+               val = i2c_smbus_read_byte_data(client, MAX1668_REG_TEMP(i));
+               if (unlikely(val < 0)) {
+                       ret = ERR_PTR(val);
+                       goto abort;
+               }
+               data->temp[i] = (s8) val;
+
+               val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIMH_RD(i));
+               if (unlikely(val < 0)) {
+                       ret = ERR_PTR(val);
+                       goto abort;
+               }
+               data->temp_max[i] = (s8) val;
+
+               val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIML_RD(i));
+               if (unlikely(val < 0)) {
+                       ret = ERR_PTR(val);
+                       goto abort;
+               }
+               data->temp_min[i] = (s8) val;
+       }
+
+       val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT1);
+       if (unlikely(val < 0)) {
+               ret = ERR_PTR(val);
+               goto abort;
+       }
+       data->alarms = val << 8;
+
+       val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT2);
+       if (unlikely(val < 0)) {
+               ret = ERR_PTR(val);
+               goto abort;
+       }
+       data->alarms |= val;
+
+       data->last_updated = jiffies;
+       data->valid = 1;
+abort:
+       mutex_unlock(&data->update_lock);
+
+       return ret;
+}
+
+static ssize_t show_temp(struct device *dev,
+                        struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct max1668_data *data = max1668_update_device(dev);
+
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       return sprintf(buf, "%d\n", data->temp[index] * 1000);
+}
+
+static ssize_t show_temp_max(struct device *dev,
+                            struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct max1668_data *data = max1668_update_device(dev);
+
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       return sprintf(buf, "%d\n", data->temp_max[index] * 1000);
+}
+
+static ssize_t show_temp_min(struct device *dev,
+                            struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct max1668_data *data = max1668_update_device(dev);
+
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       return sprintf(buf, "%d\n", data->temp_min[index] * 1000);
+}
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       int index = to_sensor_dev_attr(attr)->index;
+       struct max1668_data *data = max1668_update_device(dev);
+
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       return sprintf(buf, "%u\n", (data->alarms >> index) & 0x1);
+}
+
+static ssize_t show_fault(struct device *dev,
+                         struct device_attribute *devattr, char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct max1668_data *data = max1668_update_device(dev);
+
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       return sprintf(buf, "%u\n",
+                      (data->alarms & (1 << 12)) && data->temp[index] == 127);
+}
+
+static ssize_t set_temp_max(struct device *dev,
+                           struct device_attribute *devattr,
+                           const char *buf, size_t count)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       struct i2c_client *client = to_i2c_client(dev);
+       struct max1668_data *data = i2c_get_clientdata(client);
+       long temp;
+       int ret;
+
+       ret = kstrtol(buf, 10, &temp);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&data->update_lock);
+       data->temp_max[index] = SENSORS_LIMIT(temp/1000, -128, 127);
+       if (i2c_smbus_write_byte_data(client,
+                                       MAX1668_REG_LIMH_WR(index),
+                                       data->temp_max[index]))
+               count = -EIO;
+       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 max1668_data *data = i2c_get_clientdata(client);
+       long temp;
+       int ret;
+
+       ret = kstrtol(buf, 10, &temp);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&data->update_lock);
+       data->temp_min[index] = SENSORS_LIMIT(temp/1000, -128, 127);
+       if (i2c_smbus_write_byte_data(client,
+                                       MAX1668_REG_LIML_WR(index),
+                                       data->temp_max[index]))
+               count = -EIO;
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max,
+                               set_temp_max, 0);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp_min,
+                               set_temp_min, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp_max,
+                               set_temp_max, 1);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO, show_temp_min,
+                               set_temp_min, 1);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp_max,
+                               set_temp_max, 2);
+static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO, show_temp_min,
+                               set_temp_min, 2);
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO, show_temp_max,
+                               set_temp_max, 3);
+static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO, show_temp_min,
+                               set_temp_min, 3);
+static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp5_max, S_IRUGO, show_temp_max,
+                               set_temp_max, 4);
+static SENSOR_DEVICE_ATTR(temp5_min, S_IRUGO, show_temp_min,
+                               set_temp_min, 4);
+
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 14);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 13);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 7);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_alarm, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp5_min_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_alarm, NULL, 0);
+
+static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_fault, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_fault, NULL, 4);
+
+/* Attributes common to MAX1668, MAX1989 and MAX1805 */
+static struct attribute *max1668_attribute_common[] = {
+       &sensor_dev_attr_temp1_max.dev_attr.attr,
+       &sensor_dev_attr_temp1_min.dev_attr.attr,
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp2_max.dev_attr.attr,
+       &sensor_dev_attr_temp2_min.dev_attr.attr,
+       &sensor_dev_attr_temp2_input.dev_attr.attr,
+       &sensor_dev_attr_temp3_max.dev_attr.attr,
+       &sensor_dev_attr_temp3_min.dev_attr.attr,
+       &sensor_dev_attr_temp3_input.dev_attr.attr,
+
+       &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+
+       &sensor_dev_attr_temp2_fault.dev_attr.attr,
+       &sensor_dev_attr_temp3_fault.dev_attr.attr,
+       NULL
+};
+
+/* Attributes not present on MAX1805 */
+static struct attribute *max1668_attribute_unique[] = {
+       &sensor_dev_attr_temp4_max.dev_attr.attr,
+       &sensor_dev_attr_temp4_min.dev_attr.attr,
+       &sensor_dev_attr_temp4_input.dev_attr.attr,
+       &sensor_dev_attr_temp5_max.dev_attr.attr,
+       &sensor_dev_attr_temp5_min.dev_attr.attr,
+       &sensor_dev_attr_temp5_input.dev_attr.attr,
+
+       &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp5_min_alarm.dev_attr.attr,
+
+       &sensor_dev_attr_temp4_fault.dev_attr.attr,
+       &sensor_dev_attr_temp5_fault.dev_attr.attr,
+       NULL
+};
+
+static mode_t max1668_attribute_mode(struct kobject *kobj,
+                                    struct attribute *attr, int index)
+{
+       int ret = S_IRUGO;
+       if (read_only)
+               return ret;
+       if (attr == &sensor_dev_attr_temp1_max.dev_attr.attr ||
+           attr == &sensor_dev_attr_temp2_max.dev_attr.attr ||
+           attr == &sensor_dev_attr_temp3_max.dev_attr.attr ||
+           attr == &sensor_dev_attr_temp4_max.dev_attr.attr ||
+           attr == &sensor_dev_attr_temp5_max.dev_attr.attr ||
+           attr == &sensor_dev_attr_temp1_min.dev_attr.attr ||
+           attr == &sensor_dev_attr_temp2_min.dev_attr.attr ||
+           attr == &sensor_dev_attr_temp3_min.dev_attr.attr ||
+           attr == &sensor_dev_attr_temp4_min.dev_attr.attr ||
+           attr == &sensor_dev_attr_temp5_min.dev_attr.attr)
+               ret |= S_IWUSR;
+       return ret;
+}
+
+static const struct attribute_group max1668_group_common = {
+       .attrs = max1668_attribute_common,
+       .is_visible = max1668_attribute_mode
+};
+
+static const struct attribute_group max1668_group_unique = {
+       .attrs = max1668_attribute_unique,
+       .is_visible = max1668_attribute_mode
+};
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int max1668_detect(struct i2c_client *client,
+                         struct i2c_board_info *info)
+{
+       struct i2c_adapter *adapter = client->adapter;
+       const char *type_name;
+       int man_id, dev_id;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return -ENODEV;
+
+       /* Check for unsupported part */
+       man_id = i2c_smbus_read_byte_data(client, MAX1668_REG_MAN_ID);
+       if (man_id != MAN_ID_MAXIM)
+               return -ENODEV;
+
+       dev_id = i2c_smbus_read_byte_data(client, MAX1668_REG_DEV_ID);
+       if (dev_id < 0)
+               return -ENODEV;
+
+       type_name = NULL;
+       if (dev_id == DEV_ID_MAX1668)
+               type_name = "max1668";
+       else if (dev_id == DEV_ID_MAX1805)
+               type_name = "max1805";
+       else if (dev_id == DEV_ID_MAX1989)
+               type_name = "max1989";
+
+       if (!type_name)
+               return -ENODEV;
+
+       strlcpy(info->type, type_name, I2C_NAME_SIZE);
+
+       return 0;
+}
+
+static int max1668_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct i2c_adapter *adapter = client->adapter;
+       struct max1668_data *data;
+       int err;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return -ENODEV;
+
+       data = kzalloc(sizeof(struct max1668_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, data);
+       data->type = id->driver_data;
+       mutex_init(&data->update_lock);
+
+       /* Register sysfs hooks */
+       err = sysfs_create_group(&client->dev.kobj, &max1668_group_common);
+       if (err)
+               goto error_free;
+
+       if (data->type == max1668 || data->type == max1989) {
+               err = sysfs_create_group(&client->dev.kobj,
+                                        &max1668_group_unique);
+               if (err)
+                       goto error_sysrem0;
+       }
+
+       data->hwmon_dev = hwmon_device_register(&client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
+               goto error_sysrem1;
+       }
+
+       return 0;
+
+error_sysrem1:
+       if (data->type == max1668 || data->type == max1989)
+               sysfs_remove_group(&client->dev.kobj, &max1668_group_unique);
+error_sysrem0:
+       sysfs_remove_group(&client->dev.kobj, &max1668_group_common);
+error_free:
+       kfree(data);
+       return err;
+}
+
+static int max1668_remove(struct i2c_client *client)
+{
+       struct max1668_data *data = i2c_get_clientdata(client);
+
+       hwmon_device_unregister(data->hwmon_dev);
+       if (data->type == max1668 || data->type == max1989)
+               sysfs_remove_group(&client->dev.kobj, &max1668_group_unique);
+
+       sysfs_remove_group(&client->dev.kobj, &max1668_group_common);
+
+       kfree(data);
+       return 0;
+}
+
+static const struct i2c_device_id max1668_id[] = {
+       { "max1668", max1668 },
+       { "max1805", max1805 },
+       { "max1989", max1989 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max1668_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver max1668_driver = {
+       .class = I2C_CLASS_HWMON,
+       .driver = {
+                 .name = "max1668",
+                 },
+       .probe = max1668_probe,
+       .remove = max1668_remove,
+       .id_table = max1668_id,
+       .detect = max1668_detect,
+       .address_list = max1668_addr_list,
+};
+
+static int __init sensors_max1668_init(void)
+{
+       return i2c_add_driver(&max1668_driver);
+}
+
+static void __exit sensors_max1668_exit(void)
+{
+       i2c_del_driver(&max1668_driver);
+}
+
+MODULE_AUTHOR("David George <david.george@ska.ac.za>");
+MODULE_DESCRIPTION("MAX1668 remote temperature sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_max1668_init)
+module_exit(sensors_max1668_exit)
diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c
new file mode 100644 (file)
index 0000000..d7926f4
--- /dev/null
@@ -0,0 +1,453 @@
+/*
+ * ntc_thermistor.c - NTC Thermistors
+ *
+ *  Copyright (C) 2010 Samsung Electronics
+ *  MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/math64.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+
+#include <linux/platform_data/ntc_thermistor.h>
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+struct ntc_compensation {
+       int             temp_C;
+       unsigned int    ohm;
+};
+
+/*
+ * A compensation table should be sorted by the values of .ohm
+ * in descending order.
+ * The following compensation tables are from the specification of Murata NTC
+ * Thermistors Datasheet
+ */
+const struct ntc_compensation ncpXXwb473[] = {
+       { .temp_C       = -40, .ohm     = 1747920 },
+       { .temp_C       = -35, .ohm     = 1245428 },
+       { .temp_C       = -30, .ohm     = 898485 },
+       { .temp_C       = -25, .ohm     = 655802 },
+       { .temp_C       = -20, .ohm     = 483954 },
+       { .temp_C       = -15, .ohm     = 360850 },
+       { .temp_C       = -10, .ohm     = 271697 },
+       { .temp_C       = -5, .ohm      = 206463 },
+       { .temp_C       = 0, .ohm       = 158214 },
+       { .temp_C       = 5, .ohm       = 122259 },
+       { .temp_C       = 10, .ohm      = 95227 },
+       { .temp_C       = 15, .ohm      = 74730 },
+       { .temp_C       = 20, .ohm      = 59065 },
+       { .temp_C       = 25, .ohm      = 47000 },
+       { .temp_C       = 30, .ohm      = 37643 },
+       { .temp_C       = 35, .ohm      = 30334 },
+       { .temp_C       = 40, .ohm      = 24591 },
+       { .temp_C       = 45, .ohm      = 20048 },
+       { .temp_C       = 50, .ohm      = 16433 },
+       { .temp_C       = 55, .ohm      = 13539 },
+       { .temp_C       = 60, .ohm      = 11209 },
+       { .temp_C       = 65, .ohm      = 9328 },
+       { .temp_C       = 70, .ohm      = 7798 },
+       { .temp_C       = 75, .ohm      = 6544 },
+       { .temp_C       = 80, .ohm      = 5518 },
+       { .temp_C       = 85, .ohm      = 4674 },
+       { .temp_C       = 90, .ohm      = 3972 },
+       { .temp_C       = 95, .ohm      = 3388 },
+       { .temp_C       = 100, .ohm     = 2902 },
+       { .temp_C       = 105, .ohm     = 2494 },
+       { .temp_C       = 110, .ohm     = 2150 },
+       { .temp_C       = 115, .ohm     = 1860 },
+       { .temp_C       = 120, .ohm     = 1615 },
+       { .temp_C       = 125, .ohm     = 1406 },
+};
+const struct ntc_compensation ncpXXwl333[] = {
+       { .temp_C       = -40, .ohm     = 1610154 },
+       { .temp_C       = -35, .ohm     = 1130850 },
+       { .temp_C       = -30, .ohm     = 802609 },
+       { .temp_C       = -25, .ohm     = 575385 },
+       { .temp_C       = -20, .ohm     = 416464 },
+       { .temp_C       = -15, .ohm     = 304219 },
+       { .temp_C       = -10, .ohm     = 224193 },
+       { .temp_C       = -5, .ohm      = 166623 },
+       { .temp_C       = 0, .ohm       = 124850 },
+       { .temp_C       = 5, .ohm       = 94287 },
+       { .temp_C       = 10, .ohm      = 71747 },
+       { .temp_C       = 15, .ohm      = 54996 },
+       { .temp_C       = 20, .ohm      = 42455 },
+       { .temp_C       = 25, .ohm      = 33000 },
+       { .temp_C       = 30, .ohm      = 25822 },
+       { .temp_C       = 35, .ohm      = 20335 },
+       { .temp_C       = 40, .ohm      = 16115 },
+       { .temp_C       = 45, .ohm      = 12849 },
+       { .temp_C       = 50, .ohm      = 10306 },
+       { .temp_C       = 55, .ohm      = 8314 },
+       { .temp_C       = 60, .ohm      = 6746 },
+       { .temp_C       = 65, .ohm      = 5503 },
+       { .temp_C       = 70, .ohm      = 4513 },
+       { .temp_C       = 75, .ohm      = 3721 },
+       { .temp_C       = 80, .ohm      = 3084 },
+       { .temp_C       = 85, .ohm      = 2569 },
+       { .temp_C       = 90, .ohm      = 2151 },
+       { .temp_C       = 95, .ohm      = 1809 },
+       { .temp_C       = 100, .ohm     = 1529 },
+       { .temp_C       = 105, .ohm     = 1299 },
+       { .temp_C       = 110, .ohm     = 1108 },
+       { .temp_C       = 115, .ohm     = 949 },
+       { .temp_C       = 120, .ohm     = 817 },
+       { .temp_C       = 125, .ohm     = 707 },
+};
+
+struct ntc_data {
+       struct device *hwmon_dev;
+       struct ntc_thermistor_platform_data *pdata;
+       const struct ntc_compensation *comp;
+       struct device *dev;
+       int n_comp;
+       char name[PLATFORM_NAME_SIZE];
+};
+
+static inline u64 div64_u64_safe(u64 dividend, u64 divisor)
+{
+       if (divisor == 0 && dividend == 0)
+               return 0;
+       if (divisor == 0)
+               return UINT_MAX;
+       return div64_u64(dividend, divisor);
+}
+
+static unsigned int get_ohm_of_thermistor(struct ntc_data *data,
+               unsigned int uV)
+{
+       struct ntc_thermistor_platform_data *pdata = data->pdata;
+       u64 mV = uV / 1000;
+       u64 pmV = pdata->pullup_uV / 1000;
+       u64 N, puO, pdO;
+       puO = pdata->pullup_ohm;
+       pdO = pdata->pulldown_ohm;
+
+       if (mV == 0) {
+               if (pdata->connect == NTC_CONNECTED_POSITIVE)
+                       return UINT_MAX;
+               return 0;
+       }
+       if (mV >= pmV)
+               return (pdata->connect == NTC_CONNECTED_POSITIVE) ?
+                       0 : UINT_MAX;
+
+       if (pdata->connect == NTC_CONNECTED_POSITIVE && puO == 0)
+               N = div64_u64_safe(pdO * (pmV - mV), mV);
+       else if (pdata->connect == NTC_CONNECTED_GROUND && pdO == 0)
+               N = div64_u64_safe(puO * mV, pmV - mV);
+       else if (pdata->connect == NTC_CONNECTED_POSITIVE)
+               N = div64_u64_safe(pdO * puO * (pmV - mV),
+                               puO * mV - pdO * (pmV - mV));
+       else
+               N = div64_u64_safe(pdO * puO * mV, pdO * (pmV - mV) - puO * mV);
+
+       return (unsigned int) N;
+}
+
+static int lookup_comp(struct ntc_data *data,
+               unsigned int ohm, int *i_low, int *i_high)
+{
+       int start, end, mid = -1;
+
+       /* Do a binary search on compensation table */
+       start = 0;
+       end = data->n_comp;
+
+       while (end > start) {
+               mid = start + (end - start) / 2;
+               if (data->comp[mid].ohm < ohm)
+                       end = mid;
+               else if (data->comp[mid].ohm > ohm)
+                       start = mid + 1;
+               else
+                       break;
+       }
+
+       if (mid == 0) {
+               if (data->comp[mid].ohm > ohm) {
+                       *i_high = mid;
+                       *i_low = mid + 1;
+                       return 0;
+               } else {
+                       *i_low = mid;
+                       *i_high = -1;
+                       return -EINVAL;
+               }
+       }
+       if (mid == (data->n_comp - 1)) {
+               if (data->comp[mid].ohm <= ohm) {
+                       *i_low = mid;
+                       *i_high = mid - 1;
+                       return 0;
+               } else {
+                       *i_low = -1;
+                       *i_high = mid;
+                       return -EINVAL;
+               }
+       }
+
+       if (data->comp[mid].ohm <= ohm) {
+               *i_low = mid;
+               *i_high = mid - 1;
+       }
+       if (data->comp[mid].ohm > ohm) {
+               *i_low = mid + 1;
+               *i_high = mid;
+       }
+
+       return 0;
+}
+
+static int get_temp_mC(struct ntc_data *data, unsigned int ohm, int *temp)
+{
+       int low, high;
+       int ret;
+
+       ret = lookup_comp(data, ohm, &low, &high);
+       if (ret) {
+               /* Unable to use linear approximation */
+               if (low != -1)
+                       *temp = data->comp[low].temp_C * 1000;
+               else if (high != -1)
+                       *temp = data->comp[high].temp_C * 1000;
+               else
+                       return ret;
+       } else {
+               *temp = data->comp[low].temp_C * 1000 +
+                       ((data->comp[high].temp_C - data->comp[low].temp_C) *
+                        1000 * ((int)ohm - (int)data->comp[low].ohm)) /
+                       ((int)data->comp[high].ohm - (int)data->comp[low].ohm);
+       }
+
+       return 0;
+}
+
+static int ntc_thermistor_read(struct ntc_data *data, int *temp)
+{
+       int ret;
+       int read_ohm, read_uV;
+       unsigned int ohm = 0;
+
+       if (data->pdata->read_ohm) {
+               read_ohm = data->pdata->read_ohm();
+               if (read_ohm < 0)
+                       return read_ohm;
+               ohm = (unsigned int)read_ohm;
+       }
+
+       if (data->pdata->read_uV) {
+               read_uV = data->pdata->read_uV();
+               if (read_uV < 0)
+                       return read_uV;
+               ohm = get_ohm_of_thermistor(data, (unsigned int)read_uV);
+       }
+
+       ret = get_temp_mC(data, ohm, temp);
+       if (ret) {
+               dev_dbg(data->dev, "Sensor reading function not available.\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static ssize_t ntc_show_name(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct ntc_data *data = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%s\n", data->name);
+}
+
+static ssize_t ntc_show_type(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "4\n");
+}
+
+static ssize_t ntc_show_temp(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct ntc_data *data = dev_get_drvdata(dev);
+       int temp, ret;
+
+       ret = ntc_thermistor_read(data, &temp);
+       if (ret)
+               return ret;
+       return sprintf(buf, "%d\n", temp);
+}
+
+static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ntc_show_temp, NULL, 0);
+static DEVICE_ATTR(name, S_IRUGO, ntc_show_name, NULL);
+
+static struct attribute *ntc_attributes[] = {
+       &dev_attr_name.attr,
+       &sensor_dev_attr_temp1_type.dev_attr.attr,
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       NULL,
+};
+
+static const struct attribute_group ntc_attr_group = {
+       .attrs = ntc_attributes,
+};
+
+static int __devinit ntc_thermistor_probe(struct platform_device *pdev)
+{
+       struct ntc_data *data;
+       struct ntc_thermistor_platform_data *pdata = pdev->dev.platform_data;
+       int ret = 0;
+
+       if (!pdata) {
+               dev_err(&pdev->dev, "No platform init data supplied.\n");
+               return -ENODEV;
+       }
+
+       /* Either one of the two is required. */
+       if (!pdata->read_uV && !pdata->read_ohm) {
+               dev_err(&pdev->dev, "Both read_uV and read_ohm missing."
+                               "Need either one of the two.\n");
+               return -EINVAL;
+       }
+
+       if (pdata->read_uV && pdata->read_ohm) {
+               dev_warn(&pdev->dev, "Only one of read_uV and read_ohm "
+                               "is needed; ignoring read_uV.\n");
+               pdata->read_uV = NULL;
+       }
+
+       if (pdata->read_uV && (pdata->pullup_uV == 0 ||
+                               (pdata->pullup_ohm == 0 && pdata->connect ==
+                                NTC_CONNECTED_GROUND) ||
+                               (pdata->pulldown_ohm == 0 && pdata->connect ==
+                                NTC_CONNECTED_POSITIVE) ||
+                               (pdata->connect != NTC_CONNECTED_POSITIVE &&
+                                pdata->connect != NTC_CONNECTED_GROUND))) {
+               dev_err(&pdev->dev, "Required data to use read_uV not "
+                               "supplied.\n");
+               return -EINVAL;
+       }
+
+       data = kzalloc(sizeof(struct ntc_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->dev = &pdev->dev;
+       data->pdata = pdata;
+       strncpy(data->name, pdev->id_entry->name, PLATFORM_NAME_SIZE);
+
+       switch (pdev->id_entry->driver_data) {
+       case TYPE_NCPXXWB473:
+               data->comp = ncpXXwb473;
+               data->n_comp = ARRAY_SIZE(ncpXXwb473);
+               break;
+       case TYPE_NCPXXWL333:
+               data->comp = ncpXXwl333;
+               data->n_comp = ARRAY_SIZE(ncpXXwl333);
+               break;
+       default:
+               dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n",
+                               pdev->id_entry->driver_data,
+                               pdev->id_entry->name);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       platform_set_drvdata(pdev, data);
+
+       ret = sysfs_create_group(&data->dev->kobj, &ntc_attr_group);
+       if (ret) {
+               dev_err(data->dev, "unable to create sysfs files\n");
+               goto err;
+       }
+
+       data->hwmon_dev = hwmon_device_register(data->dev);
+       if (IS_ERR_OR_NULL(data->hwmon_dev)) {
+               dev_err(data->dev, "unable to register as hwmon device.\n");
+               ret = -EINVAL;
+               goto err_after_sysfs;
+       }
+
+       dev_info(&pdev->dev, "Thermistor %s:%d (type: %s/%lu) successfully probed.\n",
+                       pdev->name, pdev->id, pdev->id_entry->name,
+                       pdev->id_entry->driver_data);
+       return 0;
+err_after_sysfs:
+       sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
+err:
+       kfree(data);
+       return ret;
+}
+
+static int __devexit ntc_thermistor_remove(struct platform_device *pdev)
+{
+       struct ntc_data *data = platform_get_drvdata(pdev);
+
+       hwmon_device_unregister(data->hwmon_dev);
+       sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
+       platform_set_drvdata(pdev, NULL);
+
+       kfree(data);
+
+       return 0;
+}
+
+static const struct platform_device_id ntc_thermistor_id[] = {
+       { "ncp15wb473", TYPE_NCPXXWB473 },
+       { "ncp18wb473", TYPE_NCPXXWB473 },
+       { "ncp21wb473", TYPE_NCPXXWB473 },
+       { "ncp03wb473", TYPE_NCPXXWB473 },
+       { "ncp15wl333", TYPE_NCPXXWL333 },
+       { },
+};
+
+static struct platform_driver ntc_thermistor_driver = {
+       .driver = {
+               .name = "ntc-thermistor",
+               .owner = THIS_MODULE,
+       },
+       .probe = ntc_thermistor_probe,
+       .remove = __devexit_p(ntc_thermistor_remove),
+       .id_table = ntc_thermistor_id,
+};
+
+static int __init ntc_thermistor_init(void)
+{
+       return platform_driver_register(&ntc_thermistor_driver);
+}
+
+module_init(ntc_thermistor_init);
+
+static void __exit ntc_thermistor_cleanup(void)
+{
+       platform_driver_unregister(&ntc_thermistor_driver);
+}
+
+module_exit(ntc_thermistor_cleanup);
+
+MODULE_DESCRIPTION("NTC Thermistor Driver");
+MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ntc-thermistor");
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
new file mode 100644 (file)
index 0000000..c9237b9
--- /dev/null
@@ -0,0 +1,100 @@
+#
+# PMBus chip drivers configuration
+#
+
+menuconfig PMBUS
+       tristate "PMBus support"
+       depends on I2C && EXPERIMENTAL
+       default n
+       help
+         Say yes here if you want to enable PMBus support.
+
+         This driver can also be built as a module. If so, the module will
+         be called pmbus_core.
+
+if PMBUS
+
+config SENSORS_PMBUS
+       tristate "Generic PMBus devices"
+       default y
+       help
+         If you say yes here you get hardware monitoring support for generic
+         PMBus devices, including but not limited to ADP4000, BMR450, BMR451,
+         BMR453, BMR454, LTC2978, NCP4200, and NCP4208.
+
+         This driver can also be built as a module. If so, the module will
+         be called pmbus.
+
+config SENSORS_ADM1275
+       tristate "Analog Devices ADM1275"
+       default n
+       help
+         If you say yes here you get hardware monitoring support for Analog
+         Devices ADM1275 Hot-Swap Controller and Digital Power Monitor.
+
+         This driver can also be built as a module. If so, the module will
+         be called adm1275.
+
+config SENSORS_LM25066
+       tristate "National Semiconductor LM25066 and compatibles"
+       default n
+       help
+         If you say yes here you get hardware monitoring support for National
+         Semiconductor LM25066, LM5064, and LM5066.
+
+         This driver can also be built as a module. If so, the module will
+         be called lm25066.
+
+config SENSORS_MAX16064
+       tristate "Maxim MAX16064"
+       default n
+       help
+         If you say yes here you get hardware monitoring support for Maxim
+         MAX16064.
+
+         This driver can also be built as a module. If so, the module will
+         be called max16064.
+
+config SENSORS_MAX34440
+       tristate "Maxim MAX34440/MAX34441"
+       default n
+       help
+         If you say yes here you get hardware monitoring support for Maxim
+         MAX34440 and MAX34441.
+
+         This driver can also be built as a module. If so, the module will
+         be called max34440.
+
+config SENSORS_MAX8688
+       tristate "Maxim MAX8688"
+       default n
+       help
+         If you say yes here you get hardware monitoring support for Maxim
+         MAX8688.
+
+         This driver can also be built as a module. If so, the module will
+         be called max8688.
+
+config SENSORS_UCD9000
+       tristate "TI UCD90120, UCD90124, UCD9090, UCD90910"
+       default n
+       help
+         If you say yes here you get hardware monitoring support for TI
+         UCD90120, UCD90124, UCD9090, UCD90910 Sequencer and System Health
+         Controllers.
+
+         This driver can also be built as a module. If so, the module will
+         be called ucd9000.
+
+config SENSORS_UCD9200
+       tristate "TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, UCD9248"
+       default n
+       help
+         If you say yes here you get hardware monitoring support for TI
+         UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and UCD9248
+         Digital PWM System Controllers.
+
+         This driver can also be built as a module. If so, the module will
+         be called ucd9200.
+
+endif # PMBUS
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
new file mode 100644 (file)
index 0000000..623eedb
--- /dev/null
@@ -0,0 +1,13 @@
+#
+# Makefile for PMBus chip drivers.
+#
+
+obj-$(CONFIG_PMBUS)            += pmbus_core.o
+obj-$(CONFIG_SENSORS_PMBUS)    += pmbus.o
+obj-$(CONFIG_SENSORS_ADM1275)  += adm1275.o
+obj-$(CONFIG_SENSORS_LM25066)  += lm25066.o
+obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
+obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
+obj-$(CONFIG_SENSORS_MAX8688)  += max8688.o
+obj-$(CONFIG_SENSORS_UCD9000)  += ucd9000.o
+obj-$(CONFIG_SENSORS_UCD9200)  += ucd9200.o
similarity index 67%
rename from drivers/hwmon/adm1275.c
rename to drivers/hwmon/pmbus/adm1275.c
index 8bc1bd6..c936e27 100644 (file)
 #include <linux/i2c.h>
 #include "pmbus.h"
 
+#define ADM1275_PEAK_IOUT              0xd0
+#define ADM1275_PEAK_VIN               0xd1
+#define ADM1275_PEAK_VOUT              0xd2
 #define ADM1275_PMON_CONFIG            0xd4
 
 #define ADM1275_VIN_VOUT_SELECT                (1 << 6)
 #define ADM1275_VRANGE                 (1 << 5)
 
+static int adm1275_read_word_data(struct i2c_client *client, int page, int reg)
+{
+       int ret;
+
+       if (page)
+               return -EINVAL;
+
+       switch (reg) {
+       case PMBUS_VIRT_READ_IOUT_MAX:
+               ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_IOUT);
+               break;
+       case PMBUS_VIRT_READ_VOUT_MAX:
+               ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VOUT);
+               break;
+       case PMBUS_VIRT_READ_VIN_MAX:
+               ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VIN);
+               break;
+       case PMBUS_VIRT_RESET_IOUT_HISTORY:
+       case PMBUS_VIRT_RESET_VOUT_HISTORY:
+       case PMBUS_VIRT_RESET_VIN_HISTORY:
+               ret = 0;
+               break;
+       default:
+               ret = -ENODATA;
+               break;
+       }
+       return ret;
+}
+
+static int adm1275_write_word_data(struct i2c_client *client, int page, int reg,
+                                  u16 word)
+{
+       int ret;
+
+       if (page)
+               return -EINVAL;
+
+       switch (reg) {
+       case PMBUS_VIRT_RESET_IOUT_HISTORY:
+               ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_IOUT, 0);
+               break;
+       case PMBUS_VIRT_RESET_VOUT_HISTORY:
+               ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VOUT, 0);
+               break;
+       case PMBUS_VIRT_RESET_VIN_HISTORY:
+               ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VIN, 0);
+               break;
+       default:
+               ret = -ENODATA;
+               break;
+       }
+       return ret;
+}
+
 static int adm1275_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
@@ -50,14 +107,17 @@ static int adm1275_probe(struct i2c_client *client,
        }
 
        info->pages = 1;
-       info->direct[PSC_VOLTAGE_IN] = true;
-       info->direct[PSC_VOLTAGE_OUT] = true;
-       info->direct[PSC_CURRENT_OUT] = true;
+       info->format[PSC_VOLTAGE_IN] = direct;
+       info->format[PSC_VOLTAGE_OUT] = direct;
+       info->format[PSC_CURRENT_OUT] = direct;
        info->m[PSC_CURRENT_OUT] = 807;
        info->b[PSC_CURRENT_OUT] = 20475;
        info->R[PSC_CURRENT_OUT] = -1;
        info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
 
+       info->read_word_data = adm1275_read_word_data;
+       info->write_word_data = adm1275_write_word_data;
+
        if (config & ADM1275_VRANGE) {
                info->m[PSC_VOLTAGE_IN] = 19199;
                info->b[PSC_VOLTAGE_IN] = 0;
diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c
new file mode 100644 (file)
index 0000000..d4bc114
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * Hardware monitoring driver for LM25066 / LM5064 / LM5066
+ *
+ * Copyright (c) 2011 Ericsson AB.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include "pmbus.h"
+
+enum chips { lm25066, lm5064, lm5066 };
+
+#define LM25066_READ_VAUX              0xd0
+#define LM25066_MFR_READ_IIN           0xd1
+#define LM25066_MFR_READ_PIN           0xd2
+#define LM25066_MFR_IIN_OC_WARN_LIMIT  0xd3
+#define LM25066_MFR_PIN_OP_WARN_LIMIT  0xd4
+#define LM25066_READ_PIN_PEAK          0xd5
+#define LM25066_CLEAR_PIN_PEAK         0xd6
+#define LM25066_DEVICE_SETUP           0xd9
+#define LM25066_READ_AVG_VIN           0xdc
+#define LM25066_READ_AVG_VOUT          0xdd
+#define LM25066_READ_AVG_IIN           0xde
+#define LM25066_READ_AVG_PIN           0xdf
+
+#define LM25066_DEV_SETUP_CL           (1 << 4)        /* Current limit */
+
+struct lm25066_data {
+       int id;
+       struct pmbus_driver_info info;
+};
+
+#define to_lm25066_data(x)  container_of(x, struct lm25066_data, info)
+
+static int lm25066_read_word_data(struct i2c_client *client, int page, int reg)
+{
+       const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+       const struct lm25066_data *data = to_lm25066_data(info);
+       int ret;
+
+       if (page > 1)
+               return -EINVAL;
+
+       /* Map READ_VAUX into READ_VOUT register on page 1 */
+       if (page == 1) {
+               switch (reg) {
+               case PMBUS_READ_VOUT:
+                       ret = pmbus_read_word_data(client, 0,
+                                                  LM25066_READ_VAUX);
+                       if (ret < 0)
+                               break;
+                       /* Adjust returned value to match VOUT coefficients */
+                       switch (data->id) {
+                       case lm25066:
+                               /* VOUT: 4.54 mV VAUX: 283.2 uV LSB */
+                               ret = DIV_ROUND_CLOSEST(ret * 2832, 45400);
+                               break;
+                       case lm5064:
+                               /* VOUT: 4.53 mV VAUX: 700 uV LSB */
+                               ret = DIV_ROUND_CLOSEST(ret * 70, 453);
+                               break;
+                       case lm5066:
+                               /* VOUT: 2.18 mV VAUX: 725 uV LSB */
+                               ret = DIV_ROUND_CLOSEST(ret * 725, 2180);
+                               break;
+                       }
+                       break;
+               default:
+                       /* No other valid registers on page 1 */
+                       ret = -EINVAL;
+                       break;
+               }
+               goto done;
+       }
+
+       switch (reg) {
+       case PMBUS_READ_IIN:
+               ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_IIN);
+               break;
+       case PMBUS_READ_PIN:
+               ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_PIN);
+               break;
+       case PMBUS_IIN_OC_WARN_LIMIT:
+               ret = pmbus_read_word_data(client, 0,
+                                          LM25066_MFR_IIN_OC_WARN_LIMIT);
+               break;
+       case PMBUS_PIN_OP_WARN_LIMIT:
+               ret = pmbus_read_word_data(client, 0,
+                                          LM25066_MFR_PIN_OP_WARN_LIMIT);
+               break;
+       case PMBUS_VIRT_READ_VIN_AVG:
+               ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VIN);
+               break;
+       case PMBUS_VIRT_READ_VOUT_AVG:
+               ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VOUT);
+               break;
+       case PMBUS_VIRT_READ_IIN_AVG:
+               ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_IIN);
+               break;
+       case PMBUS_VIRT_READ_PIN_AVG:
+               ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_PIN);
+               break;
+       case PMBUS_VIRT_READ_PIN_MAX:
+               ret = pmbus_read_word_data(client, 0, LM25066_READ_PIN_PEAK);
+               break;
+       case PMBUS_VIRT_RESET_PIN_HISTORY:
+               ret = 0;
+               break;
+       default:
+               ret = -ENODATA;
+               break;
+       }
+done:
+       return ret;
+}
+
+static int lm25066_write_word_data(struct i2c_client *client, int page, int reg,
+                                  u16 word)
+{
+       int ret;
+
+       if (page > 1)
+               return -EINVAL;
+
+       switch (reg) {
+       case PMBUS_IIN_OC_WARN_LIMIT:
+               ret = pmbus_write_word_data(client, 0,
+                                           LM25066_MFR_IIN_OC_WARN_LIMIT,
+                                           word);
+               break;
+       case PMBUS_PIN_OP_WARN_LIMIT:
+               ret = pmbus_write_word_data(client, 0,
+                                           LM25066_MFR_PIN_OP_WARN_LIMIT,
+                                           word);
+               break;
+       case PMBUS_VIRT_RESET_PIN_HISTORY:
+               ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK);
+               break;
+       default:
+               ret = -ENODATA;
+               break;
+       }
+       return ret;
+}
+
+static int lm25066_probe(struct i2c_client *client,
+                         const struct i2c_device_id *id)
+{
+       int config;
+       int ret;
+       struct lm25066_data *data;
+       struct pmbus_driver_info *info;
+
+       if (!i2c_check_functionality(client->adapter,
+                                    I2C_FUNC_SMBUS_READ_BYTE_DATA))
+               return -ENODEV;
+
+       data = kzalloc(sizeof(struct lm25066_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       config = i2c_smbus_read_byte_data(client, LM25066_DEVICE_SETUP);
+       if (config < 0) {
+               ret = config;
+               goto err_mem;
+       }
+
+       data->id = id->driver_data;
+       info = &data->info;
+
+       info->pages = 2;
+       info->format[PSC_VOLTAGE_IN] = direct;
+       info->format[PSC_VOLTAGE_OUT] = direct;
+       info->format[PSC_CURRENT_IN] = direct;
+       info->format[PSC_TEMPERATURE] = direct;
+       info->format[PSC_POWER] = direct;
+
+       info->m[PSC_TEMPERATURE] = 16;
+       info->b[PSC_TEMPERATURE] = 0;
+       info->R[PSC_TEMPERATURE] = 0;
+
+       info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT
+         | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_PIN | PMBUS_HAVE_IIN
+         | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+       info->func[1] = PMBUS_HAVE_VOUT;
+
+       info->read_word_data = lm25066_read_word_data;
+       info->write_word_data = lm25066_write_word_data;
+
+       switch (id->driver_data) {
+       case lm25066:
+               info->m[PSC_VOLTAGE_IN] = 22070;
+               info->b[PSC_VOLTAGE_IN] = 0;
+               info->R[PSC_VOLTAGE_IN] = -2;
+               info->m[PSC_VOLTAGE_OUT] = 22070;
+               info->b[PSC_VOLTAGE_OUT] = 0;
+               info->R[PSC_VOLTAGE_OUT] = -2;
+
+               if (config & LM25066_DEV_SETUP_CL) {
+                       info->m[PSC_CURRENT_IN] = 6852;
+                       info->b[PSC_CURRENT_IN] = 0;
+                       info->R[PSC_CURRENT_IN] = -2;
+                       info->m[PSC_POWER] = 369;
+                       info->b[PSC_POWER] = 0;
+                       info->R[PSC_POWER] = -2;
+               } else {
+                       info->m[PSC_CURRENT_IN] = 13661;
+                       info->b[PSC_CURRENT_IN] = 0;
+                       info->R[PSC_CURRENT_IN] = -2;
+                       info->m[PSC_POWER] = 736;
+                       info->b[PSC_POWER] = 0;
+                       info->R[PSC_POWER] = -2;
+               }
+               break;
+       case lm5064:
+               info->m[PSC_VOLTAGE_IN] = 22075;
+               info->b[PSC_VOLTAGE_IN] = 0;
+               info->R[PSC_VOLTAGE_IN] = -2;
+               info->m[PSC_VOLTAGE_OUT] = 22075;
+               info->b[PSC_VOLTAGE_OUT] = 0;
+               info->R[PSC_VOLTAGE_OUT] = -2;
+
+               if (config & LM25066_DEV_SETUP_CL) {
+                       info->m[PSC_CURRENT_IN] = 6713;
+                       info->b[PSC_CURRENT_IN] = 0;
+                       info->R[PSC_CURRENT_IN] = -2;
+                       info->m[PSC_POWER] = 3619;
+                       info->b[PSC_POWER] = 0;
+                       info->R[PSC_POWER] = -3;
+               } else {
+                       info->m[PSC_CURRENT_IN] = 13426;
+                       info->b[PSC_CURRENT_IN] = 0;
+                       info->R[PSC_CURRENT_IN] = -2;
+                       info->m[PSC_POWER] = 7238;
+                       info->b[PSC_POWER] = 0;
+                       info->R[PSC_POWER] = -3;
+               }
+               break;
+       case lm5066:
+               info->m[PSC_VOLTAGE_IN] = 4587;
+               info->b[PSC_VOLTAGE_IN] = 0;
+               info->R[PSC_VOLTAGE_IN] = -2;
+               info->m[PSC_VOLTAGE_OUT] = 4587;
+               info->b[PSC_VOLTAGE_OUT] = 0;
+               info->R[PSC_VOLTAGE_OUT] = -2;
+
+               if (config & LM25066_DEV_SETUP_CL) {
+                       info->m[PSC_CURRENT_IN] = 10753;
+                       info->b[PSC_CURRENT_IN] = 0;
+                       info->R[PSC_CURRENT_IN] = -2;
+                       info->m[PSC_POWER] = 1204;
+                       info->b[PSC_POWER] = 0;
+                       info->R[PSC_POWER] = -3;
+               } else {
+                       info->m[PSC_CURRENT_IN] = 5405;
+                       info->b[PSC_CURRENT_IN] = 0;
+                       info->R[PSC_CURRENT_IN] = -2;
+                       info->m[PSC_POWER] = 605;
+                       info->b[PSC_POWER] = 0;
+                       info->R[PSC_POWER] = -3;
+               }
+               break;
+       default:
+               ret = -ENODEV;
+               goto err_mem;
+       }
+
+       ret = pmbus_do_probe(client, id, info);
+       if (ret)
+               goto err_mem;
+       return 0;
+
+err_mem:
+       kfree(data);
+       return ret;
+}
+
+static int lm25066_remove(struct i2c_client *client)
+{
+       const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+       const struct lm25066_data *data = to_lm25066_data(info);
+       int ret;
+
+       ret = pmbus_do_remove(client);
+       kfree(data);
+       return ret;
+}
+
+static const struct i2c_device_id lm25066_id[] = {
+       {"lm25066", lm25066},
+       {"lm5064", lm5064},
+       {"lm5066", lm5066},
+       { }
+};
+
+MODULE_DEVICE_TABLE(i2c, lm25066_id);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver lm25066_driver = {
+       .driver = {
+                  .name = "lm25066",
+                  },
+       .probe = lm25066_probe,
+       .remove = lm25066_remove,
+       .id_table = lm25066_id,
+};
+
+static int __init lm25066_init(void)
+{
+       return i2c_add_driver(&lm25066_driver);
+}
+
+static void __exit lm25066_exit(void)
+{
+       i2c_del_driver(&lm25066_driver);
+}
+
+MODULE_AUTHOR("Guenter Roeck");
+MODULE_DESCRIPTION("PMBus driver for LM25066/LM5064/LM5066");
+MODULE_LICENSE("GPL");
+module_init(lm25066_init);
+module_exit(lm25066_exit);
similarity index 66%
rename from drivers/hwmon/max16064.c
rename to drivers/hwmon/pmbus/max16064.c
index 1d6d717..e50b296 100644 (file)
 #include <linux/i2c.h>
 #include "pmbus.h"
 
+#define MAX16064_MFR_VOUT_PEAK         0xd4
+#define MAX16064_MFR_TEMPERATURE_PEAK  0xd6
+
+static int max16064_read_word_data(struct i2c_client *client, int page, int reg)
+{
+       int ret;
+
+       switch (reg) {
+       case PMBUS_VIRT_READ_VOUT_MAX:
+               ret = pmbus_read_word_data(client, page,
+                                          MAX16064_MFR_VOUT_PEAK);
+               break;
+       case PMBUS_VIRT_READ_TEMP_MAX:
+               ret = pmbus_read_word_data(client, page,
+                                          MAX16064_MFR_TEMPERATURE_PEAK);
+               break;
+       case PMBUS_VIRT_RESET_VOUT_HISTORY:
+       case PMBUS_VIRT_RESET_TEMP_HISTORY:
+               ret = 0;
+               break;
+       default:
+               ret = -ENODATA;
+               break;
+       }
+       return ret;
+}
+
+static int max16064_write_word_data(struct i2c_client *client, int page,
+                                   int reg, u16 word)
+{
+       int ret;
+
+       switch (reg) {
+       case PMBUS_VIRT_RESET_VOUT_HISTORY:
+               ret = pmbus_write_word_data(client, page,
+                                           MAX16064_MFR_VOUT_PEAK, 0);
+               break;
+       case PMBUS_VIRT_RESET_TEMP_HISTORY:
+               ret = pmbus_write_word_data(client, page,
+                                           MAX16064_MFR_TEMPERATURE_PEAK,
+                                           0xffff);
+               break;
+       default:
+               ret = -ENODATA;
+               break;
+       }
+       return ret;
+}
+
 static struct pmbus_driver_info max16064_info = {
        .pages = 4,
-       .direct[PSC_VOLTAGE_IN] = true,
-       .direct[PSC_VOLTAGE_OUT] = true,
-       .direct[PSC_TEMPERATURE] = true,
+       .format[PSC_VOLTAGE_IN] = direct,
+       .format[PSC_VOLTAGE_OUT] = direct,
+       .format[PSC_TEMPERATURE] = direct,
        .m[PSC_VOLTAGE_IN] = 19995,
        .b[PSC_VOLTAGE_IN] = 0,
        .R[PSC_VOLTAGE_IN] = -1,
@@ -44,6 +93,8 @@ static struct pmbus_driver_info max16064_info = {
        .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
        .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
        .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+       .read_word_data = max16064_read_word_data,
+       .write_word_data = max16064_write_word_data,
 };
 
 static int max16064_probe(struct i2c_client *client,
similarity index 76%
rename from drivers/hwmon/max34440.c
rename to drivers/hwmon/pmbus/max34440.c
index db11e1a..fda621d 100644 (file)
 
 enum chips { max34440, max34441 };
 
+#define MAX34440_MFR_VOUT_PEAK         0xd4
+#define MAX34440_MFR_IOUT_PEAK         0xd5
+#define MAX34440_MFR_TEMPERATURE_PEAK  0xd6
+
 #define MAX34440_STATUS_OC_WARN                (1 << 0)
 #define MAX34440_STATUS_OC_FAULT       (1 << 1)
 #define MAX34440_STATUS_OT_FAULT       (1 << 5)
 #define MAX34440_STATUS_OT_WARN                (1 << 6)
 
+static int max34440_read_word_data(struct i2c_client *client, int page, int reg)
+{
+       int ret;
+
+       switch (reg) {
+       case PMBUS_VIRT_READ_VOUT_MAX:
+               ret = pmbus_read_word_data(client, page,
+                                          MAX34440_MFR_VOUT_PEAK);
+               break;
+       case PMBUS_VIRT_READ_IOUT_MAX:
+               ret = pmbus_read_word_data(client, page,
+                                          MAX34440_MFR_IOUT_PEAK);
+               break;
+       case PMBUS_VIRT_READ_TEMP_MAX:
+               ret = pmbus_read_word_data(client, page,
+                                          MAX34440_MFR_TEMPERATURE_PEAK);
+               break;
+       case PMBUS_VIRT_RESET_VOUT_HISTORY:
+       case PMBUS_VIRT_RESET_IOUT_HISTORY:
+       case PMBUS_VIRT_RESET_TEMP_HISTORY:
+               ret = 0;
+               break;
+       default:
+               ret = -ENODATA;
+               break;
+       }
+       return ret;
+}
+
+static int max34440_write_word_data(struct i2c_client *client, int page,
+                                   int reg, u16 word)
+{
+       int ret;
+
+       switch (reg) {
+       case PMBUS_VIRT_RESET_VOUT_HISTORY:
+               ret = pmbus_write_word_data(client, page,
+                                           MAX34440_MFR_VOUT_PEAK, 0);
+               break;
+       case PMBUS_VIRT_RESET_IOUT_HISTORY:
+               ret = pmbus_write_word_data(client, page,
+                                           MAX34440_MFR_IOUT_PEAK, 0);
+               break;
+       case PMBUS_VIRT_RESET_TEMP_HISTORY:
+               ret = pmbus_write_word_data(client, page,
+                                           MAX34440_MFR_TEMPERATURE_PEAK,
+                                           0xffff);
+               break;
+       default:
+               ret = -ENODATA;
+               break;
+       }
+       return ret;
+}
+
 static int max34440_read_byte_data(struct i2c_client *client, int page, int reg)
 {
        int ret;
@@ -72,10 +131,10 @@ static int max34440_read_byte_data(struct i2c_client *client, int page, int reg)
 static struct pmbus_driver_info max34440_info[] = {
        [max34440] = {
                .pages = 14,
-               .direct[PSC_VOLTAGE_IN] = true,
-               .direct[PSC_VOLTAGE_OUT] = true,
-               .direct[PSC_TEMPERATURE] = true,
-               .direct[PSC_CURRENT_OUT] = true,
+               .format[PSC_VOLTAGE_IN] = direct,
+               .format[PSC_VOLTAGE_OUT] = direct,
+               .format[PSC_TEMPERATURE] = direct,
+               .format[PSC_CURRENT_OUT] = direct,
                .m[PSC_VOLTAGE_IN] = 1,
                .b[PSC_VOLTAGE_IN] = 0,
                .R[PSC_VOLTAGE_IN] = 3,     /* R = 0 in datasheet reflects mV */
@@ -109,14 +168,16 @@ static struct pmbus_driver_info max34440_info[] = {
                .func[12] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
                .func[13] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
                .read_byte_data = max34440_read_byte_data,
+               .read_word_data = max34440_read_word_data,
+               .write_word_data = max34440_write_word_data,
        },
        [max34441] = {
                .pages = 12,
-               .direct[PSC_VOLTAGE_IN] = true,
-               .direct[PSC_VOLTAGE_OUT] = true,
-               .direct[PSC_TEMPERATURE] = true,
-               .direct[PSC_CURRENT_OUT] = true,
-               .direct[PSC_FAN] = true,
+               .format[PSC_VOLTAGE_IN] = direct,
+               .format[PSC_VOLTAGE_OUT] = direct,
+               .format[PSC_TEMPERATURE] = direct,
+               .format[PSC_CURRENT_OUT] = direct,
+               .format[PSC_FAN] = direct,
                .m[PSC_VOLTAGE_IN] = 1,
                .b[PSC_VOLTAGE_IN] = 0,
                .R[PSC_VOLTAGE_IN] = 3,
@@ -150,6 +211,8 @@ static struct pmbus_driver_info max34440_info[] = {
                .func[10] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
                .func[11] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
                .read_byte_data = max34440_read_byte_data,
+               .read_word_data = max34440_read_word_data,
+               .write_word_data = max34440_write_word_data,
        },
 };
 
similarity index 72%
rename from drivers/hwmon/max8688.c
rename to drivers/hwmon/pmbus/max8688.c
index 7fb93f4..c3e72f1 100644 (file)
@@ -25,6 +25,9 @@
 #include <linux/i2c.h>
 #include "pmbus.h"
 
+#define MAX8688_MFR_VOUT_PEAK          0xd4
+#define MAX8688_MFR_IOUT_PEAK          0xd5
+#define MAX8688_MFR_TEMPERATURE_PEAK   0xd6
 #define MAX8688_MFG_STATUS             0xd8
 
 #define MAX8688_STATUS_OC_FAULT                (1 << 4)
 #define MAX8688_STATUS_OT_FAULT                (1 << 13)
 #define MAX8688_STATUS_OT_WARNING      (1 << 14)
 
+static int max8688_read_word_data(struct i2c_client *client, int page, int reg)
+{
+       int ret;
+
+       if (page)
+               return -EINVAL;
+
+       switch (reg) {
+       case PMBUS_VIRT_READ_VOUT_MAX:
+               ret = pmbus_read_word_data(client, 0, MAX8688_MFR_VOUT_PEAK);
+               break;
+       case PMBUS_VIRT_READ_IOUT_MAX:
+               ret = pmbus_read_word_data(client, 0, MAX8688_MFR_IOUT_PEAK);
+               break;
+       case PMBUS_VIRT_READ_TEMP_MAX:
+               ret = pmbus_read_word_data(client, 0,
+                                          MAX8688_MFR_TEMPERATURE_PEAK);
+               break;
+       case PMBUS_VIRT_RESET_VOUT_HISTORY:
+       case PMBUS_VIRT_RESET_IOUT_HISTORY:
+       case PMBUS_VIRT_RESET_TEMP_HISTORY:
+               ret = 0;
+               break;
+       default:
+               ret = -ENODATA;
+               break;
+       }
+       return ret;
+}
+
+static int max8688_write_word_data(struct i2c_client *client, int page, int reg,
+                                  u16 word)
+{
+       int ret;
+
+       switch (reg) {
+       case PMBUS_VIRT_RESET_VOUT_HISTORY:
+               ret = pmbus_write_word_data(client, 0, MAX8688_MFR_VOUT_PEAK,
+                                           0);
+               break;
+       case PMBUS_VIRT_RESET_IOUT_HISTORY:
+               ret = pmbus_write_word_data(client, 0, MAX8688_MFR_IOUT_PEAK,
+                                           0);
+               break;
+       case PMBUS_VIRT_RESET_TEMP_HISTORY:
+               ret = pmbus_write_word_data(client, 0,
+                                           MAX8688_MFR_TEMPERATURE_PEAK,
+                                           0xffff);
+               break;
+       default:
+               ret = -ENODATA;
+               break;
+       }
+       return ret;
+}
+
 static int max8688_read_byte_data(struct i2c_client *client, int page, int reg)
 {
        int ret = 0;
@@ -91,10 +150,10 @@ static int max8688_read_byte_data(struct i2c_client *client, int page, int reg)
 
 static struct pmbus_driver_info max8688_info = {
        .pages = 1,
-       .direct[PSC_VOLTAGE_IN] = true,
-       .direct[PSC_VOLTAGE_OUT] = true,
-       .direct[PSC_TEMPERATURE] = true,
-       .direct[PSC_CURRENT_OUT] = true,
+       .format[PSC_VOLTAGE_IN] = direct,
+       .format[PSC_VOLTAGE_OUT] = direct,
+       .format[PSC_TEMPERATURE] = direct,
+       .format[PSC_CURRENT_OUT] = direct,
        .m[PSC_VOLTAGE_IN] = 19995,
        .b[PSC_VOLTAGE_IN] = 0,
        .R[PSC_VOLTAGE_IN] = -1,
@@ -111,6 +170,8 @@ static struct pmbus_driver_info max8688_info = {
                | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
                | PMBUS_HAVE_STATUS_TEMP,
        .read_byte_data = max8688_read_byte_data,
+       .read_word_data = max8688_read_word_data,
+       .write_word_data = max8688_write_word_data,
 };
 
 static int max8688_probe(struct i2c_client *client,
similarity index 89%
rename from drivers/hwmon/pmbus.c
rename to drivers/hwmon/pmbus/pmbus.c
index 9b1f0c3..73de9f1 100644 (file)
@@ -96,6 +96,8 @@ static void pmbus_find_sensor_groups(struct i2c_client *client,
 static int pmbus_identify(struct i2c_client *client,
                          struct pmbus_driver_info *info)
 {
+       int ret = 0;
+
        if (!info->pages) {
                /*
                 * Check if the PAGE command is supported. If it is,
@@ -117,6 +119,27 @@ static int pmbus_identify(struct i2c_client *client,
                }
        }
 
+       if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) {
+               int vout_mode;
+
+               vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE);
+               if (vout_mode >= 0 && vout_mode != 0xff) {
+                       switch (vout_mode >> 5) {
+                       case 0:
+                               break;
+                       case 1:
+                               info->format[PSC_VOLTAGE_OUT] = vid;
+                               break;
+                       case 2:
+                               info->format[PSC_VOLTAGE_OUT] = direct;
+                               break;
+                       default:
+                               ret = -ENODEV;
+                               goto abort;
+                       }
+               }
+       }
+
        /*
         * We should check if the COEFFICIENTS register is supported.
         * If it is, and the chip is configured for direct mode, we can read
@@ -125,13 +148,18 @@ static int pmbus_identify(struct i2c_client *client,
         *
         * To do this, we will need access to a chip which actually supports the
         * COEFFICIENTS command, since the command is too complex to implement
-        * without testing it.
+        * without testing it. Until then, abort if a chip configured for direct
+        * mode was detected.
         */
+       if (info->format[PSC_VOLTAGE_OUT] == direct) {
+               ret = -ENODEV;
+               goto abort;
+       }
 
        /* Try to find sensor groups  */
        pmbus_find_sensor_groups(client, info);
-
-       return 0;
+abort:
+       return ret;
 }
 
 static int pmbus_probe(struct i2c_client *client,
@@ -172,11 +200,14 @@ static int pmbus_remove(struct i2c_client *client)
  * Use driver_data to set the number of pages supported by the chip.
  */
 static const struct i2c_device_id pmbus_id[] = {
+       {"adp4000", 1},
        {"bmr450", 1},
        {"bmr451", 1},
        {"bmr453", 1},
        {"bmr454", 1},
        {"ltc2978", 8},
+       {"ncp4200", 1},
+       {"ncp4208", 1},
        {"pmbus", 0},
        {}
 };
similarity index 79%
rename from drivers/hwmon/pmbus.h
rename to drivers/hwmon/pmbus/pmbus.h
index 50647ab..0808d98 100644 (file)
 #define PMBUS_MFR_DATE                 0x9D
 #define PMBUS_MFR_SERIAL               0x9E
 
+/*
+ * Virtual registers.
+ * Useful to support attributes which are not supported by standard PMBus
+ * registers but exist as manufacturer specific registers on individual chips.
+ * Must be mapped to real registers in device specific code.
+ *
+ * Semantics:
+ * Virtual registers are all word size.
+ * READ registers are read-only; writes are either ignored or return an error.
+ * RESET registers are read/write. Reading returns zero (used for detection),
+ * writing any value causes the associated history to be reset.
+ */
+#define PMBUS_VIRT_BASE                        0x100
+#define PMBUS_VIRT_READ_TEMP_MIN       (PMBUS_VIRT_BASE + 0)
+#define PMBUS_VIRT_READ_TEMP_MAX       (PMBUS_VIRT_BASE + 1)
+#define PMBUS_VIRT_RESET_TEMP_HISTORY  (PMBUS_VIRT_BASE + 2)
+#define PMBUS_VIRT_READ_VIN_AVG                (PMBUS_VIRT_BASE + 3)
+#define PMBUS_VIRT_READ_VIN_MIN                (PMBUS_VIRT_BASE + 4)
+#define PMBUS_VIRT_READ_VIN_MAX                (PMBUS_VIRT_BASE + 5)
+#define PMBUS_VIRT_RESET_VIN_HISTORY   (PMBUS_VIRT_BASE + 6)
+#define PMBUS_VIRT_READ_IIN_AVG                (PMBUS_VIRT_BASE + 7)
+#define PMBUS_VIRT_READ_IIN_MIN                (PMBUS_VIRT_BASE + 8)
+#define PMBUS_VIRT_READ_IIN_MAX                (PMBUS_VIRT_BASE + 9)
+#define PMBUS_VIRT_RESET_IIN_HISTORY   (PMBUS_VIRT_BASE + 10)
+#define PMBUS_VIRT_READ_PIN_AVG                (PMBUS_VIRT_BASE + 11)
+#define PMBUS_VIRT_READ_PIN_MAX                (PMBUS_VIRT_BASE + 12)
+#define PMBUS_VIRT_RESET_PIN_HISTORY   (PMBUS_VIRT_BASE + 13)
+#define PMBUS_VIRT_READ_VOUT_AVG       (PMBUS_VIRT_BASE + 14)
+#define PMBUS_VIRT_READ_VOUT_MIN       (PMBUS_VIRT_BASE + 15)
+#define PMBUS_VIRT_READ_VOUT_MAX       (PMBUS_VIRT_BASE + 16)
+#define PMBUS_VIRT_RESET_VOUT_HISTORY  (PMBUS_VIRT_BASE + 17)
+#define PMBUS_VIRT_READ_IOUT_AVG       (PMBUS_VIRT_BASE + 18)
+#define PMBUS_VIRT_READ_IOUT_MIN       (PMBUS_VIRT_BASE + 19)
+#define PMBUS_VIRT_READ_IOUT_MAX       (PMBUS_VIRT_BASE + 20)
+#define PMBUS_VIRT_RESET_IOUT_HISTORY  (PMBUS_VIRT_BASE + 21)
+
 /*
  * CAPABILITY
  */
@@ -266,11 +302,11 @@ enum pmbus_sensor_classes {
 #define PMBUS_HAVE_STATUS_FAN12        (1 << 16)
 #define PMBUS_HAVE_STATUS_FAN34        (1 << 17)
 
+enum pmbus_data_format { linear = 0, direct, vid };
+
 struct pmbus_driver_info {
        int pages;              /* Total number of pages */
-       bool direct[PSC_NUM_CLASSES];
-                               /* true if device uses direct data format
-                                  for the given sensor class */
+       enum pmbus_data_format format[PSC_NUM_CLASSES];
        /*
         * Support one set of coefficients for each sensor type
         * Used for chips providing data in direct mode.
@@ -286,6 +322,9 @@ struct pmbus_driver_info {
         * necessary.
         */
        int (*read_byte_data)(struct i2c_client *client, int page, int reg);
+       int (*read_word_data)(struct i2c_client *client, int page, int reg);
+       int (*write_word_data)(struct i2c_client *client, int page, int reg,
+                              u16 word);
        /*
         * The identify function determines supported PMBus functionality.
         * This function is only necessary if a chip driver supports multiple
@@ -299,6 +338,9 @@ struct pmbus_driver_info {
 
 int pmbus_set_page(struct i2c_client *client, u8 page);
 int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg);
+int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word);
+int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg);
+int pmbus_write_byte(struct i2c_client *client, int page, u8 value);
 void pmbus_clear_faults(struct i2c_client *client);
 bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg);
 bool pmbus_check_word_register(struct i2c_client *client, int page, int reg);
similarity index 84%
rename from drivers/hwmon/pmbus_core.c
rename to drivers/hwmon/pmbus/pmbus_core.c
index 8e31a8e..5c1b6cf 100644 (file)
 /*
  * Constants needed to determine number of sensors, booleans, and labels.
  */
-#define PMBUS_MAX_INPUT_SENSORS                11      /* 6*volt, 3*curr, 2*power */
-#define PMBUS_VOUT_SENSORS_PER_PAGE    5       /* input, min, max, lcrit,
-                                                  crit */
-#define PMBUS_IOUT_SENSORS_PER_PAGE    4       /* input, min, max, crit */
+#define PMBUS_MAX_INPUT_SENSORS                22      /* 10*volt, 7*curr, 5*power */
+#define PMBUS_VOUT_SENSORS_PER_PAGE    9       /* input, min, max, lcrit,
+                                                  crit, lowest, highest, avg,
+                                                  reset */
+#define PMBUS_IOUT_SENSORS_PER_PAGE    8       /* input, min, max, crit,
+                                                  lowest, highest, avg,
+                                                  reset */
 #define PMBUS_POUT_SENSORS_PER_PAGE    4       /* input, cap, max, crit */
 #define PMBUS_MAX_SENSORS_PER_FAN      1       /* input */
-#define PMBUS_MAX_SENSORS_PER_TEMP     5       /* input, min, max, lcrit,
-                                                  crit */
+#define PMBUS_MAX_SENSORS_PER_TEMP     8       /* input, min, max, lcrit,
+                                                  crit, lowest, highest,
+                                                  reset */
 
 #define PMBUS_MAX_INPUT_BOOLEANS       7       /* v: min_alarm, max_alarm,
                                                   lcrit_alarm, crit_alarm;
 #define PB_STATUS_INPUT_BASE   (PB_STATUS_FAN34_BASE + PMBUS_PAGES)
 #define PB_STATUS_TEMP_BASE    (PB_STATUS_INPUT_BASE + 1)
 
+#define PMBUS_NAME_SIZE                24
+
 struct pmbus_sensor {
-       char name[I2C_NAME_SIZE];       /* sysfs sensor name */
+       char name[PMBUS_NAME_SIZE];     /* sysfs sensor name */
        struct sensor_device_attribute attribute;
        u8 page;                /* page number */
-       u8 reg;                 /* register */
+       u16 reg;                /* register */
        enum pmbus_sensor_classes class;        /* sensor class */
        bool update;            /* runtime sensor update needed */
        int data;               /* Sensor data.
@@ -86,14 +92,14 @@ struct pmbus_sensor {
 };
 
 struct pmbus_boolean {
-       char name[I2C_NAME_SIZE];       /* sysfs boolean name */
+       char name[PMBUS_NAME_SIZE];     /* sysfs boolean name */
        struct sensor_device_attribute attribute;
 };
 
 struct pmbus_label {
-       char name[I2C_NAME_SIZE];       /* sysfs label name */
+       char name[PMBUS_NAME_SIZE];     /* sysfs label name */
        struct sensor_device_attribute attribute;
-       char label[I2C_NAME_SIZE];      /* label */
+       char label[PMBUS_NAME_SIZE];    /* label */
 };
 
 struct pmbus_data {
@@ -162,19 +168,21 @@ int pmbus_set_page(struct i2c_client *client, u8 page)
 }
 EXPORT_SYMBOL_GPL(pmbus_set_page);
 
-static int pmbus_write_byte(struct i2c_client *client, u8 page, u8 value)
+int pmbus_write_byte(struct i2c_client *client, int page, u8 value)
 {
        int rv;
 
-       rv = pmbus_set_page(client, page);
-       if (rv < 0)
-               return rv;
+       if (page >= 0) {
+               rv = pmbus_set_page(client, page);
+               if (rv < 0)
+                       return rv;
+       }
 
        return i2c_smbus_write_byte(client, value);
 }
+EXPORT_SYMBOL_GPL(pmbus_write_byte);
 
-static int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg,
-                                u16 word)
+int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word)
 {
        int rv;
 
@@ -184,6 +192,28 @@ static int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg,
 
        return i2c_smbus_write_word_data(client, reg, word);
 }
+EXPORT_SYMBOL_GPL(pmbus_write_word_data);
+
+/*
+ * _pmbus_write_word_data() is similar to pmbus_write_word_data(), but checks if
+ * a device specific mapping function exists and calls it if necessary.
+ */
+static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg,
+                                 u16 word)
+{
+       struct pmbus_data *data = i2c_get_clientdata(client);
+       const struct pmbus_driver_info *info = data->info;
+       int status;
+
+       if (info->write_word_data) {
+               status = info->write_word_data(client, page, reg, word);
+               if (status != -ENODATA)
+                       return status;
+       }
+       if (reg >= PMBUS_VIRT_BASE)
+               return -EINVAL;
+       return pmbus_write_word_data(client, page, reg, word);
+}
 
 int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg)
 {
@@ -197,16 +227,57 @@ int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg)
 }
 EXPORT_SYMBOL_GPL(pmbus_read_word_data);
 
-static int pmbus_read_byte_data(struct i2c_client *client, u8 page, u8 reg)
+/*
+ * _pmbus_read_word_data() is similar to pmbus_read_word_data(), but checks if
+ * a device specific mapping function exists and calls it if necessary.
+ */
+static int _pmbus_read_word_data(struct i2c_client *client, int page, int reg)
+{
+       struct pmbus_data *data = i2c_get_clientdata(client);
+       const struct pmbus_driver_info *info = data->info;
+       int status;
+
+       if (info->read_word_data) {
+               status = info->read_word_data(client, page, reg);
+               if (status != -ENODATA)
+                       return status;
+       }
+       if (reg >= PMBUS_VIRT_BASE)
+               return -EINVAL;
+       return pmbus_read_word_data(client, page, reg);
+}
+
+int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg)
 {
        int rv;
 
-       rv = pmbus_set_page(client, page);
-       if (rv < 0)
-               return rv;
+       if (page >= 0) {
+               rv = pmbus_set_page(client, page);
+               if (rv < 0)
+                       return rv;
+       }
 
        return i2c_smbus_read_byte_data(client, reg);
 }
+EXPORT_SYMBOL_GPL(pmbus_read_byte_data);
+
+/*
+ * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if
+ * a device specific mapping function exists and calls it if necessary.
+ */
+static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+       struct pmbus_data *data = i2c_get_clientdata(client);
+       const struct pmbus_driver_info *info = data->info;
+       int status;
+
+       if (info->read_byte_data) {
+               status = info->read_byte_data(client, page, reg);
+               if (status != -ENODATA)
+                       return status;
+       }
+       return pmbus_read_byte_data(client, page, reg);
+}
 
 static void pmbus_clear_fault_page(struct i2c_client *client, int page)
 {
@@ -223,13 +294,13 @@ void pmbus_clear_faults(struct i2c_client *client)
 }
 EXPORT_SYMBOL_GPL(pmbus_clear_faults);
 
-static int pmbus_check_status_cml(struct i2c_client *client, int page)
+static int pmbus_check_status_cml(struct i2c_client *client)
 {
        int status, status2;
 
-       status = pmbus_read_byte_data(client, page, PMBUS_STATUS_BYTE);
+       status = pmbus_read_byte_data(client, -1, PMBUS_STATUS_BYTE);
        if (status < 0 || (status & PB_STATUS_CML)) {
-               status2 = pmbus_read_byte_data(client, page, PMBUS_STATUS_CML);
+               status2 = pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML);
                if (status2 < 0 || (status2 & PB_CML_FAULT_INVALID_COMMAND))
                        return -EINVAL;
        }
@@ -241,10 +312,10 @@ bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg)
        int rv;
        struct pmbus_data *data = i2c_get_clientdata(client);
 
-       rv = pmbus_read_byte_data(client, page, reg);
+       rv = _pmbus_read_byte_data(client, page, reg);
        if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK))
-               rv = pmbus_check_status_cml(client, page);
-       pmbus_clear_fault_page(client, page);
+               rv = pmbus_check_status_cml(client);
+       pmbus_clear_fault_page(client, -1);
        return rv >= 0;
 }
 EXPORT_SYMBOL_GPL(pmbus_check_byte_register);
@@ -254,10 +325,10 @@ bool pmbus_check_word_register(struct i2c_client *client, int page, int reg)
        int rv;
        struct pmbus_data *data = i2c_get_clientdata(client);
 
-       rv = pmbus_read_word_data(client, page, reg);
+       rv = _pmbus_read_word_data(client, page, reg);
        if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK))
-               rv = pmbus_check_status_cml(client, page);
-       pmbus_clear_fault_page(client, page);
+               rv = pmbus_check_status_cml(client);
+       pmbus_clear_fault_page(client, -1);
        return rv >= 0;
 }
 EXPORT_SYMBOL_GPL(pmbus_check_word_register);
@@ -270,24 +341,6 @@ const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client)
 }
 EXPORT_SYMBOL_GPL(pmbus_get_driver_info);
 
-/*
- * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if
- * a device specific mapping funcion exists and calls it if necessary.
- */
-static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg)
-{
-       struct pmbus_data *data = i2c_get_clientdata(client);
-       const struct pmbus_driver_info *info = data->info;
-       int status;
-
-       if (info->read_byte_data) {
-               status = info->read_byte_data(client, page, reg);
-               if (status != -ENODATA)
-                       return status;
-       }
-       return pmbus_read_byte_data(client, page, reg);
-}
-
 static struct pmbus_data *pmbus_update_device(struct device *dev)
 {
        struct i2c_client *client = to_i2c_client(dev);
@@ -347,8 +400,9 @@ static struct pmbus_data *pmbus_update_device(struct device *dev)
 
                        if (!data->valid || sensor->update)
                                sensor->data
-                                   = pmbus_read_word_data(client, sensor->page,
-                                                          sensor->reg);
+                                   = _pmbus_read_word_data(client,
+                                                           sensor->page,
+                                                           sensor->reg);
                }
                pmbus_clear_faults(client);
                data->last_updated = jiffies;
@@ -443,15 +497,37 @@ static long pmbus_reg2data_direct(struct pmbus_data *data,
        return (val - b) / m;
 }
 
+/*
+ * Convert VID sensor values to milli- or micro-units
+ * depending on sensor type.
+ * We currently only support VR11.
+ */
+static long pmbus_reg2data_vid(struct pmbus_data *data,
+                              struct pmbus_sensor *sensor)
+{
+       long val = sensor->data;
+
+       if (val < 0x02 || val > 0xb2)
+               return 0;
+       return DIV_ROUND_CLOSEST(160000 - (val - 2) * 625, 100);
+}
+
 static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor)
 {
        long val;
 
-       if (data->info->direct[sensor->class])
+       switch (data->info->format[sensor->class]) {
+       case direct:
                val = pmbus_reg2data_direct(data, sensor);
-       else
+               break;
+       case vid:
+               val = pmbus_reg2data_vid(data, sensor);
+               break;
+       case linear:
+       default:
                val = pmbus_reg2data_linear(data, sensor);
-
+               break;
+       }
        return val;
 }
 
@@ -561,16 +637,31 @@ static u16 pmbus_data2reg_direct(struct pmbus_data *data,
        return val;
 }
 
+static u16 pmbus_data2reg_vid(struct pmbus_data *data,
+                             enum pmbus_sensor_classes class, long val)
+{
+       val = SENSORS_LIMIT(val, 500, 1600);
+
+       return 2 + DIV_ROUND_CLOSEST((1600 - val) * 100, 625);
+}
+
 static u16 pmbus_data2reg(struct pmbus_data *data,
                          enum pmbus_sensor_classes class, long val)
 {
        u16 regval;
 
-       if (data->info->direct[class])
+       switch (data->info->format[class]) {
+       case direct:
                regval = pmbus_data2reg_direct(data, class, val);
-       else
+               break;
+       case vid:
+               regval = pmbus_data2reg_vid(data, class, val);
+               break;
+       case linear:
+       default:
                regval = pmbus_data2reg_linear(data, class, val);
-
+               break;
+       }
        return regval;
 }
 
@@ -682,7 +773,7 @@ static ssize_t pmbus_set_sensor(struct device *dev,
 
        mutex_lock(&data->update_lock);
        regval = pmbus_data2reg(data, sensor->class, val);
-       ret = pmbus_write_word_data(client, sensor->page, sensor->reg, regval);
+       ret = _pmbus_write_word_data(client, sensor->page, sensor->reg, regval);
        if (ret < 0)
                rv = ret;
        else
@@ -867,7 +958,8 @@ static void pmbus_find_max_attr(struct i2c_client *client,
  * and its associated alarm attribute.
  */
 struct pmbus_limit_attr {
-       u8 reg;                 /* Limit register */
+       u16 reg;                /* Limit register */
+       bool update;            /* True if register needs updates */
        const char *attr;       /* Attribute name */
        const char *alarm;      /* Alarm attribute name */
        u32 sbit;               /* Alarm attribute status bit */
@@ -912,9 +1004,10 @@ static bool pmbus_add_limit_attrs(struct i2c_client *client,
                if (pmbus_check_word_register(client, page, l->reg)) {
                        cindex = data->num_sensors;
                        pmbus_add_sensor(data, name, l->attr, index, page,
-                                        l->reg, attr->class, attr->update,
+                                        l->reg, attr->class,
+                                        attr->update || l->update,
                                         false);
-                       if (info->func[page] & attr->sfunc) {
+                       if (l->sbit && (info->func[page] & attr->sfunc)) {
                                if (attr->compare) {
                                        pmbus_add_boolean_cmp(data, name,
                                                l->alarm, index,
@@ -953,9 +1046,11 @@ static void pmbus_add_sensor_attrs_one(struct i2c_client *client,
                                                   index, page, cbase, attr);
                /*
                 * Add generic alarm attribute only if there are no individual
-                * alarm attributes, and if there is a global alarm bit.
+                * alarm attributes, if there is a global alarm bit, and if
+                * the generic status register for this page is accessible.
                 */
-               if (!have_alarm && attr->gbit)
+               if (!have_alarm && attr->gbit &&
+                   pmbus_check_byte_register(client, page, PMBUS_STATUS_BYTE))
                        pmbus_add_boolean_reg(data, name, "alarm", index,
                                              PB_STATUS_BASE + page,
                                              attr->gbit);
@@ -1008,6 +1103,21 @@ static const struct pmbus_limit_attr vin_limit_attrs[] = {
                .attr = "crit",
                .alarm = "crit_alarm",
                .sbit = PB_VOLTAGE_OV_FAULT,
+       }, {
+               .reg = PMBUS_VIRT_READ_VIN_AVG,
+               .update = true,
+               .attr = "average",
+       }, {
+               .reg = PMBUS_VIRT_READ_VIN_MIN,
+               .update = true,
+               .attr = "lowest",
+       }, {
+               .reg = PMBUS_VIRT_READ_VIN_MAX,
+               .update = true,
+               .attr = "highest",
+       }, {
+               .reg = PMBUS_VIRT_RESET_VIN_HISTORY,
+               .attr = "reset_history",
        },
 };
 
@@ -1032,6 +1142,21 @@ static const struct pmbus_limit_attr vout_limit_attrs[] = {
                .attr = "crit",
                .alarm = "crit_alarm",
                .sbit = PB_VOLTAGE_OV_FAULT,
+       }, {
+               .reg = PMBUS_VIRT_READ_VOUT_AVG,
+               .update = true,
+               .attr = "average",
+       }, {
+               .reg = PMBUS_VIRT_READ_VOUT_MIN,
+               .update = true,
+               .attr = "lowest",
+       }, {
+               .reg = PMBUS_VIRT_READ_VOUT_MAX,
+               .update = true,
+               .attr = "highest",
+       }, {
+               .reg = PMBUS_VIRT_RESET_VOUT_HISTORY,
+               .attr = "reset_history",
        }
 };
 
@@ -1078,6 +1203,21 @@ static const struct pmbus_limit_attr iin_limit_attrs[] = {
                .attr = "crit",
                .alarm = "crit_alarm",
                .sbit = PB_IIN_OC_FAULT,
+       }, {
+               .reg = PMBUS_VIRT_READ_IIN_AVG,
+               .update = true,
+               .attr = "average",
+       }, {
+               .reg = PMBUS_VIRT_READ_IIN_MIN,
+               .update = true,
+               .attr = "lowest",
+       }, {
+               .reg = PMBUS_VIRT_READ_IIN_MAX,
+               .update = true,
+               .attr = "highest",
+       }, {
+               .reg = PMBUS_VIRT_RESET_IIN_HISTORY,
+               .attr = "reset_history",
        }
 };
 
@@ -1097,6 +1237,21 @@ static const struct pmbus_limit_attr iout_limit_attrs[] = {
                .attr = "crit",
                .alarm = "crit_alarm",
                .sbit = PB_IOUT_OC_FAULT,
+       }, {
+               .reg = PMBUS_VIRT_READ_IOUT_AVG,
+               .update = true,
+               .attr = "average",
+       }, {
+               .reg = PMBUS_VIRT_READ_IOUT_MIN,
+               .update = true,
+               .attr = "lowest",
+       }, {
+               .reg = PMBUS_VIRT_READ_IOUT_MAX,
+               .update = true,
+               .attr = "highest",
+       }, {
+               .reg = PMBUS_VIRT_RESET_IOUT_HISTORY,
+               .attr = "reset_history",
        }
 };
 
@@ -1132,6 +1287,17 @@ static const struct pmbus_limit_attr pin_limit_attrs[] = {
                .attr = "max",
                .alarm = "alarm",
                .sbit = PB_PIN_OP_WARNING,
+       }, {
+               .reg = PMBUS_VIRT_READ_PIN_AVG,
+               .update = true,
+               .attr = "average",
+       }, {
+               .reg = PMBUS_VIRT_READ_PIN_MAX,
+               .update = true,
+               .attr = "input_highest",
+       }, {
+               .reg = PMBUS_VIRT_RESET_PIN_HISTORY,
+               .attr = "reset_history",
        }
 };
 
@@ -1180,6 +1346,39 @@ static const struct pmbus_sensor_attr power_attributes[] = {
 /* Temperature atributes */
 
 static const struct pmbus_limit_attr temp_limit_attrs[] = {
+       {
+               .reg = PMBUS_UT_WARN_LIMIT,
+               .attr = "min",
+               .alarm = "min_alarm",
+               .sbit = PB_TEMP_UT_WARNING,
+       }, {
+               .reg = PMBUS_UT_FAULT_LIMIT,
+               .attr = "lcrit",
+               .alarm = "lcrit_alarm",
+               .sbit = PB_TEMP_UT_FAULT,
+       }, {
+               .reg = PMBUS_OT_WARN_LIMIT,
+               .attr = "max",
+               .alarm = "max_alarm",
+               .sbit = PB_TEMP_OT_WARNING,
+       }, {
+               .reg = PMBUS_OT_FAULT_LIMIT,
+               .attr = "crit",
+               .alarm = "crit_alarm",
+               .sbit = PB_TEMP_OT_FAULT,
+       }, {
+               .reg = PMBUS_VIRT_READ_TEMP_MIN,
+               .attr = "lowest",
+       }, {
+               .reg = PMBUS_VIRT_READ_TEMP_MAX,
+               .attr = "highest",
+       }, {
+               .reg = PMBUS_VIRT_RESET_TEMP_HISTORY,
+               .attr = "reset_history",
+       }
+};
+
+static const struct pmbus_limit_attr temp_limit_attrs23[] = {
        {
                .reg = PMBUS_UT_WARN_LIMIT,
                .attr = "min",
@@ -1226,8 +1425,8 @@ static const struct pmbus_sensor_attr temp_attributes[] = {
                .sfunc = PMBUS_HAVE_STATUS_TEMP,
                .sbase = PB_STATUS_TEMP_BASE,
                .gbit = PB_STATUS_TEMPERATURE,
-               .limit = temp_limit_attrs,
-               .nlimit = ARRAY_SIZE(temp_limit_attrs),
+               .limit = temp_limit_attrs23,
+               .nlimit = ARRAY_SIZE(temp_limit_attrs23),
        }, {
                .reg = PMBUS_READ_TEMPERATURE_3,
                .class = PSC_TEMPERATURE,
@@ -1238,8 +1437,8 @@ static const struct pmbus_sensor_attr temp_attributes[] = {
                .sfunc = PMBUS_HAVE_STATUS_TEMP,
                .sbase = PB_STATUS_TEMP_BASE,
                .gbit = PB_STATUS_TEMPERATURE,
-               .limit = temp_limit_attrs,
-               .nlimit = ARRAY_SIZE(temp_limit_attrs),
+               .limit = temp_limit_attrs23,
+               .nlimit = ARRAY_SIZE(temp_limit_attrs23),
        }
 };
 
@@ -1380,7 +1579,7 @@ static int pmbus_identify_common(struct i2c_client *client,
                 */
                switch (vout_mode >> 5) {
                case 0: /* linear mode      */
-                       if (data->info->direct[PSC_VOLTAGE_OUT])
+                       if (data->info->format[PSC_VOLTAGE_OUT] != linear)
                                return -ENODEV;
 
                        exponent = vout_mode & 0x1f;
@@ -1389,8 +1588,12 @@ static int pmbus_identify_common(struct i2c_client *client,
                                exponent |= ~0x1f;
                        data->exponent = exponent;
                        break;
+               case 1: /* VID mode         */
+                       if (data->info->format[PSC_VOLTAGE_OUT] != vid)
+                               return -ENODEV;
+                       break;
                case 2: /* direct mode      */
-                       if (!data->info->direct[PSC_VOLTAGE_OUT])
+                       if (data->info->format[PSC_VOLTAGE_OUT] != direct)
                                return -ENODEV;
                        break;
                default:
@@ -1457,18 +1660,6 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
                ret = -EINVAL;
                goto out_data;
        }
-       /*
-        * Bail out if more than one page was configured, but we can not
-        * select the highest page. This is an indication that the wrong
-        * chip type was selected. Better bail out now than keep
-        * returning errors later on.
-        */
-       if (info->pages > 1 && pmbus_set_page(client, info->pages - 1) < 0) {
-               dev_err(&client->dev, "Failed to select page %d\n",
-                       info->pages - 1);
-               ret = -EINVAL;
-               goto out_data;
-       }
 
        ret = pmbus_identify_common(client, data);
        if (ret < 0) {
diff --git a/include/linux/platform_data/ntc_thermistor.h b/include/linux/platform_data/ntc_thermistor.h
new file mode 100644 (file)
index 0000000..abd2862
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * ntc_thermistor.h - NTC Thermistors
+ *
+ *  Copyright (C) 2010 Samsung Electronics
+ *  MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef _LINUX_NTC_H
+#define _LINUX_NTC_H
+
+enum ntc_thermistor_type {
+       TYPE_NCPXXWB473,
+       TYPE_NCPXXWL333,
+};
+
+struct ntc_thermistor_platform_data {
+       /*
+        * One (not both) of read_uV and read_ohm should be provided and only
+        * one of the two should be provided.
+        * Both functions should return negative value for an error case.
+        *
+        * pullup_uV, pullup_ohm, pulldown_ohm, and connect are required to use
+        * read_uV()
+        *
+        * How to setup pullup_ohm, pulldown_ohm, and connect is
+        * described at Documentation/hwmon/ntc
+        *
+        * pullup/down_ohm: 0 for infinite / not-connected
+        */
+       int (*read_uV)(void);
+       unsigned int pullup_uV;
+
+       unsigned int pullup_ohm;
+       unsigned int pulldown_ohm;
+       enum { NTC_CONNECTED_POSITIVE, NTC_CONNECTED_GROUND } connect;
+
+       int (*read_ohm)(void);
+};
+
+#endif /* _LINUX_NTC_H */