Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/staging
* '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 ...
This commit is contained in:
commit
464c9098bb
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
|
@ -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
|
||||
|
|
|
@ -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.
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
|
@ -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: -
|
||||
|
|
|
@ -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.
|
||||
|
||||
*********
|
||||
|
|
18
MAINTAINERS
18
MAINTAINERS
|
@ -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
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
@ -74,15 +79,18 @@
|
|||
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]);
|
||||
|
|
|
@ -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.
|
||||
|
@ -96,13 +99,15 @@
|
|||
* 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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
|
@ -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)
|
|
@ -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");
|
|
@ -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
|
|
@ -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
|
|
@ -23,11 +23,68 @@
|
|||
#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;
|
|
@ -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);
|
|
@ -25,11 +25,60 @@
|
|||
#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,
|
|
@ -27,11 +27,70 @@
|
|||
|
||||
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,
|
||||
},
|
||||
};
|
||||
|
|
@ -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)
|
||||
|
@ -37,6 +40,62 @@
|
|||
#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,
|
|
@ -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},
|
||||
{}
|
||||
};
|
|
@ -125,6 +125,42 @@
|
|||
#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);
|
|
@ -33,14 +33,18 @@
|
|||
/*
|
||||
* 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;
|
||||
|
@ -74,11 +78,13 @@
|
|||
#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) {
|
|
@ -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 */
|
Loading…
Reference in New Issue