Commit 6fad2b5b authored by Linus Torvalds's avatar Linus Torvalds

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:
  hwmon: (coretemp) Fix checkpatch errors
  hwmon: Remove pkgtemp driver
  hwmon: (coretemp) Merge pkgtemp with coretemp
  hwmon: (pmbus) Add support for Analog Devices ADM1275
  hwmon: (pmbus) Support for TI UCD90xxx series Sequencer and System Health Controllers
  hwmon: (pmbus) Add support for TI UCD9200 series of PWM System Controllers
  hwmon: (pmbus) Use device specific function to read fan configuration
  hwmon: (pmbus) Expand scope of device specific get_status function
  hwmon: (pmbus) Introduce infrastructure to detect sensors and limit registers
  hwmon: Driver for MAX16065 System Manager and compatibles
  hwmon: (sht15) add support for CRC validation
  hwmon: (sht15) add support for the status register
  hwmon: (sht15) clean-up the probe function
  hwmon: (sht15) general code clean-up
  hwmon: Add support for MAX6642
parents 19504828 4cc45275
Kernel driver adm1275
=====================
Supported chips:
* Analog Devices ADM1275
Prefix: 'adm1275'
Addresses scanned: -
Datasheet: www.analog.com/static/imported-files/data_sheets/ADM1275.pdf
Author: Guenter Roeck <guenter.roeck@ericsson.com>
Description
-----------
This driver supports hardware montoring for Analog Devices ADM1275 Hot-Swap
Controller and Digital Power Monitor.
The ADM1275 is a hot-swap controller that allows a circuit board to be removed
from or inserted into a live backplane. It also features current and voltage
readback via an integrated 12-bit analog-to-digital converter (ADC), accessed
using a PMBus. interface.
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. Please see
Documentation/hwmon/pmbus for details.
Sysfs entries
-------------
The following attributes are supported. Limits are read-write; all other
attributes are read-only.
in1_label "vin1" or "vout1" depending on chip variant and
configuration.
in1_input Measured voltage. From READ_VOUT register.
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.
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.
...@@ -15,8 +15,13 @@ Author: Rudolf Marek ...@@ -15,8 +15,13 @@ Author: Rudolf Marek
Description Description
----------- -----------
This driver permits reading the DTS (Digital Temperature Sensor) embedded
inside Intel CPUs. This driver can read both the per-core and per-package
temperature using the appropriate sensors. The per-package sensor is new;
as of now, it is present only in the SandyBridge platform. The driver will
show the temperature of all cores inside a package under a single device
directory inside hwmon.
This driver permits reading temperature sensor embedded inside Intel Core CPU.
Temperature is measured in degrees Celsius and measurement resolution is Temperature is measured in degrees Celsius and measurement resolution is
1 degree C. Valid temperatures are from 0 to TjMax degrees C, because 1 degree C. Valid temperatures are from 0 to TjMax degrees C, because
the actual value of temperature register is in fact a delta from TjMax. the actual value of temperature register is in fact a delta from TjMax.
...@@ -27,13 +32,15 @@ mechanism will perform actions to forcibly cool down the processor. Alarm ...@@ -27,13 +32,15 @@ mechanism will perform actions to forcibly cool down the processor. Alarm
may be raised, if the temperature grows enough (more than TjMax) to trigger may be raised, if the temperature grows enough (more than TjMax) to trigger
the Out-Of-Spec bit. Following table summarizes the exported sysfs files: the Out-Of-Spec bit. Following table summarizes the exported sysfs files:
temp1_input - Core temperature (in millidegrees Celsius). All Sysfs entries are named with their core_id (represented here by 'X').
temp1_max - All cooling devices should be turned on (on Core2). tempX_input - Core temperature (in millidegrees Celsius).
temp1_crit - Maximum junction temperature (in millidegrees Celsius). tempX_max - All cooling devices should be turned on (on Core2).
temp1_crit_alarm - Set when Out-of-spec bit is set, never clears. 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. Correct CPU operation is no longer guaranteed.
temp1_label - Contains string "Core X", where X is processor tempX_label - Contains string "Core X", where X is processor
number. number. For Package temp, this will be "Physical id Y",
where Y is the package number.
The TjMax temperature is set to 85 degrees C if undocumented model specific The TjMax temperature is set to 85 degrees C if undocumented model specific
register (UMSR) 0xee has bit 30 set. If not the TjMax is 100 degrees C as register (UMSR) 0xee has bit 30 set. If not the TjMax is 100 degrees C as
......
Kernel driver max16065
======================
Supported chips:
* Maxim MAX16065, MAX16066
Prefixes: 'max16065', 'max16066'
Addresses scanned: -
Datasheet:
http://datasheets.maxim-ic.com/en/ds/MAX16065-MAX16066.pdf
* Maxim MAX16067
Prefix: 'max16067'
Addresses scanned: -
Datasheet:
http://datasheets.maxim-ic.com/en/ds/MAX16067.pdf
* Maxim MAX16068
Prefix: 'max16068'
Addresses scanned: -
Datasheet:
http://datasheets.maxim-ic.com/en/ds/MAX16068.pdf
* Maxim MAX16070/MAX16071
Prefixes: 'max16070', 'max16071'
Addresses scanned: -
Datasheet:
http://datasheets.maxim-ic.com/en/ds/MAX16070-MAX16071.pdf
Author: Guenter Roeck <guenter.roeck@ericsson.com>
Description
-----------
[From datasheets] The MAX16065/MAX16066 flash-configurable system managers
monitor and sequence multiple system voltages. The MAX16065/MAX16066 can also
accurately monitor (+/-2.5%) one current channel using a dedicated high-side
current-sense amplifier. The MAX16065 manages up to twelve system voltages
simultaneously, and the MAX16066 manages up to eight supply voltages.
The MAX16067 flash-configurable system manager monitors and sequences multiple
system voltages. The MAX16067 manages up to six system voltages simultaneously.
The MAX16068 flash-configurable system manager monitors and manages up to six
system voltages simultaneously.
The MAX16070/MAX16071 flash-configurable system monitors supervise multiple
system voltages. The MAX16070/MAX16071 can also accurately monitor (+/-2.5%)
one current channel using a dedicated high-side current-sense amplifier. The
MAX16070 monitors up to twelve system voltages simultaneously, and the MAX16071
monitors up to eight supply voltages.
Each monitored channel has its own low and high critical limits. MAX16065,
MAX16066, MAX16070, and MAX16071 support an additional limit which is
configurable as either low or high secondary limit. MAX16065, MAX16066,
MAX16070, and MAX16071 also support supply current monitoring.
Usage Notes
-----------
This driver does not probe for devices, since there is no register which
can be safely used to identify the chip. You will have to instantiate
the devices explicitly. Please see Documentation/i2c/instantiating-devices for
details.
Sysfs entries
-------------
in[0-11]_input Input voltage measurements.
in12_input Voltage on CSP (Current Sense Positive) pin.
Only if the chip supports current sensing and if
current sensing is enabled.
in[0-11]_min Low warning limit.
Supported on MAX16065, MAX16066, MAX16070, and MAX16071
only.
in[0-11]_max High warning limit.
Supported on MAX16065, MAX16066, MAX16070, and MAX16071
only.
Either low or high warning limits are supported
(depending on chip configuration), but not both.
in[0-11]_lcrit Low critical limit.
in[0-11]_crit High critical limit.
in[0-11]_alarm Input voltage alarm.
curr1_input Current sense input; only if the chip supports current
sensing and if current sensing is enabled.
Displayed current assumes 0.001 Ohm current sense
resistor.
curr1_alarm Overcurrent alarm; only if the chip supports current
sensing and if current sensing is enabled.
Kernel driver max6642
=====================
Supported chips:
* Maxim MAX6642
Prefix: 'max6642'
Addresses scanned: I2C 0x48-0x4f
Datasheet: Publicly available at the Maxim website
http://datasheets.maxim-ic.com/en/ds/MAX6642.pdf
Authors:
Per Dalen <per.dalen@appeartv.com>
Description
-----------
The MAX6642 is a digital temperature sensor. It senses its own temperature as
well as the temperature on one external diode.
All temperature values are given in degrees Celsius. Resolution
is 0.25 degree for the local temperature and for the remote temperature.
Kernel driver pkgtemp
======================
Supported chips:
* Intel family
Prefix: 'pkgtemp'
CPUID:
Datasheet: Intel 64 and IA-32 Architectures Software Developer's Manual
Volume 3A: System Programming Guide
Author: Fenghua Yu
Description
-----------
This driver permits reading package level temperature sensor embedded inside
Intel CPU package. The sensors can be in core, uncore, memory controller, or
other components in a package. The feature is first implemented in Intel Sandy
Bridge platform.
Temperature is measured in degrees Celsius and measurement resolution is
1 degree C. Valid temperatures are from 0 to TjMax degrees C, because the actual
value of temperature register is in fact a delta from TjMax.
Temperature known as TjMax is the maximum junction temperature of package.
We get this from MSR_IA32_TEMPERATURE_TARGET. If the MSR is not accessible,
we define TjMax as 100 degrees Celsius. At this temperature, protection
mechanism will perform actions to forcibly cool down the package. Alarm
may be raised, if the temperature grows enough (more than TjMax) to trigger
the Out-Of-Spec bit. Following table summarizes the exported sysfs files:
temp1_input - Package temperature (in millidegrees Celsius).
temp1_max - All cooling devices should be turned on.
temp1_crit - Maximum junction temperature (in millidegrees Celsius).
temp1_crit_alarm - Set when Out-of-spec bit is set, never clears.
Correct CPU operation is no longer guaranteed.
Kernel driver sht15
===================
Authors:
* Wouter Horre
* Jonathan Cameron
* Vivien Didelot <vivien.didelot@savoirfairelinux.com>
* Jerome Oufella <jerome.oufella@savoirfairelinux.com>
Supported chips:
* Sensirion SHT10
Prefix: 'sht10'
* Sensirion SHT11
Prefix: 'sht11'
* Sensirion SHT15
Prefix: 'sht15'
* Sensirion SHT71
Prefix: 'sht71'
* Sensirion SHT75
Prefix: 'sht75'
Datasheet: Publicly available at the Sensirion website
http://www.sensirion.ch/en/pdf/product_information/Datasheet-humidity-sensor-SHT1x.pdf
Description
-----------
The SHT10, SHT11, SHT15, SHT71, and SHT75 are humidity and temperature
sensors.
The devices communicate using two GPIO lines.
Supported resolutions for the measurements are 14 bits for temperature and 12
bits for humidity, or 12 bits for temperature and 8 bits for humidity.
The humidity calibration coefficients are programmed into an OTP memory on the
chip. These coefficients are used to internally calibrate the signals from the
sensors. Disabling the reload of those coefficients allows saving 10ms for each
measurement and decrease power consumption, while loosing on precision.
Some options may be set directly in the sht15_platform_data structure
or via sysfs attributes.
Notes:
* The regulator supply name is set to "vcc".
* If a CRC validation fails, a soft reset command is sent, which resets
status register to its hardware default value, but the driver will try to
restore the previous device configuration.
Platform data
-------------
* checksum:
set it to true to enable CRC validation of the readings (default to false).
* no_otp_reload:
flag to indicate not to reload from OTP (default to false).
* low_resolution:
flag to indicate the temp/humidity resolution to use (default to false).
Sysfs interface
---------------
* temp1_input: temperature input
* humidity1_input: humidity input
* heater_enable: write 1 in this attribute to enable the on-chip heater,
0 to disable it. Be careful not to enable the heater
for too long.
* temp1_fault: if 1, this means that the voltage is low (below 2.47V) and
measurement may be invalid.
* humidity1_fault: same as temp1_fault.
Kernel driver ucd9000
=====================
Supported chips:
* TI UCD90120, UCD90124, UCD9090, and UCD90910
Prefixes: 'ucd90120', 'ucd90124', 'ucd9090', 'ucd90910'
Addresses scanned: -
Datasheets:
http://focus.ti.com/lit/ds/symlink/ucd90120.pdf
http://focus.ti.com/lit/ds/symlink/ucd90124.pdf
http://focus.ti.com/lit/ds/symlink/ucd9090.pdf
http://focus.ti.com/lit/ds/symlink/ucd90910.pdf
Author: Guenter Roeck <guenter.roeck@ericsson.com>
Description
-----------
From datasheets:
The UCD90120 Power Supply Sequencer and System Health Monitor monitors and
sequences up to 12 independent voltage rails. The device integrates a 12-bit
ADC with a 2.5V internal reference for monitoring up to 13 power supply voltage,
current, or temperature inputs.
The UCD90124 is a 12-rail PMBus/I2C addressable power-supply sequencer and
system-health monitor. The device integrates a 12-bit ADC for monitoring up to
13 power-supply voltage, current, or temperature inputs. Twenty-six GPIO pins
can be used for power supply enables, power-on reset signals, external
interrupts, cascading, or other system functions. Twelve of these pins offer PWM
functionality. Using these pins, the UCD90124 offers support for fan control,
margining, and general-purpose PWM functions.
The UCD9090 is a 10-rail PMBus/I2C addressable power-supply sequencer and
monitor. The device integrates a 12-bit ADC for monitoring up to 10 power-supply
voltage inputs. Twenty-three GPIO pins can be used for power supply enables,
power-on reset signals, external interrupts, cascading, or other system
functions. Ten of these pins offer PWM functionality. Using these pins, the
UCD9090 offers support for margining, and general-purpose PWM functions.
The UCD90910 is a ten-rail I2C / PMBus addressable power-supply sequencer and
system-health monitor. The device integrates a 12-bit ADC for monitoring up to
13 power-supply voltage, current, or temperature inputs.
This 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. Please see
Documentation/hwmon/pmbus for details.
Sysfs entries
-------------
The following attributes are supported. Limits are read-write; all other
attributes are read-only.
in[1-12]_label "vout[1-12]".
in[1-12]_input Measured voltage. From READ_VOUT register.
in[1-12]_min Minumum Voltage. From VOUT_UV_WARN_LIMIT register.
in[1-12]_max Maximum voltage. From VOUT_OV_WARN_LIMIT register.
in[1-12]_lcrit Critical minumum Voltage. VOUT_UV_FAULT_LIMIT register.
in[1-12]_crit Critical maximum voltage. From VOUT_OV_FAULT_LIMIT register.
in[1-12]_min_alarm Voltage low alarm. From VOLTAGE_UV_WARNING status.
in[1-12]_max_alarm Voltage high alarm. From VOLTAGE_OV_WARNING status.
in[1-12]_lcrit_alarm Voltage critical low alarm. From VOLTAGE_UV_FAULT status.
in[1-12]_crit_alarm Voltage critical high alarm. From VOLTAGE_OV_FAULT status.
curr[1-12]_label "iout[1-12]".
curr[1-12]_input Measured current. From READ_IOUT register.
curr[1-12]_max Maximum current. From IOUT_OC_WARN_LIMIT register.
curr[1-12]_lcrit Critical minumum output current. From IOUT_UC_FAULT_LIMIT
register.
curr[1-12]_crit Critical maximum current. From IOUT_OC_FAULT_LIMIT register.
curr[1-12]_max_alarm Current high alarm. From IOUT_OC_WARNING status.
curr[1-12]_crit_alarm Current critical high alarm. From IOUT_OC_FAULT status.
For each attribute index, either voltage or current is
reported, but not both. If voltage or current is
reported depends on the chip configuration.
temp[1-2]_input Measured temperatures. From READ_TEMPERATURE_1 and
READ_TEMPERATURE_2 registers.
temp[1-2]_max Maximum temperature. From OT_WARN_LIMIT register.
temp[1-2]_crit Critical high temperature. From OT_FAULT_LIMIT register.
temp[1-2]_max_alarm Temperature high alarm.
temp[1-2]_crit_alarm Temperature critical high alarm.
fan[1-4]_input Fan RPM.
fan[1-4]_alarm Fan alarm.
fan[1-4]_fault Fan fault.
Fan attributes are only available on chips supporting
fan control (UCD90124, UCD90910). Attribute files are
created only for enabled fans.
Note that even though UCD90910 supports up to 10 fans,
only up to four fans are currently supported.
Kernel driver ucd9200
=====================
Supported chips:
* TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and UCD9248
Prefixes: 'ucd9220', 'ucd9222', 'ucd9224', 'ucd9240', 'ucd9244', 'ucd9246',
'ucd9248'
Addresses scanned: -
Datasheets:
http://focus.ti.com/lit/ds/symlink/ucd9220.pdf
http://focus.ti.com/lit/ds/symlink/ucd9222.pdf
http://focus.ti.com/lit/ds/symlink/ucd9224.pdf
http://focus.ti.com/lit/ds/symlink/ucd9240.pdf
http://focus.ti.com/lit/ds/symlink/ucd9244.pdf
http://focus.ti.com/lit/ds/symlink/ucd9246.pdf
http://focus.ti.com/lit/ds/symlink/ucd9248.pdf
Author: Guenter Roeck <guenter.roeck@ericsson.com>
Description
-----------
[From datasheets] UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and
UCD9248 are multi-rail, multi-phase synchronous buck digital PWM controllers
designed for non-isolated DC/DC power applications. The devices integrate
dedicated circuitry for DC/DC loop management with flash memory and a serial
interface to support configuration, monitoring and management.
This 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. Please see
Documentation/hwmon/pmbus for details.
Sysfs entries
-------------
The following attributes are supported. Limits are read-write; all other
attributes are read-only.
in1_label "vin".
in1_input Measured voltage. From READ_VIN register.
in1_min Minumum Voltage. From VIN_UV_WARN_LIMIT register.
in1_max Maximum voltage. From VIN_OV_WARN_LIMIT register.
in1_lcrit Critical minumum Voltage. VIN_UV_FAULT_LIMIT register.
in1_crit Critical maximum voltage. From VIN_OV_FAULT_LIMIT register.
in1_min_alarm Voltage low alarm. From VIN_UV_WARNING status.
in1_max_alarm Voltage high alarm. From VIN_OV_WARNING status.
in1_lcrit_alarm Voltage critical low alarm. From VIN_UV_FAULT status.
in1_crit_alarm Voltage critical high alarm. From VIN_OV_FAULT status.
in[2-5]_label "vout[1-4]".
in[2-5]_input Measured voltage. From READ_VOUT register.
in[2-5]_min Minumum Voltage. From VOUT_UV_WARN_LIMIT register.
in[2-5]_max Maximum voltage. From VOUT_OV_WARN_LIMIT register.
in[2-5]_lcrit Critical minumum Voltage. VOUT_UV_FAULT_LIMIT register.
in[2-5]_crit Critical maximum voltage. From VOUT_OV_FAULT_LIMIT register.
in[2-5]_min_alarm Voltage low alarm. From VOLTAGE_UV_WARNING status.
in[2-5]_max_alarm Voltage high alarm. From VOLTAGE_OV_WARNING status.
in[2-5]_lcrit_alarm Voltage critical low alarm. From VOLTAGE_UV_FAULT status.
in[2-5]_crit_alarm Voltage critical high alarm. From VOLTAGE_OV_FAULT status.
curr1_label "iin".
curr1_input Measured current. From READ_IIN register.
curr[2-5]_label "iout[1-4]".
curr[2-5]_input Measured current. From READ_IOUT register.
curr[2-5]_max Maximum current. From IOUT_OC_WARN_LIMIT register.
curr[2-5]_lcrit Critical minumum output current. From IOUT_UC_FAULT_LIMIT
register.
curr[2-5]_crit Critical maximum current. From IOUT_OC_FAULT_LIMIT register.
curr[2-5]_max_alarm Current high alarm. From IOUT_OC_WARNING status.
curr[2-5]_crit_alarm Current critical high alarm. From IOUT_OC_FAULT status.
power1_input Measured input power. From READ_PIN register.
power1_label "pin"
power[2-5]_input Measured output power. From READ_POUT register.
power[2-5]_label "pout[1-4]"
The number of output voltage, current, and power
attribute sets is determined by the number of enabled
rails. See chip datasheets for details.
temp[1-5]_input Measured temperatures. From READ_TEMPERATURE_1 and
READ_TEMPERATURE_2 registers.
temp1 is the chip internal temperature. temp[2-5] are
rail temperatures. temp[2-5] attributes are only
created for enabled rails. See chip datasheets for
details.
temp[1-5]_max Maximum temperature. From OT_WARN_LIMIT register.
temp[1-5]_crit Critical high temperature. From OT_FAULT_LIMIT register.
temp[1-5]_max_alarm Temperature high alarm.
temp[1-5]_crit_alarm Temperature critical high alarm.
fan1_input Fan RPM. ucd9240 only.
fan1_alarm Fan alarm. ucd9240 only.
fan1_fault Fan fault. ucd9240 only.
...@@ -408,13 +408,6 @@ config SENSORS_CORETEMP ...@@ -408,13 +408,6 @@ config SENSORS_CORETEMP
sensor inside your CPU. Most of the family 6 CPUs sensor inside your CPU. Most of the family 6 CPUs
are supported. Check Documentation/hwmon/coretemp for details. are supported. Check Documentation/hwmon/coretemp for details.
config SENSORS_PKGTEMP
tristate "Intel processor package temperature sensor"
depends on X86 && EXPERIMENTAL
help
If you say yes here you get support for the package level temperature
sensor inside your CPU. Check documentation/driver for details.
config SENSORS_IBMAEM config SENSORS_IBMAEM
tristate "IBM Active Energy Manager temperature/power sensors and control" tristate "IBM Active Energy Manager temperature/power sensors and control"
select IPMI_SI select IPMI_SI
...@@ -708,6 +701,22 @@ config SENSORS_MAX1111 ...@@ -708,6 +701,22 @@ config SENSORS_MAX1111
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called max1111. will be called max1111.
config SENSORS_MAX16065
tristate "Maxim MAX16065 System Manager and compatibles"
depends on I2C
help
If you say yes here you get support for hardware monitoring
capabilities of the following Maxim System Manager chips.
MAX16065
MAX16066
MAX16067
MAX16068
MAX16070
MAX16071
This driver can also be built as a module. If so, the module
will be called max16065.
config SENSORS_MAX1619 config SENSORS_MAX1619
tristate "Maxim MAX1619 sensor chip" tristate "Maxim MAX1619 sensor chip"
depends on I2C depends on I2C
...@@ -727,6 +736,17 @@ config SENSORS_MAX6639 ...@@ -727,6 +736,17 @@ config SENSORS_MAX6639
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called max6639. will be called max6639.
config SENSORS_MAX6642
tristate "Maxim MAX6642 sensor chip"
depends on I2C && EXPERIMENTAL
help
If you say yes here you get support for MAX6642 sensor chip.
MAX6642 is a SMBus-Compatible Remote/Local Temperature Sensor
with Overtemperature Alarm from Maxim.
This driver can also be built as a module. If so, the module
will be called max6642.
config SENSORS_MAX6650 config SENSORS_MAX6650
tristate "Maxim MAX6650 sensor chip" tristate "Maxim MAX6650 sensor chip"
depends on I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
...@@ -800,6 +820,16 @@ config SENSORS_PMBUS ...@@ -800,6 +820,16 @@ config SENSORS_PMBUS
This driver can also be built as a module. If so, the module will This driver can also be built as a module. If so, the module will
be called pmbus. 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 config SENSORS_MAX16064
tristate "Maxim MAX16064" tristate "Maxim MAX16064"
default n default n
...@@ -830,6 +860,28 @@ config SENSORS_MAX8688 ...@@ -830,6 +860,28 @@ config SENSORS_MAX8688
This driver can also be built as a module. If so, the module will This driver can also be built as a module. If so, the module will
be called max8688. 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 endif # PMBUS
config SENSORS_SHT15 config SENSORS_SHT15
......
...@@ -40,7 +40,6 @@ obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o ...@@ -40,7 +40,6 @@ obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o
obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
obj-$(CONFIG_SENSORS_PKGTEMP) += pkgtemp.o
obj-$(CONFIG_SENSORS_DME1737) += dme1737.o obj-$(CONFIG_SENSORS_DME1737) += dme1737.o
obj-$(CONFIG_SENSORS_DS620) += ds620.o obj-$(CONFIG_SENSORS_DS620) += ds620.o
obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
...@@ -83,8 +82,10 @@ obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o ...@@ -83,8 +82,10 @@ obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o
obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o
obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o
obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
obj-$(CONFIG_SENSORS_MAX16065) += max16065.o
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
obj-$(CONFIG_SENSORS_MAX6639) += max6639.o obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
...@@ -118,9 +119,12 @@ obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o ...@@ -118,9 +119,12 @@ obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
# PMBus drivers # PMBus drivers
obj-$(CONFIG_PMBUS) += pmbus_core.o obj-$(CONFIG_PMBUS) += pmbus_core.o
obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o
obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
obj-$(CONFIG_SENSORS_MAX16064) += max16064.o obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG
/*
* Hardware monitoring driver for Analog Devices ADM1275 Hot-Swap Controller
* and Digital Power Monitor
*
* Copyright (c) 2011 Ericsson AB.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include "pmbus.h"
#define ADM1275_PMON_CONFIG 0xd4
#define ADM1275_VIN_VOUT_SELECT (1 << 6)
#define ADM1275_VRANGE (1 << 5)
static int adm1275_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int config;
struct pmbus_driver_info *info;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_BYTE_DATA))
return -ENODEV;
info = kzalloc(sizeof(struct pmbus_driver_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
config = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG);
if (config < 0)
return config;
info->pages = 1;
info->direct[PSC_VOLTAGE_IN] = true;
info->direct[PSC_VOLTAGE_OUT] = true;
info->direct[PSC_CURRENT_OUT] = true;
info->m[PSC_CURRENT_OUT] = 800;
info->b[PSC_CURRENT_OUT] = 20475;
info->R[PSC_CURRENT_OUT] = -1;
info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
if (config & ADM1275_VRANGE) {
info->m[PSC_VOLTAGE_IN] = 19045;
info->b[PSC_VOLTAGE_IN] = 0;
info->R[PSC_VOLTAGE_IN] = -2;
info->m[PSC_VOLTAGE_OUT] = 19045;
info->b[PSC_VOLTAGE_OUT] = 0;
info->R[PSC_VOLTAGE_OUT] = -2;
} else {
info->m[PSC_VOLTAGE_IN] = 6666;
info->b[PSC_VOLTAGE_IN] = 0;
info->R[PSC_VOLTAGE_IN] = -1;
info->m[PSC_VOLTAGE_OUT] = 6666;
info->b[PSC_VOLTAGE_OUT] = 0;
info->R[PSC_VOLTAGE_OUT] = -1;
}
if (config & ADM1275_VIN_VOUT_SELECT)
info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
else
info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT;
return pmbus_do_probe(client, id, info);
}
static int adm1275_remove(struct i2c_client *client)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
int ret;
ret = pmbus_do_remove(client);
kfree(info);
return ret;
}
static const struct i2c_device_id adm1275_id[] = {
{"adm1275", 0},
{ }
};
MODULE_DEVICE_TABLE(i2c, adm1275_id);
static struct i2c_driver adm1275_driver = {
.driver = {
.name = "adm1275",
},
.probe = adm1275_probe,
.remove = adm1275_remove,
.id_table = adm1275_id,
};
static int __init adm1275_init(void)
{
return i2c_add_driver(&adm1275_driver);
}
static void __exit adm1275_exit(void)
{
i2c_del_driver(&adm1275_driver);
}
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275");
MODULE_LICENSE("GPL");
module_init(adm1275_init);
module_exit(adm1275_exit);
...@@ -35,128 +35,152 @@ ...@@ -35,128 +35,152 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/smp.h>
#include <asm/msr.h> #include <asm/msr.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/smp.h>
#define DRVNAME "coretemp" #define DRVNAME "coretemp"
typedef enum { SHOW_TEMP, SHOW_TJMAX, SHOW_TTARGET, SHOW_LABEL, #define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */
SHOW_NAME } SHOW; #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_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO)
#ifdef CONFIG_SMP
#define TO_PHYS_ID(cpu) cpu_data(cpu).phys_proc_id
#define TO_CORE_ID(cpu) cpu_data(cpu).cpu_core_id
#define TO_ATTR_NO(cpu) (TO_CORE_ID(cpu) + BASE_SYSFS_ATTR_NO)
#else
#define TO_PHYS_ID(cpu) (cpu)
#define TO_CORE_ID(cpu) (cpu)
#define TO_ATTR_NO(cpu) (cpu)
#endif
/* /*
* Functions declaration * Per-Core Temperature Data
* @last_updated: The time when the current temperature value was updated
* earlier (in jiffies).
* @cpu_core_id: The CPU Core from which temperature values should be read
* This value is passed as "id" field to rdmsr/wrmsr functions.
* @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS,
* from where the temperature values should be read.
* @is_pkg_data: If this is 1, the temp_data holds pkgtemp data.
* Otherwise, temp_data holds coretemp data.
* @valid: If this is 1, the current temperature is valid.
*/ */
struct temp_data {
static struct coretemp_data *coretemp_update_device(struct device *dev);
struct coretemp_data {
struct device *hwmon_dev;
struct mutex update_lock;
const char *name;
u32 id;
u16 core_id;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
int temp; int temp;
int tjmax;
int ttarget; int ttarget;
u8 alarm; int tjmax;
unsigned long last_updated;
unsigned int cpu;
u32 cpu_core_id;
u32 status_reg;
bool is_pkg_data;
bool valid;
struct sensor_device_attribute sd_attrs[MAX_ATTRS];
char attr_name[MAX_ATTRS][CORETEMP_NAME_LENGTH];
struct mutex update_lock;
}; };
/* /* Platform Data per Physical CPU */
* Sysfs stuff struct platform_data {
*/ struct device *hwmon_dev;
u16 phys_proc_id;
struct temp_data *core_data[MAX_CORE_DATA];
struct device_attribute name_attr;
};
static ssize_t show_name(struct device *dev, struct device_attribute struct pdev_entry {
*devattr, char *buf) struct list_head list;
struct platform_device *pdev;
unsigned int cpu;
u16 phys_proc_id;
u16 cpu_core_id;
};
static LIST_HEAD(pdev_list);
static DEFINE_MUTEX(pdev_list_mutex);
static ssize_t show_name(struct device *dev,
struct device_attribute *devattr, char *buf)
{
return sprintf(buf, "%s\n", DRVNAME);
}
static ssize_t show_label(struct device *dev,
struct device_attribute *devattr, char *buf)
{ {
int ret;
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct coretemp_data *data = dev_get_drvdata(dev); struct platform_data *pdata = dev_get_drvdata(dev);
struct temp_data *tdata = pdata->core_data[attr->index];
if (attr->index == SHOW_NAME) if (tdata->is_pkg_data)
ret = sprintf(buf, "%s\n", data->name); return sprintf(buf, "Physical id %u\n", pdata->phys_proc_id);
else /* show label */
ret = sprintf(buf, "Core %d\n", data->core_id); return sprintf(buf, "Core %u\n", tdata->cpu_core_id);
return ret;
} }
static ssize_t show_alarm(struct device *dev, struct device_attribute static ssize_t show_crit_alarm(struct device *dev,
*devattr, char *buf) struct device_attribute *devattr, char *buf)
{ {
struct coretemp_data *data = coretemp_update_device(dev); u32 eax, edx;
/* read the Out-of-spec log, never clear */ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
return sprintf(buf, "%d\n", data->alarm); struct platform_data *pdata = dev_get_drvdata(dev);
struct temp_data *tdata = pdata->core_data[attr->index];
rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx);
return sprintf(buf, "%d\n", (eax >> 5) & 1);
} }
static ssize_t show_temp(struct device *dev, static ssize_t show_tjmax(struct device *dev,
struct device_attribute *devattr, char *buf) struct device_attribute *devattr, char *buf)
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct coretemp_data *data = coretemp_update_device(dev); struct platform_data *pdata = dev_get_drvdata(dev);
int err;
if (attr->index == SHOW_TEMP) return sprintf(buf, "%d\n", pdata->core_data[attr->index]->tjmax);
err = data->valid ? sprintf(buf, "%d\n", data->temp) : -EAGAIN;
else if (attr->index == SHOW_TJMAX)
err = sprintf(buf, "%d\n", data->tjmax);
else
err = sprintf(buf, "%d\n", data->ttarget);
return err;
} }
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, static ssize_t show_ttarget(struct device *dev,
SHOW_TEMP); struct device_attribute *devattr, char *buf)
static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, NULL, {
SHOW_TJMAX); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, NULL, struct platform_data *pdata = dev_get_drvdata(dev);
SHOW_TTARGET);
static DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL);
static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL);
static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, SHOW_NAME);
static struct attribute *coretemp_attributes[] = {
&sensor_dev_attr_name.dev_attr.attr,
&sensor_dev_attr_temp1_label.dev_attr.attr,
&dev_attr_temp1_crit_alarm.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_crit.dev_attr.attr,
NULL
};
static const struct attribute_group coretemp_group = { return sprintf(buf, "%d\n", pdata->core_data[attr->index]->ttarget);
.attrs = coretemp_attributes, }
};
static struct coretemp_data *coretemp_update_device(struct device *dev) static ssize_t show_temp(struct device *dev,
struct device_attribute *devattr, char *buf)
{ {
struct coretemp_data *data = dev_get_drvdata(dev); u32 eax, edx;
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
mutex_lock(&data->update_lock); struct platform_data *pdata = dev_get_drvdata(dev);
struct temp_data *tdata = pdata->core_data[attr->index];
if (!data->valid || time_after(jiffies, data->last_updated + HZ)) { mutex_lock(&tdata->update_lock);
u32 eax, edx;
data->valid = 0; /* Check whether the time interval has elapsed */
rdmsr_on_cpu(data->id, MSR_IA32_THERM_STATUS, &eax, &edx); if (!tdata->valid || time_after(jiffies, tdata->last_updated + HZ)) {
data->alarm = (eax >> 5) & 1; rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx);
/* update only if data has been valid */ tdata->valid = 0;
/* Check whether the data is valid */
if (eax & 0x80000000) { if (eax & 0x80000000) {
data->temp = data->tjmax - (((eax >> 16) tdata->temp = tdata->tjmax -
& 0x7f) * 1000); ((eax >> 16) & 0x7f) * 1000;
data->valid = 1; tdata->valid = 1;
} else {
dev_dbg(dev, "Temperature data invalid (0x%x)\n", eax);
} }
data->last_updated = jiffies; tdata->last_updated = jiffies;
} }
mutex_unlock(&data->update_lock); mutex_unlock(&tdata->update_lock);
return data; return tdata->valid ? sprintf(buf, "%d\n", tdata->temp) : -EAGAIN;
} }
static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
{ {
/* The 100C is default for both mobile and non mobile CPUs */ /* The 100C is default for both mobile and non mobile CPUs */
...@@ -169,9 +193,8 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device * ...@@ -169,9 +193,8 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *
/* Early chips have no MSR for TjMax */ /* Early chips have no MSR for TjMax */
if ((c->x86_model == 0xf) && (c->x86_mask < 4)) { if (c->x86_model == 0xf && c->x86_mask < 4)
usemsr_ee = 0; usemsr_ee = 0;
}
/* Atom CPUs */ /* Atom CPUs */
...@@ -190,14 +213,14 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device * ...@@ -190,14 +213,14 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *
pci_dev_put(host_bridge); pci_dev_put(host_bridge);
} }
if ((c->x86_model > 0xe) && (usemsr_ee)) { if (c->x86_model > 0xe && usemsr_ee) {
u8 platform_id; u8 platform_id;
/* Now we can detect the mobile CPU using Intel provided table /*
http://softwarecommunity.intel.com/Wiki/Mobility/720.htm * Now we can detect the mobile CPU using Intel provided table
For Core2 cores, check MSR 0x17, bit 28 1 = Mobile CPU * http://softwarecommunity.intel.com/Wiki/Mobility/720.htm
*/ * For Core2 cores, check MSR 0x17, bit 28 1 = Mobile CPU
*/
err = rdmsr_safe_on_cpu(id, 0x17, &eax, &edx); err = rdmsr_safe_on_cpu(id, 0x17, &eax, &edx);
if (err) { if (err) {
dev_warn(dev, dev_warn(dev,
...@@ -205,20 +228,26 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device * ...@@ -205,20 +228,26 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *
" CPU\n"); " CPU\n");
usemsr_ee = 0; usemsr_ee = 0;
} else if (c->x86_model < 0x17 && !(eax & 0x10000000)) { } else if (c->x86_model < 0x17 && !(eax & 0x10000000)) {
/* Trust bit 28 up to Penryn, I could not find any /*
documentation on that; if you happen to know * Trust bit 28 up to Penryn, I could not find any
someone at Intel please ask */ * documentation on that; if you happen to know
* someone at Intel please ask
*/
usemsr_ee = 0; usemsr_ee = 0;
} else { } else {
/* Platform ID bits 52:50 (EDX starts at bit 32) */ /* Platform ID bits 52:50 (EDX starts at bit 32) */
platform_id = (edx >> 18) & 0x7; platform_id = (edx >> 18) & 0x7;
/* Mobile Penryn CPU seems to be platform ID 7 or 5 /*
(guesswork) */ * Mobile Penryn CPU seems to be platform ID 7 or 5
if ((c->x86_model == 0x17) && * (guesswork)
((platform_id == 5) || (platform_id == 7))) { */
/* If MSR EE bit is set, set it to 90 degrees C, if (c->x86_model == 0x17 &&
otherwise 105 degrees C */ (platform_id == 5 || platform_id == 7)) {
/*
* If MSR EE bit is set, set it to 90 degrees C,
* otherwise 105 degrees C
*/
tjmax_ee = 90000; tjmax_ee = 90000;
tjmax = 105000; tjmax = 105000;
} }
...@@ -226,7 +255,6 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device * ...@@ -226,7 +255,6 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *
} }
if (usemsr_ee) { if (usemsr_ee) {
err = rdmsr_safe_on_cpu(id, 0xee, &eax, &edx); err = rdmsr_safe_on_cpu(id, 0xee, &eax, &edx);
if (err) { if (err) {
dev_warn(dev, dev_warn(dev,
...@@ -235,25 +263,28 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device * ...@@ -235,25 +263,28 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *
} else if (eax & 0x40000000) { } else if (eax & 0x40000000) {
tjmax = tjmax_ee; tjmax = tjmax_ee;
} }
/* if we dont use msr EE it means we are desktop CPU (with exeception
of Atom) */
} else if (tjmax == 100000) { } else if (tjmax == 100000) {
/*
* If we don't use msr EE it means we are desktop CPU
* (with exeception of Atom)
*/
dev_warn(dev, "Using relative temperature scale!\n"); dev_warn(dev, "Using relative temperature scale!\n");
} }
return tjmax; return tjmax;
} }
static int __devinit get_tjmax(struct cpuinfo_x86 *c, u32 id, static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
struct device *dev)
{ {
/* The 100C is default for both mobile and non mobile CPUs */ /* The 100C is default for both mobile and non mobile CPUs */
int err; int err;
u32 eax, edx; u32 eax, edx;
u32 val; u32 val;
/* A new feature of current Intel(R) processors, the /*
IA32_TEMPERATURE_TARGET contains the TjMax value */ * A new feature of current Intel(R) processors, the
* IA32_TEMPERATURE_TARGET contains the TjMax value
*/
err = rdmsr_safe_on_cpu(id, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); err = rdmsr_safe_on_cpu(id, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
if (err) { if (err) {
dev_warn(dev, "Unable to read TjMax from CPU.\n"); dev_warn(dev, "Unable to read TjMax from CPU.\n");
...@@ -263,7 +294,7 @@ static int __devinit get_tjmax(struct cpuinfo_x86 *c, u32 id, ...@@ -263,7 +294,7 @@ static int __devinit get_tjmax(struct cpuinfo_x86 *c, u32 id,
* If the TjMax is not plausible, an assumption * If the TjMax is not plausible, an assumption
* will be used * will be used
*/ */
if ((val > 80) && (val < 120)) { if (val > 80 && val < 120) {
dev_info(dev, "TjMax is %d C.\n", val); dev_info(dev, "TjMax is %d C.\n", val);
return val * 1000; return val * 1000;
} }
...@@ -300,115 +331,293 @@ static void __devinit get_ucode_rev_on_cpu(void *edx) ...@@ -300,115 +331,293 @@ static void __devinit get_ucode_rev_on_cpu(void *edx)
rdmsr(MSR_IA32_UCODE_REV, eax, *(u32 *)edx); rdmsr(MSR_IA32_UCODE_REV, eax, *(u32 *)edx);
} }
static int __devinit coretemp_probe(struct platform_device *pdev) static int get_pkg_tjmax(unsigned int cpu, struct device *dev)
{ {
struct coretemp_data *data;
struct cpuinfo_x86 *c = &cpu_data(pdev->id);
int err; int err;
u32 eax, edx; u32 eax, edx, val;
if (!(data = kzalloc(sizeof(struct coretemp_data), GFP_KERNEL))) { err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
err = -ENOMEM; if (!err) {
dev_err(&pdev->dev, "Out of memory\n"); val = (eax >> 16) & 0xff;
goto exit; if (val > 80 && val < 120)
return val * 1000;
} }
dev_warn(dev, "Unable to read Pkg-TjMax from CPU:%u\n", cpu);
return 100000; /* Default TjMax: 100 degree celsius */
}
data->id = pdev->id; static int create_name_attr(struct platform_data *pdata, struct device *dev)
#ifdef CONFIG_SMP {
data->core_id = c->cpu_core_id; pdata->name_attr.attr.name = "name";
#endif pdata->name_attr.attr.mode = S_IRUGO;
data->name = "coretemp"; pdata->name_attr.show = show_name;
mutex_init(&data->update_lock); return device_create_file(dev, &pdata->name_attr);
}
/* test if we can access the THERM_STATUS MSR */ static int create_core_attrs(struct temp_data *tdata, struct device *dev,
err = rdmsr_safe_on_cpu(data->id, MSR_IA32_THERM_STATUS, &eax, &edx); int attr_no)
if (err) { {
dev_err(&pdev->dev, int err, i;
"Unable to access THERM_STATUS MSR, giving up\n"); static ssize_t (*rd_ptr[MAX_ATTRS]) (struct device *dev,
goto exit_free; struct device_attribute *devattr, char *buf) = {
show_label, show_crit_alarm, show_ttarget,
show_temp, show_tjmax };
static const char *names[MAX_ATTRS] = {
"temp%d_label", "temp%d_crit_alarm",
"temp%d_max", "temp%d_input",
"temp%d_crit" };
for (i = 0; i < MAX_ATTRS; i++) {
snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i],
attr_no);
tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i];
tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO;
tdata->sd_attrs[i].dev_attr.show = rd_ptr[i];
tdata->sd_attrs[i].dev_attr.store = NULL;
tdata->sd_attrs[i].index = attr_no;
err = device_create_file(dev, &tdata->sd_attrs[i].dev_attr);
if (err)
goto exit_free;
}
return 0;
exit_free:
while (--i >= 0)
device_remove_file(dev, &tdata->sd_attrs[i].dev_attr);
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;
}
} }
}
/* Check if we have problem with errata AE18 of Core processors: static int chk_ucode_version(struct platform_device *pdev)
Readings might stop update when processor visited too deep sleep, {
fixed for stepping D0 (6EC). struct cpuinfo_x86 *c = &cpu_data(pdev->id);
*/ int err;
u32 edx;
if ((c->x86_model == 0xe) && (c->x86_mask < 0xc)) { /*
* Check if we have problem with errata AE18 of Core processors:
* Readings might stop update when processor visited too deep sleep,
* fixed for stepping D0 (6EC).
*/
if (c->x86_model == 0xe && c->x86_mask < 0xc) {
/* check for microcode update */ /* check for microcode update */
err = smp_call_function_single(data->id, get_ucode_rev_on_cpu, err = smp_call_function_single(pdev->id, get_ucode_rev_on_cpu,
&edx, 1); &edx, 1);
if (err) { if (err) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"Cannot determine microcode revision of " "Cannot determine microcode revision of "
"CPU#%u (%d)!\n", data->id, err); "CPU#%u (%d)!\n", pdev->id, err);
err = -ENODEV; return -ENODEV;
goto exit_free;
} else if (edx < 0x39) { } else if (edx < 0x39) {
err = -ENODEV;
dev_err(&pdev->dev, dev_err(&pdev->dev,
"Errata AE18 not fixed, update BIOS or " "Errata AE18 not fixed, update BIOS or "
"microcode of the CPU!\n"); "microcode of the CPU!\n");
goto exit_free; return -ENODEV;
} }
} }
return 0;
}
data->tjmax = get_tjmax(c, data->id, &pdev->dev); static struct platform_device *coretemp_get_pdev(unsigned int cpu)
platform_set_drvdata(pdev, data); {
u16 phys_proc_id = TO_PHYS_ID(cpu);
struct pdev_entry *p;
mutex_lock(&pdev_list_mutex);
list_for_each_entry(p, &pdev_list, list)
if (p->phys_proc_id == phys_proc_id) {
mutex_unlock(&pdev_list_mutex);
return p->pdev;
}
mutex_unlock(&pdev_list_mutex);
return NULL;
}
static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag)
{
struct temp_data *tdata;
tdata = kzalloc(sizeof(struct temp_data), GFP_KERNEL);
if (!tdata)
return NULL;
tdata->status_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_STATUS :
MSR_IA32_THERM_STATUS;
tdata->is_pkg_data = pkg_flag;
tdata->cpu = cpu;
tdata->cpu_core_id = TO_CORE_ID(cpu);
mutex_init(&tdata->update_lock);
return tdata;
}
static int create_core_data(struct platform_data *pdata,
struct platform_device *pdev,
unsigned int cpu, int pkg_flag)
{
struct temp_data *tdata;
struct cpuinfo_x86 *c = &cpu_data(cpu);
u32 eax, edx;
int err, attr_no;
/* /*
* read the still undocumented IA32_TEMPERATURE_TARGET. It exists * Find attr number for sysfs:
* on older CPUs but not in this register, * We map the attr number to core id of the CPU
* Atoms don't have it either. * The attr number is always core id + 2
* The Pkgtemp will always show up as temp1_*, if available
*/ */
attr_no = pkg_flag ? 1 : TO_ATTR_NO(cpu);
if ((c->x86_model > 0xe) && (c->x86_model != 0x1c)) { if (attr_no > MAX_CORE_DATA - 1)
err = rdmsr_safe_on_cpu(data->id, MSR_IA32_TEMPERATURE_TARGET, return -ERANGE;
&eax, &edx);
if (err) { /* Skip if it is a HT core, Not an error */
dev_warn(&pdev->dev, "Unable to read" if (pdata->core_data[attr_no] != NULL)
" IA32_TEMPERATURE_TARGET MSR\n"); return 0;
} else {
data->ttarget = data->tjmax -
(((eax >> 8) & 0xff) * 1000);
err = device_create_file(&pdev->dev,
&sensor_dev_attr_temp1_max.dev_attr);
if (err)
goto exit_free;
}
}
if ((err = sysfs_create_group(&pdev->dev.kobj, &coretemp_group))) tdata = init_temp_data(cpu, pkg_flag);
goto exit_dev; if (!tdata)
return -ENOMEM;
data->hwmon_dev = hwmon_device_register(&pdev->dev); /* Test if we can access the status register */
if (IS_ERR(data->hwmon_dev)) { err = rdmsr_safe_on_cpu(cpu, tdata->status_reg, &eax, &edx);
err = PTR_ERR(data->hwmon_dev); if (err)
dev_err(&pdev->dev, "Class registration failed (%d)\n", goto exit_free;
err);
goto exit_class; /* We can access status register. Get Critical Temperature */
} if (pkg_flag)
tdata->tjmax = get_pkg_tjmax(pdev->id, &pdev->dev);
else
tdata->tjmax = get_tjmax(c, cpu, &pdev->dev);
update_ttarget(c->x86_model, tdata, &pdev->dev);
pdata->core_data[attr_no] = tdata;
/* Create sysfs interfaces */
err = create_core_attrs(tdata, &pdev->dev, attr_no);
if (err)
goto exit_free;
return 0; return 0;
exit_free:
kfree(tdata);
return err;
}
static void coretemp_add_core(unsigned int cpu, int pkg_flag)
{
struct platform_data *pdata;
struct platform_device *pdev = coretemp_get_pdev(cpu);
int err;
if (!pdev)
return;
pdata = platform_get_drvdata(pdev);
err = create_core_data(pdata, pdev, cpu, pkg_flag);
if (err)
dev_err(&pdev->dev, "Adding Core %u failed\n", cpu);
}
static void coretemp_remove_core(struct platform_data *pdata,
struct device *dev, int indx)
{
int i;
struct temp_data *tdata = pdata->core_data[indx];
exit_class: /* Remove the sysfs attributes */
sysfs_remove_group(&pdev->dev.kobj, &coretemp_group); for (i = 0; i < MAX_ATTRS; i++)
exit_dev: device_remove_file(dev, &tdata->sd_attrs[i].dev_attr);
device_remove_file(&pdev->dev, &sensor_dev_attr_temp1_max.dev_attr);
kfree(pdata->core_data[indx]);
pdata->core_data[indx] = NULL;
}
static int __devinit coretemp_probe(struct platform_device *pdev)
{
struct platform_data *pdata;
int err;
/* Check the microcode version of the CPU */
err = chk_ucode_version(pdev);
if (err)
return err;
/* Initialize the per-package data structures */
pdata = kzalloc(sizeof(struct platform_data), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
err = create_name_attr(pdata, &pdev->dev);
if (err)
goto exit_free;
pdata->phys_proc_id = TO_PHYS_ID(pdev->id);
platform_set_drvdata(pdev, pdata);
pdata->hwmon_dev = hwmon_device_register(&pdev->dev);
if (IS_ERR(pdata->hwmon_dev)) {
err = PTR_ERR(pdata->hwmon_dev);
dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
goto exit_name;
}
return 0;
exit_name:
device_remove_file(&pdev->dev, &pdata->name_attr);
platform_set_drvdata(pdev, NULL);
exit_free: exit_free:
kfree(data); kfree(pdata);
exit:
return err; return err;
} }
static int __devexit coretemp_remove(struct platform_device *pdev) static int __devexit coretemp_remove(struct platform_device *pdev)
{ {
struct coretemp_data *data = platform_get_drvdata(pdev); struct platform_data *pdata = platform_get_drvdata(pdev);
int i;
for (i = MAX_CORE_DATA - 1; i >= 0; --i)
if (pdata->core_data[i])
coretemp_remove_core(pdata, &pdev->dev, i);
hwmon_device_unregister(data->hwmon_dev); device_remove_file(&pdev->dev, &pdata->name_attr);
sysfs_remove_group(&pdev->dev.kobj, &coretemp_group); hwmon_device_unregister(pdata->hwmon_dev);
device_remove_file(&pdev->dev, &sensor_dev_attr_temp1_max.dev_attr);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
kfree(data); kfree(pdata);
return 0; return 0;
} }
...@@ -421,50 +630,14 @@ static struct platform_driver coretemp_driver = { ...@@ -421,50 +630,14 @@ static struct platform_driver coretemp_driver = {
.remove = __devexit_p(coretemp_remove), .remove = __devexit_p(coretemp_remove),
}; };
struct pdev_entry {
struct list_head list;
struct platform_device *pdev;
unsigned int cpu;
#ifdef CONFIG_SMP
u16 phys_proc_id;
u16 cpu_core_id;
#endif
};
static LIST_HEAD(pdev_list);
static DEFINE_MUTEX(pdev_list_mutex);
static int __cpuinit coretemp_device_add(unsigned int cpu) static int __cpuinit coretemp_device_add(unsigned int cpu)
{ {
int err; int err;
struct platform_device *pdev; struct platform_device *pdev;
struct pdev_entry *pdev_entry; struct pdev_entry *pdev_entry;
struct cpuinfo_x86 *c = &cpu_data(cpu);
/*
* CPUID.06H.EAX[0] indicates whether the CPU has thermal
* sensors. We check this bit only, all the early CPUs
* without thermal sensors will be filtered out.
*/
if (!cpu_has(c, X86_FEATURE_DTS)) {
pr_info("CPU (model=0x%x) has no thermal sensor\n",
c->x86_model);
return 0;
}
mutex_lock(&pdev_list_mutex); mutex_lock(&pdev_list_mutex);
#ifdef CONFIG_SMP
/* Skip second HT entry of each core */
list_for_each_entry(pdev_entry, &pdev_list, list) {
if (c->phys_proc_id == pdev_entry->phys_proc_id &&
c->cpu_core_id == pdev_entry->cpu_core_id) {
err = 0; /* Not an error */
goto exit;
}
}
#endif
pdev = platform_device_alloc(DRVNAME, cpu); pdev = platform_device_alloc(DRVNAME, cpu);
if (!pdev) { if (!pdev) {
err = -ENOMEM; err = -ENOMEM;
...@@ -486,10 +659,9 @@ static int __cpuinit coretemp_device_add(unsigned int cpu) ...@@ -486,10 +659,9 @@ static int __cpuinit coretemp_device_add(unsigned int cpu)
pdev_entry->pdev = pdev; pdev_entry->pdev = pdev;
pdev_entry->cpu = cpu; pdev_entry->cpu = cpu;
#ifdef CONFIG_SMP pdev_entry->phys_proc_id = TO_PHYS_ID(cpu);
pdev_entry->phys_proc_id = c->phys_proc_id; pdev_entry->cpu_core_id = TO_CORE_ID(cpu);
pdev_entry->cpu_core_id = c->cpu_core_id;
#endif
list_add_tail(&pdev_entry->list, &pdev_list); list_add_tail(&pdev_entry->list, &pdev_list);
mutex_unlock(&pdev_list_mutex); mutex_unlock(&pdev_list_mutex);
...@@ -504,28 +676,108 @@ static int __cpuinit coretemp_device_add(unsigned int cpu) ...@@ -504,28 +676,108 @@ static int __cpuinit coretemp_device_add(unsigned int cpu)
return err; return err;
} }
static void __cpuinit coretemp_device_remove(unsigned int cpu) static void coretemp_device_remove(unsigned int cpu)
{ {
struct pdev_entry *p; struct pdev_entry *p, *n;
unsigned int i; u16 phys_proc_id = TO_PHYS_ID(cpu);
mutex_lock(&pdev_list_mutex); mutex_lock(&pdev_list_mutex);
list_for_each_entry(p, &pdev_list, list) { list_for_each_entry_safe(p, n, &pdev_list, list) {
if (p->cpu != cpu) if (p->phys_proc_id != phys_proc_id)
continue; continue;
platform_device_unregister(p->pdev); platform_device_unregister(p->pdev);
list_del(&p->list); list_del(&p->list);
mutex_unlock(&pdev_list_mutex);
kfree(p); kfree(p);
for_each_cpu(i, cpu_sibling_mask(cpu))
if (i != cpu && !coretemp_device_add(i))
break;
return;
} }
mutex_unlock(&pdev_list_mutex); mutex_unlock(&pdev_list_mutex);
} }
static bool is_any_core_online(struct platform_data *pdata)
{
int i;
/* Find online cores, except pkgtemp data */
for (i = MAX_CORE_DATA - 1; i >= 0; --i) {
if (pdata->core_data[i] &&
!pdata->core_data[i]->is_pkg_data) {
return true;
}
}
return false;
}
static void __cpuinit get_core_online(unsigned int cpu)
{
struct cpuinfo_x86 *c = &cpu_data(cpu);
struct platform_device *pdev = coretemp_get_pdev(cpu);
int err;
/*
* CPUID.06H.EAX[0] indicates whether the CPU has thermal
* sensors. We check this bit only, all the early CPUs
* without thermal sensors will be filtered out.
*/
if (!cpu_has(c, X86_FEATURE_DTS))
return;
if (!pdev) {
/*
* Alright, we have DTS support.
* We are bringing the _first_ core in this pkg
* online. So, initialize per-pkg data structures and
* then bring this core online.
*/
err = coretemp_device_add(cpu);
if (err)
return;
/*
* Check whether pkgtemp support is available.
* If so, add interfaces for pkgtemp.
*/
if (cpu_has(c, X86_FEATURE_PTS))
coretemp_add_core(cpu, 1);
}
/*
* Physical CPU device already exists.
* So, just add interfaces for this core.
*/
coretemp_add_core(cpu, 0);
}
static void __cpuinit put_core_offline(unsigned int cpu)
{
int i, indx;
struct platform_data *pdata;
struct platform_device *pdev = coretemp_get_pdev(cpu);
/* If the physical CPU device does not exist, just return */
if (!pdev)
return;
pdata = platform_get_drvdata(pdev);
indx = TO_ATTR_NO(cpu);
if (pdata->core_data[indx] && pdata->core_data[indx]->cpu == cpu)
coretemp_remove_core(pdata, &pdev->dev, indx);
/* Online the HT version of this core, if any */
for_each_cpu(i, cpu_sibling_mask(cpu)) {
if (i != cpu) {
get_core_online(i);
break;
}
}
/*
* If all cores in this pkg are offline, remove the device.
* coretemp_device_remove calls unregister_platform_device,
* which in turn calls coretemp_remove. This removes the
* pkgtemp entry and does other clean ups.
*/
if (!is_any_core_online(pdata))
coretemp_device_remove(cpu);
}
static int __cpuinit coretemp_cpu_callback(struct notifier_block *nfb, static int __cpuinit coretemp_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu) unsigned long action, void *hcpu)
{ {
...@@ -534,10 +786,10 @@ static int __cpuinit coretemp_cpu_callback(struct notifier_block *nfb, ...@@ -534,10 +786,10 @@ static int __cpuinit coretemp_cpu_callback(struct notifier_block *nfb,
switch (action) { switch (action) {
case CPU_ONLINE: case CPU_ONLINE:
case CPU_DOWN_FAILED: case CPU_DOWN_FAILED:
coretemp_device_add(cpu); get_core_online(cpu);
break; break;
case CPU_DOWN_PREPARE: case CPU_DOWN_PREPARE:
coretemp_device_remove(cpu); put_core_offline(cpu);
break; break;
} }
return NOTIFY_OK; return NOTIFY_OK;
...@@ -560,7 +812,7 @@ static int __init coretemp_init(void) ...@@ -560,7 +812,7 @@ static int __init coretemp_init(void)
goto exit; goto exit;
for_each_online_cpu(i) for_each_online_cpu(i)
coretemp_device_add(i); get_core_online(i);
#ifndef CONFIG_HOTPLUG_CPU #ifndef CONFIG_HOTPLUG_CPU
if (list_empty(&pdev_list)) { if (list_empty(&pdev_list)) {
......
/*
* Driver for
* Maxim MAX16065/MAX16066 12-Channel/8-Channel, Flash-Configurable
* System Managers with Nonvolatile Fault Registers
* Maxim MAX16067/MAX16068 6-Channel, Flash-Configurable System Managers
* with Nonvolatile Fault Registers
* Maxim MAX16070/MAX16071 12-Channel/8-Channel, Flash-Configurable System
* Monitors with Nonvolatile Fault Registers
*
* Copyright (C) 2011 Ericsson AB.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
enum chips { max16065, max16066, max16067, max16068, max16070, max16071 };
/*
* Registers
*/
#define MAX16065_ADC(x) ((x) * 2)
#define MAX16065_CURR_SENSE 0x18
#define MAX16065_CSP_ADC 0x19
#define MAX16065_FAULT(x) (0x1b + (x))
#define MAX16065_SCALE(x) (0x43 + (x))
#define MAX16065_CURR_CONTROL 0x47
#define MAX16065_LIMIT(l, x) (0x48 + (l) + (x) * 3) /*
* l: limit
* 0: min/max
* 1: crit
* 2: lcrit
* x: ADC index
*/
#define MAX16065_SW_ENABLE 0x73
#define MAX16065_WARNING_OV (1 << 3) /* Set if secondary threshold is OV
warning */
#define MAX16065_CURR_ENABLE (1 << 0)
#define MAX16065_NUM_LIMIT 3
#define MAX16065_NUM_ADC 12 /* maximum number of ADC channels */
static const int max16065_num_adc[] = {
[max16065] = 12,
[max16066] = 8,
[max16067] = 6,
[max16068] = 6,
[max16070] = 12,
[max16071] = 8,
};
static const bool max16065_have_secondary[] = {
[max16065] = true,
[max16066] = true,
[max16067] = false,
[max16068] = false,
[max16070] = true,
[max16071] = true,
};
static const bool max16065_have_current[] = {
[max16065] = true,
[max16066] = true,
[max16067] = false,
[max16068] = false,
[max16070] = true,
[max16071] = true,
};
struct max16065_data {
enum chips type;
struct device *hwmon_dev;
struct mutex update_lock;
bool valid;
unsigned long last_updated; /* in jiffies */
int num_adc;
bool have_current;
int curr_gain;
/* limits are in mV */
int limit[MAX16065_NUM_LIMIT][MAX16065_NUM_ADC];
int range[MAX16065_NUM_ADC + 1];/* voltage range */
int adc[MAX16065_NUM_ADC + 1]; /* adc values (raw) including csp_adc */
int curr_sense;
int fault[2];
};
static const int max16065_adc_range[] = { 5560, 2780, 1390, 0 };
static const int max16065_csp_adc_range[] = { 7000, 14000 };
/* ADC registers have 10 bit resolution. */
static inline int ADC_TO_MV(int adc, int range)
{
return (adc * range) / 1024;
}
/*
* Limit registers have 8 bit resolution and match upper 8 bits of ADC
* registers.
*/
static inline int LIMIT_TO_MV(int limit, int range)
{
return limit * range / 256;
}
static inline int MV_TO_LIMIT(int mv, int range)
{
return SENSORS_LIMIT(DIV_ROUND_CLOSEST(mv * 256, range), 0, 255);
}
static inline int ADC_TO_CURR(int adc, int gain)
{
return adc * 1400000 / gain * 255;
}
/*
* max16065_read_adc()
*
* Read 16 bit value from <reg>, <reg+1>.
* Upper 8 bits are in <reg>, lower 2 bits are in bits 7:6 of <reg+1>.
*/
static int max16065_read_adc(struct i2c_client *client, int reg)
{
int rv;
rv = i2c_smbus_read_word_data(client, reg);
if (unlikely(rv < 0))
return rv;
return ((rv & 0xff) << 2) | ((rv >> 14) & 0x03);
}
static struct max16065_data *max16065_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct max16065_data *data = i2c_get_clientdata(client);
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
int i;
for (i = 0; i < data->num_adc; i++)
data->adc[i]
= max16065_read_adc(client, MAX16065_ADC(i));
if (data->have_current) {
data->adc[MAX16065_NUM_ADC]
= max16065_read_adc(client, MAX16065_CSP_ADC);
data->curr_sense
= i2c_smbus_read_byte_data(client,
MAX16065_CURR_SENSE);
}
for (i = 0; i < DIV_ROUND_UP(data->num_adc, 8); i++)
data->fault[i]
= i2c_smbus_read_byte_data(client, MAX16065_FAULT(i));
data->last_updated = jiffies;
data->valid = 1;
}
mutex_unlock(&data->update_lock);
return data;
}
static ssize_t max16065_show_alarm(struct device *dev,
struct device_attribute *da, char *buf)
{
struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(da);
struct max16065_data *data = max16065_update_device(dev);
int val = data->fault[attr2->nr];
if (val < 0)
return val;
val &= (1 << attr2->index);
if (val)
i2c_smbus_write_byte_data(to_i2c_client(dev),
MAX16065_FAULT(attr2->nr), val);
return snprintf(buf, PAGE_SIZE, "%d\n", !!val);
}
static ssize_t max16065_show_input(struct device *dev,
struct device_attribute *da, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct max16065_data *data = max16065_update_device(dev);
int adc = data->adc[attr->index];
if (unlikely(adc < 0))
return adc;
return snprintf(buf, PAGE_SIZE, "%d\n",
ADC_TO_MV(adc, data->range[attr->index]));
}
static ssize_t max16065_show_current(struct device *dev,
struct device_attribute *da, char *buf)
{
struct max16065_data *data = max16065_update_device(dev);
if (unlikely(data->curr_sense < 0))
return data->curr_sense;
return snprintf(buf, PAGE_SIZE, "%d\n",
ADC_TO_CURR(data->curr_sense, data->curr_gain));
}
static ssize_t max16065_set_limit(struct device *dev,
struct device_attribute *da,
const char *buf, size_t count)
{
struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(da);
struct i2c_client *client = to_i2c_client(dev);
struct max16065_data *data = i2c_get_clientdata(client);
unsigned long val;
int err;
int limit;
err = strict_strtoul(buf, 10, &val);
if (unlikely(err < 0))
return err;
limit = MV_TO_LIMIT(val, data->range[attr2->index]);
mutex_lock(&data->update_lock);
data->limit[attr2->nr][attr2->index]
= LIMIT_TO_MV(limit, data->range[attr2->index]);
i2c_smbus_write_byte_data(client,
MAX16065_LIMIT(attr2->nr, attr2->index),
limit);
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t max16065_show_limit(struct device *dev,
struct device_attribute *da, char *buf)
{
struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(da);
struct i2c_client *client = to_i2c_client(dev);
struct max16065_data *data = i2c_get_clientdata(client);
return snprintf(buf, PAGE_SIZE, "%d\n",
data->limit[attr2->nr][attr2->index]);
}
/* Construct a sensor_device_attribute structure for each register */
/* Input voltages */
static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, max16065_show_input, NULL, 0);
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, max16065_show_input, NULL, 1);
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, max16065_show_input, NULL, 2);
static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, max16065_show_input, NULL, 3);
static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, max16065_show_input, NULL, 4);
static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, max16065_show_input, NULL, 5);
static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, max16065_show_input, NULL, 6);
static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, max16065_show_input, NULL, 7);
static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, max16065_show_input, NULL, 8);
static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, max16065_show_input, NULL, 9);
static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, max16065_show_input, NULL, 10);
static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, max16065_show_input, NULL, 11);
static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, max16065_show_input, NULL, 12);
/* Input voltages lcrit */
static SENSOR_DEVICE_ATTR_2(in0_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 2, 0);
static SENSOR_DEVICE_ATTR_2(in1_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 2, 1);
static SENSOR_DEVICE_ATTR_2(in2_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 2, 2);
static SENSOR_DEVICE_ATTR_2(in3_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 2, 3);
static SENSOR_DEVICE_ATTR_2(in4_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 2, 4);
static SENSOR_DEVICE_ATTR_2(in5_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 2, 5);
static SENSOR_DEVICE_ATTR_2(in6_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 2, 6);
static SENSOR_DEVICE_ATTR_2(in7_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 2, 7);
static SENSOR_DEVICE_ATTR_2(in8_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 2, 8);
static SENSOR_DEVICE_ATTR_2(in9_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 2, 9);
static SENSOR_DEVICE_ATTR_2(in10_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 2, 10);
static SENSOR_DEVICE_ATTR_2(in11_lcrit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 2, 11);
/* Input voltages crit */
static SENSOR_DEVICE_ATTR_2(in0_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 1, 0);
static SENSOR_DEVICE_ATTR_2(in1_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 1, 1);
static SENSOR_DEVICE_ATTR_2(in2_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 1, 2);
static SENSOR_DEVICE_ATTR_2(in3_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 1, 3);
static SENSOR_DEVICE_ATTR_2(in4_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 1, 4);
static SENSOR_DEVICE_ATTR_2(in5_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 1, 5);
static SENSOR_DEVICE_ATTR_2(in6_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 1, 6);
static SENSOR_DEVICE_ATTR_2(in7_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 1, 7);
static SENSOR_DEVICE_ATTR_2(in8_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 1, 8);
static SENSOR_DEVICE_ATTR_2(in9_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 1, 9);
static SENSOR_DEVICE_ATTR_2(in10_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 1, 10);
static SENSOR_DEVICE_ATTR_2(in11_crit, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 1, 11);
/* Input voltages min */
static SENSOR_DEVICE_ATTR_2(in0_min, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 0);
static SENSOR_DEVICE_ATTR_2(in1_min, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 1);
static SENSOR_DEVICE_ATTR_2(in2_min, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 2);
static SENSOR_DEVICE_ATTR_2(in3_min, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 3);
static SENSOR_DEVICE_ATTR_2(in4_min, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 4);
static SENSOR_DEVICE_ATTR_2(in5_min, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 5);
static SENSOR_DEVICE_ATTR_2(in6_min, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 6);
static SENSOR_DEVICE_ATTR_2(in7_min, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 7);
static SENSOR_DEVICE_ATTR_2(in8_min, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 8);
static SENSOR_DEVICE_ATTR_2(in9_min, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 9);
static SENSOR_DEVICE_ATTR_2(in10_min, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 10);
static SENSOR_DEVICE_ATTR_2(in11_min, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 11);
/* Input voltages max */
static SENSOR_DEVICE_ATTR_2(in0_max, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 0);
static SENSOR_DEVICE_ATTR_2(in1_max, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 1);
static SENSOR_DEVICE_ATTR_2(in2_max, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 2);
static SENSOR_DEVICE_ATTR_2(in3_max, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 3);
static SENSOR_DEVICE_ATTR_2(in4_max, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 4);
static SENSOR_DEVICE_ATTR_2(in5_max, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 5);
static SENSOR_DEVICE_ATTR_2(in6_max, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 6);
static SENSOR_DEVICE_ATTR_2(in7_max, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 7);
static SENSOR_DEVICE_ATTR_2(in8_max, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 8);
static SENSOR_DEVICE_ATTR_2(in9_max, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 9);
static SENSOR_DEVICE_ATTR_2(in10_max, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 10);
static SENSOR_DEVICE_ATTR_2(in11_max, S_IWUSR | S_IRUGO, max16065_show_limit,
max16065_set_limit, 0, 11);
/* alarms */
static SENSOR_DEVICE_ATTR_2(in0_alarm, S_IRUGO, max16065_show_alarm, NULL,
0, 0);
static SENSOR_DEVICE_ATTR_2(in1_alarm, S_IRUGO, max16065_show_alarm, NULL,
0, 1);
static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, max16065_show_alarm, NULL,
0, 2);
static SENSOR_DEVICE_ATTR_2(in3_alarm, S_IRUGO, max16065_show_alarm, NULL,
0, 3);
static SENSOR_DEVICE_ATTR_2(in4_alarm, S_IRUGO, max16065_show_alarm, NULL,
0, 4);
static SENSOR_DEVICE_ATTR_2(in5_alarm, S_IRUGO, max16065_show_alarm, NULL,
0, 5);
static SENSOR_DEVICE_ATTR_2(in6_alarm, S_IRUGO, max16065_show_alarm, NULL,
0, 6);
static SENSOR_DEVICE_ATTR_2(in7_alarm, S_IRUGO, max16065_show_alarm, NULL,
0, 7);
static SENSOR_DEVICE_ATTR_2(in8_alarm, S_IRUGO, max16065_show_alarm, NULL,
1, 0);
static SENSOR_DEVICE_ATTR_2(in9_alarm, S_IRUGO, max16065_show_alarm, NULL,
1, 1);
static SENSOR_DEVICE_ATTR_2(in10_alarm, S_IRUGO, max16065_show_alarm, NULL,
1, 2);
static SENSOR_DEVICE_ATTR_2(in11_alarm, S_IRUGO, max16065_show_alarm, NULL,
1, 3);
/* Current and alarm */
static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, max16065_show_current, NULL, 0);
static SENSOR_DEVICE_ATTR_2(curr1_alarm, S_IRUGO, max16065_show_alarm, NULL,
1, 4);
/*
* Finally, construct an array of pointers to members of the above objects,
* as required for sysfs_create_group()
*/
static struct attribute *max16065_basic_attributes[] = {
&sensor_dev_attr_in0_input.dev_attr.attr,
&sensor_dev_attr_in0_lcrit.dev_attr.attr,
&sensor_dev_attr_in0_crit.dev_attr.attr,
&sensor_dev_attr_in0_alarm.dev_attr.attr,
&sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in1_lcrit.dev_attr.attr,
&sensor_dev_attr_in1_crit.dev_attr.attr,
&sensor_dev_attr_in1_alarm.dev_attr.attr,
&sensor_dev_attr_in2_input.dev_attr.attr,
&sensor_dev_attr_in2_lcrit.dev_attr.attr,
&sensor_dev_attr_in2_crit.dev_attr.attr,
&sensor_dev_attr_in2_alarm.dev_attr.attr,
&sensor_dev_attr_in3_input.dev_attr.attr,
&sensor_dev_attr_in3_lcrit.dev_attr.attr,
&sensor_dev_attr_in3_crit.dev_attr.attr,
&sensor_dev_attr_in3_alarm.dev_attr.attr,
&sensor_dev_attr_in4_input.dev_attr.attr,
&sensor_dev_attr_in4_lcrit.dev_attr.attr,
&sensor_dev_attr_in4_crit.dev_attr.attr,
&sensor_dev_attr_in4_alarm.dev_attr.attr,
&sensor_dev_attr_in5_input.dev_attr.attr,
&sensor_dev_attr_in5_lcrit.dev_attr.attr,
&sensor_dev_attr_in5_crit.dev_attr.attr,
&sensor_dev_attr_in5_alarm.dev_attr.attr,
&sensor_dev_attr_in6_input.dev_attr.attr,
&sensor_dev_attr_in6_lcrit.dev_attr.attr,
&sensor_dev_attr_in6_crit.dev_attr.attr,
&sensor_dev_attr_in6_alarm.dev_attr.attr,
&sensor_dev_attr_in7_input.dev_attr.attr,
&sensor_dev_attr_in7_lcrit.dev_attr.attr,
&sensor_dev_attr_in7_crit.dev_attr.attr,
&sensor_dev_attr_in7_alarm.dev_attr.attr,
&sensor_dev_attr_in8_input.dev_attr.attr,
&sensor_dev_attr_in8_lcrit.dev_attr.attr,
&sensor_dev_attr_in8_crit.dev_attr.attr,
&sensor_dev_attr_in8_alarm.dev_attr.attr,
&sensor_dev_attr_in9_input.dev_attr.attr,
&sensor_dev_attr_in9_lcrit.dev_attr.attr,
&sensor_dev_attr_in9_crit.dev_attr.attr,
&sensor_dev_attr_in9_alarm.dev_attr.attr,
&sensor_dev_attr_in10_input.dev_attr.attr,
&sensor_dev_attr_in10_lcrit.dev_attr.attr,
&sensor_dev_attr_in10_crit.dev_attr.attr,
&sensor_dev_attr_in10_alarm.dev_attr.attr,
&sensor_dev_attr_in11_input.dev_attr.attr,
&sensor_dev_attr_in11_lcrit.dev_attr.attr,
&sensor_dev_attr_in11_crit.dev_attr.attr,
&sensor_dev_attr_in11_alarm.dev_attr.attr,
NULL
};
static struct attribute *max16065_current_attributes[] = {
&sensor_dev_attr_in12_input.dev_attr.attr,
&sensor_dev_attr_curr1_input.dev_attr.attr,
&sensor_dev_attr_curr1_alarm.dev_attr.attr,
NULL
};
static struct attribute *max16065_min_attributes[] = {
&sensor_dev_attr_in0_min.dev_attr.attr,
&sensor_dev_attr_in1_min.dev_attr.attr,
&sensor_dev_attr_in2_min.dev_attr.attr,
&sensor_dev_attr_in3_min.dev_attr.attr,
&sensor_dev_attr_in4_min.dev_attr.attr,
&sensor_dev_attr_in5_min.dev_attr.attr,
&sensor_dev_attr_in6_min.dev_attr.attr,
&sensor_dev_attr_in7_min.dev_attr.attr,
&sensor_dev_attr_in8_min.dev_attr.attr,
&sensor_dev_attr_in9_min.dev_attr.attr,
&sensor_dev_attr_in10_min.dev_attr.attr,
&sensor_dev_attr_in11_min.dev_attr.attr,
NULL
};
static struct attribute *max16065_max_attributes[] = {
&sensor_dev_attr_in0_max.dev_attr.attr,
&sensor_dev_attr_in1_max.dev_attr.attr,
&sensor_dev_attr_in2_max.dev_attr.attr,
&sensor_dev_attr_in3_max.dev_attr.attr,
&sensor_dev_attr_in4_max.dev_attr.attr,
&sensor_dev_attr_in5_max.dev_attr.attr,
&sensor_dev_attr_in6_max.dev_attr.attr,
&sensor_dev_attr_in7_max.dev_attr.attr,
&sensor_dev_attr_in8_max.dev_attr.attr,
&sensor_dev_attr_in9_max.dev_attr.attr,
&sensor_dev_attr_in10_max.dev_attr.attr,
&sensor_dev_attr_in11_max.dev_attr.attr,
NULL
};
static const struct attribute_group max16065_basic_group = {
.attrs = max16065_basic_attributes,
};
static const struct attribute_group max16065_current_group = {
.attrs = max16065_current_attributes,
};
static const struct attribute_group max16065_min_group = {
.attrs = max16065_min_attributes,
};
static const struct attribute_group max16065_max_group = {
.attrs = max16065_max_attributes,
};
static void max16065_cleanup(struct i2c_client *client)
{
sysfs_remove_group(&client->dev.kobj, &max16065_max_group);
sysfs_remove_group(&client->dev.kobj, &max16065_min_group);
sysfs_remove_group(&client->dev.kobj, &max16065_current_group);
sysfs_remove_group(&client->dev.kobj, &max16065_basic_group);
}
static int max16065_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = client->adapter;
struct max16065_data *data;
int i, j, val, ret;
bool have_secondary; /* true if chip has secondary limits */
bool secondary_is_max = false; /* secondary limits reflect max */
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
| I2C_FUNC_SMBUS_READ_WORD_DATA))
return -ENODEV;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (unlikely(!data))
return -ENOMEM;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
data->num_adc = max16065_num_adc[id->driver_data];
data->have_current = max16065_have_current[id->driver_data];
have_secondary = max16065_have_secondary[id->driver_data];
if (have_secondary) {
val = i2c_smbus_read_byte_data(client, MAX16065_SW_ENABLE);
if (unlikely(val < 0)) {
ret = val;
goto out_free;
}
secondary_is_max = val & MAX16065_WARNING_OV;
}
/* Read scale registers, convert to range */
for (i = 0; i < DIV_ROUND_UP(data->num_adc, 4); i++) {
val = i2c_smbus_read_byte_data(client, MAX16065_SCALE(i));
if (unlikely(val < 0)) {
ret = val;
goto out_free;
}
for (j = 0; j < 4 && i * 4 + j < data->num_adc; j++) {
data->range[i * 4 + j] =
max16065_adc_range[(val >> (j * 2)) & 0x3];
}
}
/* Read limits */
for (i = 0; i < MAX16065_NUM_LIMIT; i++) {
if (i == 0 && !have_secondary)
continue;
for (j = 0; j < data->num_adc; j++) {
val = i2c_smbus_read_byte_data(client,
MAX16065_LIMIT(i, j));
if (unlikely(val < 0)) {
ret = val;
goto out_free;
}
data->limit[i][j] = LIMIT_TO_MV(val, data->range[j]);
}
}
/* Register sysfs hooks */
for (i = 0; i < data->num_adc * 4; i++) {
/* Do not create sysfs entry if channel is disabled */
if (!data->range[i / 4])
continue;
ret = sysfs_create_file(&client->dev.kobj,
max16065_basic_attributes[i]);
if (unlikely(ret))
goto out;
}
if (have_secondary) {
struct attribute **attr = secondary_is_max ?
max16065_max_attributes : max16065_min_attributes;
for (i = 0; i < data->num_adc; i++) {
if (!data->range[i])
continue;
ret = sysfs_create_file(&client->dev.kobj, attr[i]);
if (unlikely(ret))
goto out;
}
}
if (data->have_current) {
val = i2c_smbus_read_byte_data(client, MAX16065_CURR_CONTROL);
if (unlikely(val < 0)) {
ret = val;
goto out;
}
if (val & MAX16065_CURR_ENABLE) {
/*
* Current gain is 6, 12, 24, 48 based on values in
* bit 2,3.
*/
data->curr_gain = 6 << ((val >> 2) & 0x03);
data->range[MAX16065_NUM_ADC]
= max16065_csp_adc_range[(val >> 1) & 0x01];
ret = sysfs_create_group(&client->dev.kobj,
&max16065_current_group);
if (unlikely(ret))
goto out;
} else {
data->have_current = false;
}
}
data->hwmon_dev = hwmon_device_register(&client->dev);
if (unlikely(IS_ERR(data->hwmon_dev))) {
ret = PTR_ERR(data->hwmon_dev);
goto out;
}
return 0;
out:
max16065_cleanup(client);
out_free:
kfree(data);
return ret;
}
static int max16065_remove(struct i2c_client *client)
{
struct max16065_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
max16065_cleanup(client);
kfree(data);
return 0;
}
static const struct i2c_device_id max16065_id[] = {
{ "max16065", max16065 },
{ "max16066", max16066 },
{ "max16067", max16067 },
{ "max16068", max16068 },
{ "max16070", max16070 },
{ "max16071", max16071 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max16065_id);
/* This is the driver that will be inserted */
static struct i2c_driver max16065_driver = {
.driver = {
.name = "max16065",
},
.probe = max16065_probe,
.remove = max16065_remove,
.id_table = max16065_id,
};
static int __init max16065_init(void)
{
return i2c_add_driver(&max16065_driver);
}
static void __exit max16065_exit(void)
{
i2c_del_driver(&max16065_driver);
}
MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>");
MODULE_DESCRIPTION("MAX16065 driver");
MODULE_LICENSE("GPL");
module_init(max16065_init);
module_exit(max16065_exit);
...@@ -32,7 +32,7 @@ enum chips { max34440, max34441 }; ...@@ -32,7 +32,7 @@ enum chips { max34440, max34441 };
#define MAX34440_STATUS_OT_FAULT (1 << 5) #define MAX34440_STATUS_OT_FAULT (1 << 5)
#define MAX34440_STATUS_OT_WARN (1 << 6) #define MAX34440_STATUS_OT_WARN (1 << 6)
static int max34440_get_status(struct i2c_client *client, int page, int reg) static int max34440_read_byte_data(struct i2c_client *client, int page, int reg)
{ {
int ret; int ret;
int mfg_status; int mfg_status;
...@@ -108,7 +108,7 @@ static struct pmbus_driver_info max34440_info[] = { ...@@ -108,7 +108,7 @@ static struct pmbus_driver_info max34440_info[] = {
.func[11] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[11] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[12] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[12] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[13] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[13] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.get_status = max34440_get_status, .read_byte_data = max34440_read_byte_data,
}, },
[max34441] = { [max34441] = {
.pages = 12, .pages = 12,
...@@ -149,7 +149,7 @@ static struct pmbus_driver_info max34440_info[] = { ...@@ -149,7 +149,7 @@ static struct pmbus_driver_info max34440_info[] = {
.func[9] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[9] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[10] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[10] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[11] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[11] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.get_status = max34440_get_status, .read_byte_data = max34440_read_byte_data,
}, },
}; };
......
/*
* Driver for +/-1 degree C, SMBus-Compatible Remote/Local Temperature Sensor
* with Overtemperature Alarm
*
* Copyright (C) 2011 AppearTV AS
*
* Derived from:
*
* Based on the max1619 driver.
* Copyright (C) 2003-2004 Alexey Fisher <fishor@mail.ru>
* Jean Delvare <khali@linux-fr.org>
*
* The MAX6642 is a sensor chip made by Maxim.
* It reports up to two temperatures (its own plus up to
* one external one). Complete datasheet can be
* obtained from Maxim's website at:
* http://datasheets.maxim-ic.com/en/ds/MAX6642.pdf
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
static const unsigned short normal_i2c[] = {
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
/*
* The MAX6642 registers
*/
#define MAX6642_REG_R_MAN_ID 0xFE
#define MAX6642_REG_R_CONFIG 0x03
#define MAX6642_REG_W_CONFIG 0x09
#define MAX6642_REG_R_STATUS 0x02
#define MAX6642_REG_R_LOCAL_TEMP 0x00
#define MAX6642_REG_R_LOCAL_TEMPL 0x11
#define MAX6642_REG_R_LOCAL_HIGH 0x05
#define MAX6642_REG_W_LOCAL_HIGH 0x0B
#define MAX6642_REG_R_REMOTE_TEMP 0x01
#define MAX6642_REG_R_REMOTE_TEMPL 0x10
#define MAX6642_REG_R_REMOTE_HIGH 0x07
#define MAX6642_REG_W_REMOTE_HIGH 0x0D
/*
* Conversions
*/
static int temp_from_reg10(int val)
{
return val * 250;
}
static int temp_from_reg(int val)
{
return val * 1000;
}
static int temp_to_reg(int val)
{
return val / 1000;
}
/*
* Client data (each client gets its own)
*/
struct max6642_data {
struct device *hwmon_dev;
struct mutex update_lock;
bool valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
/* registers values */
u16 temp_input[2]; /* local/remote */
u16 temp_high[2]; /* local/remote */
u8 alarms;
};
/*
* Real code
*/
static void max6642_init_client(struct i2c_client *client)
{
u8 config;
struct max6642_data *data = i2c_get_clientdata(client);
/*
* Start the conversions.
*/
config = i2c_smbus_read_byte_data(client, MAX6642_REG_R_CONFIG);
if (config & 0x40)
i2c_smbus_write_byte_data(client, MAX6642_REG_W_CONFIG,
config & 0xBF); /* run */
data->temp_high[0] = i2c_smbus_read_byte_data(client,
MAX6642_REG_R_LOCAL_HIGH);
data->temp_high[1] = i2c_smbus_read_byte_data(client,
MAX6642_REG_R_REMOTE_HIGH);
}
/* Return 0 if detection is successful, -ENODEV otherwise */
static int max6642_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
u8 reg_config, reg_status, man_id;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
/* identification */
man_id = i2c_smbus_read_byte_data(client, MAX6642_REG_R_MAN_ID);
if (man_id != 0x4D)
return -ENODEV;
/*
* We read the config and status register, the 4 lower bits in the
* config register should be zero and bit 5, 3, 1 and 0 should be
* zero in the status register.
*/
reg_config = i2c_smbus_read_byte_data(client, MAX6642_REG_R_CONFIG);
reg_status = i2c_smbus_read_byte_data(client, MAX6642_REG_R_STATUS);
if (((reg_config & 0x0f) != 0x00) ||
((reg_status & 0x2b) != 0x00))
return -ENODEV;
strlcpy(info->type, "max6642", I2C_NAME_SIZE);
return 0;
}
static struct max6642_data *max6642_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct max6642_data *data = i2c_get_clientdata(client);
u16 val, tmp;
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
dev_dbg(&client->dev, "Updating max6642 data.\n");
val = i2c_smbus_read_byte_data(client,
MAX6642_REG_R_LOCAL_TEMPL);
tmp = (val >> 6) & 3;
val = i2c_smbus_read_byte_data(client,
MAX6642_REG_R_LOCAL_TEMP);
val = (val << 2) | tmp;
data->temp_input[0] = val;
val = i2c_smbus_read_byte_data(client,
MAX6642_REG_R_REMOTE_TEMPL);
tmp = (val >> 6) & 3;
val = i2c_smbus_read_byte_data(client,
MAX6642_REG_R_REMOTE_TEMP);
val = (val << 2) | tmp;
data->temp_input[1] = val;
data->alarms = i2c_smbus_read_byte_data(client,
MAX6642_REG_R_STATUS);
data->last_updated = jiffies;
data->valid = 1;
}
mutex_unlock(&data->update_lock);
return data;
}
/*
* Sysfs stuff
*/
static ssize_t show_temp_max10(struct device *dev,
struct device_attribute *dev_attr, char *buf)
{
struct max6642_data *data = max6642_update_device(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
return sprintf(buf, "%d\n",
temp_from_reg10(data->temp_input[attr->index]));
}
static ssize_t show_temp_max(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct max6642_data *data = max6642_update_device(dev);
struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr);
return sprintf(buf, "%d\n", temp_from_reg(data->temp_high[attr2->nr]));
}
static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long val;
int err;
struct i2c_client *client = to_i2c_client(dev);
struct max6642_data *data = i2c_get_clientdata(client);
struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr);
err = strict_strtoul(buf, 10, &val);
if (err < 0)
return err;
mutex_lock(&data->update_lock);
data->temp_high[attr2->nr] = SENSORS_LIMIT(temp_to_reg(val), 0, 255);
i2c_smbus_write_byte_data(client, attr2->index,
data->temp_high[attr2->nr]);
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
char *buf)
{
int bitnr = to_sensor_dev_attr(attr)->index;
struct max6642_data *data = max6642_update_device(dev);
return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_max10, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_max10, NULL, 1);
static SENSOR_DEVICE_ATTR_2(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
set_temp_max, 0, MAX6642_REG_W_LOCAL_HIGH);
static SENSOR_DEVICE_ATTR_2(temp2_max, S_IWUSR | S_IRUGO, show_temp_max,
set_temp_max, 1, MAX6642_REG_W_REMOTE_HIGH);
static SENSOR_DEVICE_ATTR(temp_fault, S_IRUGO, show_alarm, NULL, 2);
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6);
static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4);
static struct attribute *max6642_attributes[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp2_max.dev_attr.attr,
&sensor_dev_attr_temp_fault.dev_attr.attr,
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
NULL
};
static const struct attribute_group max6642_group = {
.attrs = max6642_attributes,
};
static int max6642_probe(struct i2c_client *new_client,
const struct i2c_device_id *id)
{
struct max6642_data *data;
int err;
data = kzalloc(sizeof(struct max6642_data), GFP_KERNEL);
if (!data) {
err = -ENOMEM;
goto exit;
}
i2c_set_clientdata(new_client, data);
mutex_init(&data->update_lock);
/* Initialize the MAX6642 chip */
max6642_init_client(new_client);
/* Register sysfs hooks */
err = sysfs_create_group(&new_client->dev.kobj, &max6642_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, &max6642_group);
exit_free:
kfree(data);
exit:
return err;
}
static int max6642_remove(struct i2c_client *client)
{
struct max6642_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &max6642_group);
kfree(data);
return 0;
}
/*
* Driver data (common to all clients)
*/
static const struct i2c_device_id max6642_id[] = {
{ "max6642", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max6642_id);
static struct i2c_driver max6642_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "max6642",
},
.probe = max6642_probe,
.remove = max6642_remove,
.id_table = max6642_id,
.detect = max6642_detect,
.address_list = normal_i2c,
};
static int __init max6642_init(void)
{
return i2c_add_driver(&max6642_driver);
}
static void __exit max6642_exit(void)
{
i2c_del_driver(&max6642_driver);
}
MODULE_AUTHOR("Per Dalen <per.dalen@appeartv.com>");
MODULE_DESCRIPTION("MAX6642 sensor driver");
MODULE_LICENSE("GPL");
module_init(max6642_init);
module_exit(max6642_exit);
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
#define MAX8688_STATUS_OT_FAULT (1 << 13) #define MAX8688_STATUS_OT_FAULT (1 << 13)
#define MAX8688_STATUS_OT_WARNING (1 << 14) #define MAX8688_STATUS_OT_WARNING (1 << 14)
static int max8688_get_status(struct i2c_client *client, int page, int reg) static int max8688_read_byte_data(struct i2c_client *client, int page, int reg)
{ {
int ret = 0; int ret = 0;
int mfg_status; int mfg_status;
...@@ -110,7 +110,7 @@ static struct pmbus_driver_info max8688_info = { ...@@ -110,7 +110,7 @@ static struct pmbus_driver_info max8688_info = {
.func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP
| PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
| PMBUS_HAVE_STATUS_TEMP, | PMBUS_HAVE_STATUS_TEMP,
.get_status = max8688_get_status, .read_byte_data = max8688_read_byte_data,
}; };
static int max8688_probe(struct i2c_client *client, static int max8688_probe(struct i2c_client *client,
......
/*
* pkgtemp.c - Linux kernel module for processor package hardware monitoring
*
* Copyright (C) 2010 Fenghua Yu <fenghua.yu@intel.com>
*
* Inspired from many hwmon drivers especially coretemp.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/hwmon.h>
#include <linux/sysfs.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/cpu.h>
#include <asm/msr.h>
#include <asm/processor.h>
#include <asm/smp.h>
#define DRVNAME "pkgtemp"
enum { SHOW_TEMP, SHOW_TJMAX, SHOW_TTARGET, SHOW_LABEL, SHOW_NAME };
/*
* Functions declaration
*/
static struct pkgtemp_data *pkgtemp_update_device(struct device *dev);
struct pkgtemp_data {
struct device *hwmon_dev;
struct mutex update_lock;
const char *name;
u32 id;
u16 phys_proc_id;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
int temp;
int tjmax;
int ttarget;
u8 alarm;
};
/*
* Sysfs stuff
*/
static ssize_t show_name(struct device *dev, struct device_attribute
*devattr, char *buf)
{
int ret;
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct pkgtemp_data *data = dev_get_drvdata(dev);
if (attr->index == SHOW_NAME)
ret = sprintf(buf, "%s\n", data->name);
else /* show label */
ret = sprintf(buf, "physical id %d\n",
data->phys_proc_id);
return ret;
}
static ssize_t show_alarm(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pkgtemp_data *data = pkgtemp_update_device(dev);
/* read the Out-of-spec log, never clear */
return sprintf(buf, "%d\n", data->alarm);
}
static ssize_t show_temp(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct pkgtemp_data *data = pkgtemp_update_device(dev);
int err = 0;
if (attr->index == SHOW_TEMP)
err = data->valid ? sprintf(buf, "%d\n", data->temp) : -EAGAIN;
else if (attr->index == SHOW_TJMAX)
err = sprintf(buf, "%d\n", data->tjmax);
else
err = sprintf(buf, "%d\n", data->ttarget);
return err;
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, SHOW_TEMP);
static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, NULL, SHOW_TJMAX);
static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, NULL, SHOW_TTARGET);
static DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL);
static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL);
static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, SHOW_NAME);
static struct attribute *pkgtemp_attributes[] = {
&sensor_dev_attr_name.dev_attr.attr,
&sensor_dev_attr_temp1_label.dev_attr.attr,
&dev_attr_temp1_crit_alarm.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_crit.dev_attr.attr,
NULL
};
static const struct attribute_group pkgtemp_group = {
.attrs = pkgtemp_attributes,
};
static struct pkgtemp_data *pkgtemp_update_device(struct device *dev)
{
struct pkgtemp_data *data = dev_get_drvdata(dev);
unsigned int cpu;
int err;
mutex_lock(&data->update_lock);
if (!data->valid || time_after(jiffies, data->last_updated + HZ)) {
u32 eax, edx;
data->valid = 0;
cpu = data->id;
err = rdmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_STATUS,
&eax, &edx);
if (!err) {
data->alarm = (eax >> 5) & 1;
data->temp = data->tjmax - (((eax >> 16)
& 0x7f) * 1000);
data->valid = 1;
} else
dev_dbg(dev, "Temperature data invalid (0x%x)\n", eax);
data->last_updated = jiffies;
}
mutex_unlock(&data->update_lock);
return data;
}
static int get_tjmax(int cpu, struct device *dev)
{
int default_tjmax = 100000;
int err;
u32 eax, edx;
u32 val;
/* IA32_TEMPERATURE_TARGET contains the TjMax value */
err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
if (!err) {
val = (eax >> 16) & 0xff;
if ((val > 80) && (val < 120)) {
dev_info(dev, "TjMax is %d C.\n", val);
return val * 1000;
}
}
dev_warn(dev, "Unable to read TjMax from CPU.\n");
return default_tjmax;
}
static int __devinit pkgtemp_probe(struct platform_device *pdev)
{
struct pkgtemp_data *data;
int err;
u32 eax, edx;
#ifdef CONFIG_SMP
struct cpuinfo_x86 *c = &cpu_data(pdev->id);
#endif
data = kzalloc(sizeof(struct pkgtemp_data), GFP_KERNEL);
if (!data) {
err = -ENOMEM;
dev_err(&pdev->dev, "Out of memory\n");
goto exit;
}
data->id = pdev->id;
#ifdef CONFIG_SMP
data->phys_proc_id = c->phys_proc_id;
#endif
data->name = "pkgtemp";
mutex_init(&data->update_lock);
/* test if we can access the THERM_STATUS MSR */
err = rdmsr_safe_on_cpu(data->id, MSR_IA32_PACKAGE_THERM_STATUS,
&eax, &edx);
if (err) {
dev_err(&pdev->dev,
"Unable to access THERM_STATUS MSR, giving up\n");
goto exit_free;
}
data->tjmax = get_tjmax(data->id, &pdev->dev);
platform_set_drvdata(pdev, data);
err = rdmsr_safe_on_cpu(data->id, MSR_IA32_TEMPERATURE_TARGET,
&eax, &edx);
if (err) {
dev_warn(&pdev->dev, "Unable to read"
" IA32_TEMPERATURE_TARGET MSR\n");
} else {
data->ttarget = data->tjmax - (((eax >> 8) & 0xff) * 1000);
err = device_create_file(&pdev->dev,
&sensor_dev_attr_temp1_max.dev_attr);
if (err)
goto exit_free;
}
err = sysfs_create_group(&pdev->dev.kobj, &pkgtemp_group);
if (err)
goto exit_dev;
data->hwmon_dev = hwmon_device_register(&pdev->dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
dev_err(&pdev->dev, "Class registration failed (%d)\n",
err);
goto exit_class;
}
return 0;
exit_class:
sysfs_remove_group(&pdev->dev.kobj, &pkgtemp_group);
exit_dev:
device_remove_file(&pdev->dev, &sensor_dev_attr_temp1_max.dev_attr);
exit_free:
kfree(data);
exit:
return err;
}
static int __devexit pkgtemp_remove(struct platform_device *pdev)
{
struct pkgtemp_data *data = platform_get_drvdata(pdev);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&pdev->dev.kobj, &pkgtemp_group);
device_remove_file(&pdev->dev, &sensor_dev_attr_temp1_max.dev_attr);
platform_set_drvdata(pdev, NULL);
kfree(data);
return 0;
}
static struct platform_driver pkgtemp_driver = {
.driver = {
.owner = THIS_MODULE,
.name = DRVNAME,
},
.probe = pkgtemp_probe,
.remove = __devexit_p(pkgtemp_remove),
};
struct pdev_entry {
struct list_head list;
struct platform_device *pdev;
unsigned int cpu;
#ifdef CONFIG_SMP
u16 phys_proc_id;
#endif
};
static LIST_HEAD(pdev_list);
static DEFINE_MUTEX(pdev_list_mutex);
static int __cpuinit pkgtemp_device_add(unsigned int cpu)
{
int err;
struct platform_device *pdev;
struct pdev_entry *pdev_entry;
struct cpuinfo_x86 *c = &cpu_data(cpu);
if (!cpu_has(c, X86_FEATURE_PTS))
return 0;
mutex_lock(&pdev_list_mutex);
#ifdef CONFIG_SMP
/* Only keep the first entry in each package */
list_for_each_entry(pdev_entry, &pdev_list, list) {
if (c->phys_proc_id == pdev_entry->phys_proc_id) {
err = 0; /* Not an error */
goto exit;
}
}
#endif
pdev = platform_device_alloc(DRVNAME, cpu);
if (!pdev) {
err = -ENOMEM;
pr_err("Device allocation failed\n");
goto exit;
}
pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL);
if (!pdev_entry) {
err = -ENOMEM;
goto exit_device_put;
}
err = platform_device_add(pdev);
if (err) {
pr_err("Device addition failed (%d)\n", err);
goto exit_device_free;
}
#ifdef CONFIG_SMP
pdev_entry->phys_proc_id = c->phys_proc_id;
#endif
pdev_entry->pdev = pdev;
pdev_entry->cpu = cpu;
list_add_tail(&pdev_entry->list, &pdev_list);
mutex_unlock(&pdev_list_mutex);
return 0;
exit_device_free:
kfree(pdev_entry);
exit_device_put:
platform_device_put(pdev);
exit:
mutex_unlock(&pdev_list_mutex);
return err;
}
static void __cpuinit pkgtemp_device_remove(unsigned int cpu)
{
struct pdev_entry *p;
unsigned int i;
int err;
mutex_lock(&pdev_list_mutex);
list_for_each_entry(p, &pdev_list, list) {
if (p->cpu != cpu)
continue;
platform_device_unregister(p->pdev);
list_del(&p->list);
mutex_unlock(&pdev_list_mutex);
kfree(p);
for_each_cpu(i, cpu_core_mask(cpu)) {
if (i != cpu) {
err = pkgtemp_device_add(i);
if (!err)
break;
}
}
return;
}
mutex_unlock(&pdev_list_mutex);
}
static int __cpuinit pkgtemp_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
unsigned int cpu = (unsigned long) hcpu;
switch (action) {
case CPU_ONLINE:
case CPU_DOWN_FAILED:
pkgtemp_device_add(cpu);
break;
case CPU_DOWN_PREPARE:
pkgtemp_device_remove(cpu);
break;
}
return NOTIFY_OK;
}
static struct notifier_block pkgtemp_cpu_notifier __refdata = {
.notifier_call = pkgtemp_cpu_callback,
};
static int __init pkgtemp_init(void)
{
int i, err = -ENODEV;
/* quick check if we run Intel */
if (cpu_data(0).x86_vendor != X86_VENDOR_INTEL)
goto exit;
err = platform_driver_register(&pkgtemp_driver);
if (err)
goto exit;
for_each_online_cpu(i)
pkgtemp_device_add(i);
#ifndef CONFIG_HOTPLUG_CPU
if (list_empty(&pdev_list)) {
err = -ENODEV;
goto exit_driver_unreg;
}
#endif
register_hotcpu_notifier(&pkgtemp_cpu_notifier);
return 0;
#ifndef CONFIG_HOTPLUG_CPU
exit_driver_unreg:
platform_driver_unregister(&pkgtemp_driver);
#endif
exit:
return err;
}
static void __exit pkgtemp_exit(void)
{
struct pdev_entry *p, *n;
unregister_hotcpu_notifier(&pkgtemp_cpu_notifier);
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
platform_device_unregister(p->pdev);
list_del(&p->list);
kfree(p);
}
mutex_unlock(&pdev_list_mutex);
platform_driver_unregister(&pkgtemp_driver);
}
MODULE_AUTHOR("Fenghua Yu <fenghua.yu@intel.com>");
MODULE_DESCRIPTION("Intel processor package temperature monitor");
MODULE_LICENSE("GPL");
module_init(pkgtemp_init)
module_exit(pkgtemp_exit)
...@@ -281,13 +281,11 @@ struct pmbus_driver_info { ...@@ -281,13 +281,11 @@ struct pmbus_driver_info {
u32 func[PMBUS_PAGES]; /* Functionality, per page */ u32 func[PMBUS_PAGES]; /* Functionality, per page */
/* /*
* The get_status function maps manufacturing specific status values * The following functions map manufacturing specific register values
* into PMBus standard status values. * to PMBus standard register values. Specify only if mapping is
* This function is optional and only necessary if chip specific status * necessary.
* register values have to be mapped into standard PMBus status register
* values.
*/ */
int (*get_status)(struct i2c_client *client, int page, int reg); int (*read_byte_data)(struct i2c_client *client, int page, int reg);
/* /*
* The identify function determines supported PMBus functionality. * The identify function determines supported PMBus functionality.
* This function is only necessary if a chip driver supports multiple * This function is only necessary if a chip driver supports multiple
......
...@@ -270,18 +270,22 @@ const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client) ...@@ -270,18 +270,22 @@ const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client)
} }
EXPORT_SYMBOL_GPL(pmbus_get_driver_info); EXPORT_SYMBOL_GPL(pmbus_get_driver_info);
static int pmbus_get_status(struct i2c_client *client, int page, int reg) /*
* _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); struct pmbus_data *data = i2c_get_clientdata(client);
const struct pmbus_driver_info *info = data->info; const struct pmbus_driver_info *info = data->info;
int status; int status;
if (info->get_status) { if (info->read_byte_data) {
status = info->get_status(client, page, reg); status = info->read_byte_data(client, page, reg);
if (status != -ENODATA) if (status != -ENODATA)
return status; return status;
} }
return pmbus_read_byte_data(client, page, reg); return pmbus_read_byte_data(client, page, reg);
} }
static struct pmbus_data *pmbus_update_device(struct device *dev) static struct pmbus_data *pmbus_update_device(struct device *dev)
...@@ -302,38 +306,41 @@ static struct pmbus_data *pmbus_update_device(struct device *dev) ...@@ -302,38 +306,41 @@ static struct pmbus_data *pmbus_update_device(struct device *dev)
if (!(info->func[i] & PMBUS_HAVE_STATUS_VOUT)) if (!(info->func[i] & PMBUS_HAVE_STATUS_VOUT))
continue; continue;
data->status[PB_STATUS_VOUT_BASE + i] data->status[PB_STATUS_VOUT_BASE + i]
= pmbus_get_status(client, i, PMBUS_STATUS_VOUT); = _pmbus_read_byte_data(client, i, PMBUS_STATUS_VOUT);
} }
for (i = 0; i < info->pages; i++) { for (i = 0; i < info->pages; i++) {
if (!(info->func[i] & PMBUS_HAVE_STATUS_IOUT)) if (!(info->func[i] & PMBUS_HAVE_STATUS_IOUT))
continue; continue;
data->status[PB_STATUS_IOUT_BASE + i] data->status[PB_STATUS_IOUT_BASE + i]
= pmbus_get_status(client, i, PMBUS_STATUS_IOUT); = _pmbus_read_byte_data(client, i, PMBUS_STATUS_IOUT);
} }
for (i = 0; i < info->pages; i++) { for (i = 0; i < info->pages; i++) {
if (!(info->func[i] & PMBUS_HAVE_STATUS_TEMP)) if (!(info->func[i] & PMBUS_HAVE_STATUS_TEMP))
continue; continue;
data->status[PB_STATUS_TEMP_BASE + i] data->status[PB_STATUS_TEMP_BASE + i]
= pmbus_get_status(client, i, = _pmbus_read_byte_data(client, i,
PMBUS_STATUS_TEMPERATURE); PMBUS_STATUS_TEMPERATURE);
} }
for (i = 0; i < info->pages; i++) { for (i = 0; i < info->pages; i++) {
if (!(info->func[i] & PMBUS_HAVE_STATUS_FAN12)) if (!(info->func[i] & PMBUS_HAVE_STATUS_FAN12))
continue; continue;
data->status[PB_STATUS_FAN_BASE + i] data->status[PB_STATUS_FAN_BASE + i]
= pmbus_get_status(client, i, PMBUS_STATUS_FAN_12); = _pmbus_read_byte_data(client, i,
PMBUS_STATUS_FAN_12);
} }
for (i = 0; i < info->pages; i++) { for (i = 0; i < info->pages; i++) {
if (!(info->func[i] & PMBUS_HAVE_STATUS_FAN34)) if (!(info->func[i] & PMBUS_HAVE_STATUS_FAN34))
continue; continue;
data->status[PB_STATUS_FAN34_BASE + i] data->status[PB_STATUS_FAN34_BASE + i]
= pmbus_get_status(client, i, PMBUS_STATUS_FAN_34); = _pmbus_read_byte_data(client, i,
PMBUS_STATUS_FAN_34);
} }
if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) if (info->func[0] & PMBUS_HAVE_STATUS_INPUT)
data->status[PB_STATUS_INPUT_BASE] data->status[PB_STATUS_INPUT_BASE]
= pmbus_get_status(client, 0, PMBUS_STATUS_INPUT); = _pmbus_read_byte_data(client, 0,
PMBUS_STATUS_INPUT);
for (i = 0; i < data->num_sensors; i++) { for (i = 0; i < data->num_sensors; i++) {
struct pmbus_sensor *sensor = &data->sensors[i]; struct pmbus_sensor *sensor = &data->sensors[i];
...@@ -793,53 +800,6 @@ static void pmbus_add_label(struct pmbus_data *data, ...@@ -793,53 +800,6 @@ static void pmbus_add_label(struct pmbus_data *data,
data->num_labels++; data->num_labels++;
} }
static const int pmbus_temp_registers[] = {
PMBUS_READ_TEMPERATURE_1,
PMBUS_READ_TEMPERATURE_2,
PMBUS_READ_TEMPERATURE_3
};
static const int pmbus_temp_flags[] = {
PMBUS_HAVE_TEMP,
PMBUS_HAVE_TEMP2,
PMBUS_HAVE_TEMP3
};
static const int pmbus_fan_registers[] = {
PMBUS_READ_FAN_SPEED_1,
PMBUS_READ_FAN_SPEED_2,
PMBUS_READ_FAN_SPEED_3,
PMBUS_READ_FAN_SPEED_4
};
static const int pmbus_fan_config_registers[] = {
PMBUS_FAN_CONFIG_12,
PMBUS_FAN_CONFIG_12,
PMBUS_FAN_CONFIG_34,
PMBUS_FAN_CONFIG_34
};
static const int pmbus_fan_status_registers[] = {
PMBUS_STATUS_FAN_12,
PMBUS_STATUS_FAN_12,
PMBUS_STATUS_FAN_34,
PMBUS_STATUS_FAN_34
};
static const u32 pmbus_fan_flags[] = {
PMBUS_HAVE_FAN12,
PMBUS_HAVE_FAN12,
PMBUS_HAVE_FAN34,
PMBUS_HAVE_FAN34
};
static const u32 pmbus_fan_status_flags[] = {
PMBUS_HAVE_STATUS_FAN12,
PMBUS_HAVE_STATUS_FAN12,
PMBUS_HAVE_STATUS_FAN34,
PMBUS_HAVE_STATUS_FAN34
};
/* /*
* Determine maximum number of sensors, booleans, and labels. * Determine maximum number of sensors, booleans, and labels.
* To keep things simple, only make a rough high estimate. * To keep things simple, only make a rough high estimate.
...@@ -900,499 +860,431 @@ static void pmbus_find_max_attr(struct i2c_client *client, ...@@ -900,499 +860,431 @@ static void pmbus_find_max_attr(struct i2c_client *client,
/* /*
* Search for attributes. Allocate sensors, booleans, and labels as needed. * Search for attributes. Allocate sensors, booleans, and labels as needed.
*/ */
static void pmbus_find_attributes(struct i2c_client *client,
struct pmbus_data *data)
{
const struct pmbus_driver_info *info = data->info;
int page, i0, i1, in_index;
/* /*
* Input voltage sensors * The pmbus_limit_attr structure describes a single limit attribute
*/ * and its associated alarm attribute.
in_index = 1; */
if (info->func[0] & PMBUS_HAVE_VIN) { struct pmbus_limit_attr {
bool have_alarm = false; u8 reg; /* Limit register */
const char *attr; /* Attribute name */
i0 = data->num_sensors; const char *alarm; /* Alarm attribute name */
pmbus_add_label(data, "in", in_index, "vin", 0); u32 sbit; /* Alarm attribute status bit */
pmbus_add_sensor(data, "in", "input", in_index, 0, };
PMBUS_READ_VIN, PSC_VOLTAGE_IN, true, true);
if (pmbus_check_word_register(client, 0, /*
PMBUS_VIN_UV_WARN_LIMIT)) { * The pmbus_sensor_attr structure describes one sensor attribute. This
i1 = data->num_sensors; * description includes a reference to the associated limit attributes.
pmbus_add_sensor(data, "in", "min", in_index, */
0, PMBUS_VIN_UV_WARN_LIMIT, struct pmbus_sensor_attr {
PSC_VOLTAGE_IN, false, false); u8 reg; /* sensor register */
if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) { enum pmbus_sensor_classes class;/* sensor class */
pmbus_add_boolean_reg(data, "in", "min_alarm", const char *label; /* sensor label */
in_index, bool paged; /* true if paged sensor */
PB_STATUS_INPUT_BASE, bool update; /* true if update needed */
PB_VOLTAGE_UV_WARNING); bool compare; /* true if compare function needed */
have_alarm = true; u32 func; /* sensor mask */
} u32 sfunc; /* sensor status mask */
} int sbase; /* status base register */
if (pmbus_check_word_register(client, 0, u32 gbit; /* generic status bit */
PMBUS_VIN_UV_FAULT_LIMIT)) { const struct pmbus_limit_attr *limit;/* limit registers */
i1 = data->num_sensors; int nlimit; /* # of limit registers */
pmbus_add_sensor(data, "in", "lcrit", in_index, };
0, PMBUS_VIN_UV_FAULT_LIMIT,
PSC_VOLTAGE_IN, false, false); /*
if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) { * Add a set of limit attributes and, if supported, the associated
pmbus_add_boolean_reg(data, "in", "lcrit_alarm", * alarm attributes.
in_index, */
PB_STATUS_INPUT_BASE, static bool pmbus_add_limit_attrs(struct i2c_client *client,
PB_VOLTAGE_UV_FAULT); struct pmbus_data *data,
have_alarm = true; const struct pmbus_driver_info *info,
} const char *name, int index, int page,
} int cbase,
if (pmbus_check_word_register(client, 0, const struct pmbus_sensor_attr *attr)
PMBUS_VIN_OV_WARN_LIMIT)) { {
i1 = data->num_sensors; const struct pmbus_limit_attr *l = attr->limit;
pmbus_add_sensor(data, "in", "max", in_index, int nlimit = attr->nlimit;
0, PMBUS_VIN_OV_WARN_LIMIT, bool have_alarm = false;
PSC_VOLTAGE_IN, false, false); int i, cindex;
if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) {
pmbus_add_boolean_reg(data, "in", "max_alarm", for (i = 0; i < nlimit; i++) {
in_index, if (pmbus_check_word_register(client, page, l->reg)) {
PB_STATUS_INPUT_BASE, cindex = data->num_sensors;
PB_VOLTAGE_OV_WARNING); pmbus_add_sensor(data, name, l->attr, index, page,
have_alarm = true; l->reg, attr->class, attr->update,
} false);
} if (info->func[page] & attr->sfunc) {
if (pmbus_check_word_register(client, 0, if (attr->compare) {
PMBUS_VIN_OV_FAULT_LIMIT)) { pmbus_add_boolean_cmp(data, name,
i1 = data->num_sensors; l->alarm, index,
pmbus_add_sensor(data, "in", "crit", in_index, cbase, cindex,
0, PMBUS_VIN_OV_FAULT_LIMIT, attr->sbase + page, l->sbit);
PSC_VOLTAGE_IN, false, false); } else {
if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) { pmbus_add_boolean_reg(data, name,
pmbus_add_boolean_reg(data, "in", "crit_alarm", l->alarm, index,
in_index, attr->sbase + page, l->sbit);
PB_STATUS_INPUT_BASE, }
PB_VOLTAGE_OV_FAULT);
have_alarm = true; have_alarm = true;
} }
} }
/* l++;
* Add generic alarm attribute only if there are no individual
* attributes.
*/
if (!have_alarm)
pmbus_add_boolean_reg(data, "in", "alarm",
in_index,
PB_STATUS_BASE,
PB_STATUS_VIN_UV);
in_index++;
}
if (info->func[0] & PMBUS_HAVE_VCAP) {
pmbus_add_label(data, "in", in_index, "vcap", 0);
pmbus_add_sensor(data, "in", "input", in_index, 0,
PMBUS_READ_VCAP, PSC_VOLTAGE_IN, true, true);
in_index++;
} }
return have_alarm;
}
/* static void pmbus_add_sensor_attrs_one(struct i2c_client *client,
* Output voltage sensors struct pmbus_data *data,
*/ const struct pmbus_driver_info *info,
for (page = 0; page < info->pages; page++) { const char *name,
bool have_alarm = false; int index, int page,
const struct pmbus_sensor_attr *attr)
if (!(info->func[page] & PMBUS_HAVE_VOUT)) {
continue; bool have_alarm;
int cbase = data->num_sensors;
i0 = data->num_sensors;
pmbus_add_label(data, "in", in_index, "vout", page + 1); if (attr->label)
pmbus_add_sensor(data, "in", "input", in_index, page, pmbus_add_label(data, name, index, attr->label,
PMBUS_READ_VOUT, PSC_VOLTAGE_OUT, true, true); attr->paged ? page + 1 : 0);
if (pmbus_check_word_register(client, page, pmbus_add_sensor(data, name, "input", index, page, attr->reg,
PMBUS_VOUT_UV_WARN_LIMIT)) { attr->class, true, true);
i1 = data->num_sensors; if (attr->sfunc) {
pmbus_add_sensor(data, "in", "min", in_index, page, have_alarm = pmbus_add_limit_attrs(client, data, info, name,
PMBUS_VOUT_UV_WARN_LIMIT, index, page, cbase, attr);
PSC_VOLTAGE_OUT, false, false);
if (info->func[page] & PMBUS_HAVE_STATUS_VOUT) {
pmbus_add_boolean_reg(data, "in", "min_alarm",
in_index,
PB_STATUS_VOUT_BASE +
page,
PB_VOLTAGE_UV_WARNING);
have_alarm = true;
}
}
if (pmbus_check_word_register(client, page,
PMBUS_VOUT_UV_FAULT_LIMIT)) {
i1 = data->num_sensors;
pmbus_add_sensor(data, "in", "lcrit", in_index, page,
PMBUS_VOUT_UV_FAULT_LIMIT,
PSC_VOLTAGE_OUT, false, false);
if (info->func[page] & PMBUS_HAVE_STATUS_VOUT) {
pmbus_add_boolean_reg(data, "in", "lcrit_alarm",
in_index,
PB_STATUS_VOUT_BASE +
page,
PB_VOLTAGE_UV_FAULT);
have_alarm = true;
}
}
if (pmbus_check_word_register(client, page,
PMBUS_VOUT_OV_WARN_LIMIT)) {
i1 = data->num_sensors;
pmbus_add_sensor(data, "in", "max", in_index, page,
PMBUS_VOUT_OV_WARN_LIMIT,
PSC_VOLTAGE_OUT, false, false);
if (info->func[page] & PMBUS_HAVE_STATUS_VOUT) {
pmbus_add_boolean_reg(data, "in", "max_alarm",
in_index,
PB_STATUS_VOUT_BASE +
page,
PB_VOLTAGE_OV_WARNING);
have_alarm = true;
}
}
if (pmbus_check_word_register(client, page,
PMBUS_VOUT_OV_FAULT_LIMIT)) {
i1 = data->num_sensors;
pmbus_add_sensor(data, "in", "crit", in_index, page,
PMBUS_VOUT_OV_FAULT_LIMIT,
PSC_VOLTAGE_OUT, false, false);
if (info->func[page] & PMBUS_HAVE_STATUS_VOUT) {
pmbus_add_boolean_reg(data, "in", "crit_alarm",
in_index,
PB_STATUS_VOUT_BASE +
page,
PB_VOLTAGE_OV_FAULT);
have_alarm = true;
}
}
/* /*
* Add generic alarm attribute only if there are no individual * Add generic alarm attribute only if there are no individual
* attributes. * alarm attributes, and if there is a global alarm bit.
*/ */
if (!have_alarm) if (!have_alarm && attr->gbit)
pmbus_add_boolean_reg(data, "in", "alarm", pmbus_add_boolean_reg(data, name, "alarm", index,
in_index,
PB_STATUS_BASE + page, PB_STATUS_BASE + page,
PB_STATUS_VOUT_OV); attr->gbit);
in_index++;
} }
}
/* static void pmbus_add_sensor_attrs(struct i2c_client *client,
* Current sensors struct pmbus_data *data,
*/ const char *name,
const struct pmbus_sensor_attr *attrs,
int nattrs)
{
const struct pmbus_driver_info *info = data->info;
int index, i;
/* index = 1;
* Input current sensors for (i = 0; i < nattrs; i++) {
*/ int page, pages;
in_index = 1;
if (info->func[0] & PMBUS_HAVE_IIN) { pages = attrs->paged ? info->pages : 1;
i0 = data->num_sensors; for (page = 0; page < pages; page++) {
pmbus_add_label(data, "curr", in_index, "iin", 0); if (!(info->func[page] & attrs->func))
pmbus_add_sensor(data, "curr", "input", in_index, 0, continue;
PMBUS_READ_IIN, PSC_CURRENT_IN, true, true); pmbus_add_sensor_attrs_one(client, data, info, name,
if (pmbus_check_word_register(client, 0, index, page, attrs);
PMBUS_IIN_OC_WARN_LIMIT)) { index++;
i1 = data->num_sensors;
pmbus_add_sensor(data, "curr", "max", in_index,
0, PMBUS_IIN_OC_WARN_LIMIT,
PSC_CURRENT_IN, false, false);
if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) {
pmbus_add_boolean_reg(data, "curr", "max_alarm",
in_index,
PB_STATUS_INPUT_BASE,
PB_IIN_OC_WARNING);
}
}
if (pmbus_check_word_register(client, 0,
PMBUS_IIN_OC_FAULT_LIMIT)) {
i1 = data->num_sensors;
pmbus_add_sensor(data, "curr", "crit", in_index,
0, PMBUS_IIN_OC_FAULT_LIMIT,
PSC_CURRENT_IN, false, false);
if (info->func[0] & PMBUS_HAVE_STATUS_INPUT)
pmbus_add_boolean_reg(data, "curr",
"crit_alarm",
in_index,
PB_STATUS_INPUT_BASE,
PB_IIN_OC_FAULT);
} }
in_index++; attrs++;
} }
}
/* static const struct pmbus_limit_attr vin_limit_attrs[] = {
* Output current sensors {
*/ .reg = PMBUS_VIN_UV_WARN_LIMIT,
for (page = 0; page < info->pages; page++) { .attr = "min",
bool have_alarm = false; .alarm = "min_alarm",
.sbit = PB_VOLTAGE_UV_WARNING,
if (!(info->func[page] & PMBUS_HAVE_IOUT)) }, {
continue; .reg = PMBUS_VIN_UV_FAULT_LIMIT,
.attr = "lcrit",
i0 = data->num_sensors; .alarm = "lcrit_alarm",
pmbus_add_label(data, "curr", in_index, "iout", page + 1); .sbit = PB_VOLTAGE_UV_FAULT,
pmbus_add_sensor(data, "curr", "input", in_index, page, }, {
PMBUS_READ_IOUT, PSC_CURRENT_OUT, true, true); .reg = PMBUS_VIN_OV_WARN_LIMIT,
if (pmbus_check_word_register(client, page, .attr = "max",
PMBUS_IOUT_OC_WARN_LIMIT)) { .alarm = "max_alarm",
i1 = data->num_sensors; .sbit = PB_VOLTAGE_OV_WARNING,
pmbus_add_sensor(data, "curr", "max", in_index, page, }, {
PMBUS_IOUT_OC_WARN_LIMIT, .reg = PMBUS_VIN_OV_FAULT_LIMIT,
PSC_CURRENT_OUT, false, false); .attr = "crit",
if (info->func[page] & PMBUS_HAVE_STATUS_IOUT) { .alarm = "crit_alarm",
pmbus_add_boolean_reg(data, "curr", "max_alarm", .sbit = PB_VOLTAGE_OV_FAULT,
in_index, },
PB_STATUS_IOUT_BASE + };
page, PB_IOUT_OC_WARNING);
have_alarm = true; static const struct pmbus_limit_attr vout_limit_attrs[] = {
} {
} .reg = PMBUS_VOUT_UV_WARN_LIMIT,
if (pmbus_check_word_register(client, page, .attr = "min",
PMBUS_IOUT_UC_FAULT_LIMIT)) { .alarm = "min_alarm",
i1 = data->num_sensors; .sbit = PB_VOLTAGE_UV_WARNING,
pmbus_add_sensor(data, "curr", "lcrit", in_index, page, }, {
PMBUS_IOUT_UC_FAULT_LIMIT, .reg = PMBUS_VOUT_UV_FAULT_LIMIT,
PSC_CURRENT_OUT, false, false); .attr = "lcrit",
if (info->func[page] & PMBUS_HAVE_STATUS_IOUT) { .alarm = "lcrit_alarm",
pmbus_add_boolean_reg(data, "curr", .sbit = PB_VOLTAGE_UV_FAULT,
"lcrit_alarm", }, {
in_index, .reg = PMBUS_VOUT_OV_WARN_LIMIT,
PB_STATUS_IOUT_BASE + .attr = "max",
page, PB_IOUT_UC_FAULT); .alarm = "max_alarm",
have_alarm = true; .sbit = PB_VOLTAGE_OV_WARNING,
} }, {
} .reg = PMBUS_VOUT_OV_FAULT_LIMIT,
if (pmbus_check_word_register(client, page, .attr = "crit",
PMBUS_IOUT_OC_FAULT_LIMIT)) { .alarm = "crit_alarm",
i1 = data->num_sensors; .sbit = PB_VOLTAGE_OV_FAULT,
pmbus_add_sensor(data, "curr", "crit", in_index, page,
PMBUS_IOUT_OC_FAULT_LIMIT,
PSC_CURRENT_OUT, false, false);
if (info->func[page] & PMBUS_HAVE_STATUS_IOUT) {
pmbus_add_boolean_reg(data, "curr",
"crit_alarm",
in_index,
PB_STATUS_IOUT_BASE +
page, PB_IOUT_OC_FAULT);
have_alarm = true;
}
}
/*
* Add generic alarm attribute only if there are no individual
* attributes.
*/
if (!have_alarm)
pmbus_add_boolean_reg(data, "curr", "alarm",
in_index,
PB_STATUS_BASE + page,
PB_STATUS_IOUT_OC);
in_index++;
} }
};
/* static const struct pmbus_sensor_attr voltage_attributes[] = {
* Power sensors {
*/ .reg = PMBUS_READ_VIN,
/* .class = PSC_VOLTAGE_IN,
* Input Power sensors .label = "vin",
*/ .func = PMBUS_HAVE_VIN,
in_index = 1; .sfunc = PMBUS_HAVE_STATUS_INPUT,
if (info->func[0] & PMBUS_HAVE_PIN) { .sbase = PB_STATUS_INPUT_BASE,
i0 = data->num_sensors; .gbit = PB_STATUS_VIN_UV,
pmbus_add_label(data, "power", in_index, "pin", 0); .limit = vin_limit_attrs,
pmbus_add_sensor(data, "power", "input", in_index, .nlimit = ARRAY_SIZE(vin_limit_attrs),
0, PMBUS_READ_PIN, PSC_POWER, true, true); }, {
if (pmbus_check_word_register(client, 0, .reg = PMBUS_READ_VCAP,
PMBUS_PIN_OP_WARN_LIMIT)) { .class = PSC_VOLTAGE_IN,
i1 = data->num_sensors; .label = "vcap",
pmbus_add_sensor(data, "power", "max", in_index, .func = PMBUS_HAVE_VCAP,
0, PMBUS_PIN_OP_WARN_LIMIT, PSC_POWER, }, {
false, false); .reg = PMBUS_READ_VOUT,
if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) .class = PSC_VOLTAGE_OUT,
pmbus_add_boolean_reg(data, "power", .label = "vout",
"alarm", .paged = true,
in_index, .func = PMBUS_HAVE_VOUT,
PB_STATUS_INPUT_BASE, .sfunc = PMBUS_HAVE_STATUS_VOUT,
PB_PIN_OP_WARNING); .sbase = PB_STATUS_VOUT_BASE,
} .gbit = PB_STATUS_VOUT_OV,
in_index++; .limit = vout_limit_attrs,
.nlimit = ARRAY_SIZE(vout_limit_attrs),
} }
};
/* /* Current attributes */
* Output Power sensors
*/ static const struct pmbus_limit_attr iin_limit_attrs[] = {
for (page = 0; page < info->pages; page++) { {
bool need_alarm = false; .reg = PMBUS_IIN_OC_WARN_LIMIT,
.attr = "max",
.alarm = "max_alarm",
.sbit = PB_IIN_OC_WARNING,
}, {
.reg = PMBUS_IIN_OC_FAULT_LIMIT,
.attr = "crit",
.alarm = "crit_alarm",
.sbit = PB_IIN_OC_FAULT,
}
};
if (!(info->func[page] & PMBUS_HAVE_POUT)) static const struct pmbus_limit_attr iout_limit_attrs[] = {
continue; {
.reg = PMBUS_IOUT_OC_WARN_LIMIT,
.attr = "max",
.alarm = "max_alarm",
.sbit = PB_IOUT_OC_WARNING,
}, {
.reg = PMBUS_IOUT_UC_FAULT_LIMIT,
.attr = "lcrit",
.alarm = "lcrit_alarm",
.sbit = PB_IOUT_UC_FAULT,
}, {
.reg = PMBUS_IOUT_OC_FAULT_LIMIT,
.attr = "crit",
.alarm = "crit_alarm",
.sbit = PB_IOUT_OC_FAULT,
}
};
i0 = data->num_sensors; static const struct pmbus_sensor_attr current_attributes[] = {
pmbus_add_label(data, "power", in_index, "pout", page + 1); {
pmbus_add_sensor(data, "power", "input", in_index, page, .reg = PMBUS_READ_IIN,
PMBUS_READ_POUT, PSC_POWER, true, true); .class = PSC_CURRENT_IN,
/* .label = "iin",
* Per hwmon sysfs API, power_cap is to be used to limit output .func = PMBUS_HAVE_IIN,
* power. .sfunc = PMBUS_HAVE_STATUS_INPUT,
* We have two registers related to maximum output power, .sbase = PB_STATUS_INPUT_BASE,
* PMBUS_POUT_MAX and PMBUS_POUT_OP_WARN_LIMIT. .limit = iin_limit_attrs,
* PMBUS_POUT_MAX matches the powerX_cap attribute definition. .nlimit = ARRAY_SIZE(iin_limit_attrs),
* There is no attribute in the API to match }, {
* PMBUS_POUT_OP_WARN_LIMIT. We use powerX_max for now. .reg = PMBUS_READ_IOUT,
*/ .class = PSC_CURRENT_OUT,
if (pmbus_check_word_register(client, page, PMBUS_POUT_MAX)) { .label = "iout",
i1 = data->num_sensors; .paged = true,
pmbus_add_sensor(data, "power", "cap", in_index, page, .func = PMBUS_HAVE_IOUT,
PMBUS_POUT_MAX, PSC_POWER, .sfunc = PMBUS_HAVE_STATUS_IOUT,
false, false); .sbase = PB_STATUS_IOUT_BASE,
need_alarm = true; .gbit = PB_STATUS_IOUT_OC,
} .limit = iout_limit_attrs,
if (pmbus_check_word_register(client, page, .nlimit = ARRAY_SIZE(iout_limit_attrs),
PMBUS_POUT_OP_WARN_LIMIT)) {
i1 = data->num_sensors;
pmbus_add_sensor(data, "power", "max", in_index, page,
PMBUS_POUT_OP_WARN_LIMIT, PSC_POWER,
false, false);
need_alarm = true;
}
if (need_alarm && (info->func[page] & PMBUS_HAVE_STATUS_IOUT))
pmbus_add_boolean_reg(data, "power", "alarm",
in_index,
PB_STATUS_IOUT_BASE + page,
PB_POUT_OP_WARNING
| PB_POWER_LIMITING);
if (pmbus_check_word_register(client, page,
PMBUS_POUT_OP_FAULT_LIMIT)) {
i1 = data->num_sensors;
pmbus_add_sensor(data, "power", "crit", in_index, page,
PMBUS_POUT_OP_FAULT_LIMIT, PSC_POWER,
false, false);
if (info->func[page] & PMBUS_HAVE_STATUS_IOUT)
pmbus_add_boolean_reg(data, "power",
"crit_alarm",
in_index,
PB_STATUS_IOUT_BASE
+ page,
PB_POUT_OP_FAULT);
}
in_index++;
} }
};
/* /* Power attributes */
* Temperature sensors
*/
in_index = 1;
for (page = 0; page < info->pages; page++) {
int t;
for (t = 0; t < ARRAY_SIZE(pmbus_temp_registers); t++) { static const struct pmbus_limit_attr pin_limit_attrs[] = {
bool have_alarm = false; {
.reg = PMBUS_PIN_OP_WARN_LIMIT,
.attr = "max",
.alarm = "alarm",
.sbit = PB_PIN_OP_WARNING,
}
};
/* static const struct pmbus_limit_attr pout_limit_attrs[] = {
* A PMBus chip may support any combination of {
* temperature registers on any page. So we can not .reg = PMBUS_POUT_MAX,
* abort after a failure to detect a register, but have .attr = "cap",
* to continue checking for all registers on all pages. .alarm = "cap_alarm",
*/ .sbit = PB_POWER_LIMITING,
if (!(info->func[page] & pmbus_temp_flags[t])) }, {
continue; .reg = PMBUS_POUT_OP_WARN_LIMIT,
.attr = "max",
.alarm = "max_alarm",
.sbit = PB_POUT_OP_WARNING,
}, {
.reg = PMBUS_POUT_OP_FAULT_LIMIT,
.attr = "crit",
.alarm = "crit_alarm",
.sbit = PB_POUT_OP_FAULT,
}
};
if (!pmbus_check_word_register static const struct pmbus_sensor_attr power_attributes[] = {
(client, page, pmbus_temp_registers[t])) {
continue; .reg = PMBUS_READ_PIN,
.class = PSC_POWER,
.label = "pin",
.func = PMBUS_HAVE_PIN,
.sfunc = PMBUS_HAVE_STATUS_INPUT,
.sbase = PB_STATUS_INPUT_BASE,
.limit = pin_limit_attrs,
.nlimit = ARRAY_SIZE(pin_limit_attrs),
}, {
.reg = PMBUS_READ_POUT,
.class = PSC_POWER,
.label = "pout",
.paged = true,
.func = PMBUS_HAVE_POUT,
.sfunc = PMBUS_HAVE_STATUS_IOUT,
.sbase = PB_STATUS_IOUT_BASE,
.limit = pout_limit_attrs,
.nlimit = ARRAY_SIZE(pout_limit_attrs),
}
};
i0 = data->num_sensors; /* Temperature atributes */
pmbus_add_sensor(data, "temp", "input", in_index, page,
pmbus_temp_registers[t], static const struct pmbus_limit_attr temp_limit_attrs[] = {
PSC_TEMPERATURE, true, true); {
.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,
}
};
/* static const struct pmbus_sensor_attr temp_attributes[] = {
* PMBus provides only one status register for TEMP1-3. {
* Thus, we can not use the status register to determine .reg = PMBUS_READ_TEMPERATURE_1,
* which of the three sensors actually caused an alarm. .class = PSC_TEMPERATURE,
* Always compare current temperature against the limit .paged = true,
* registers to determine alarm conditions for a .update = true,
* specific sensor. .compare = true,
* .func = PMBUS_HAVE_TEMP,
* Since there is only one set of limit registers for .sfunc = PMBUS_HAVE_STATUS_TEMP,
* up to three temperature sensors, we need to update .sbase = PB_STATUS_TEMP_BASE,
* all limit registers after the limit was changed for .gbit = PB_STATUS_TEMPERATURE,
* one of the sensors. This ensures that correct limits .limit = temp_limit_attrs,
* are reported for all temperature sensors. .nlimit = ARRAY_SIZE(temp_limit_attrs),
*/ }, {
if (pmbus_check_word_register .reg = PMBUS_READ_TEMPERATURE_2,
(client, page, PMBUS_UT_WARN_LIMIT)) { .class = PSC_TEMPERATURE,
i1 = data->num_sensors; .paged = true,
pmbus_add_sensor(data, "temp", "min", in_index, .update = true,
page, PMBUS_UT_WARN_LIMIT, .compare = true,
PSC_TEMPERATURE, true, false); .func = PMBUS_HAVE_TEMP2,
if (info->func[page] & PMBUS_HAVE_STATUS_TEMP) { .sfunc = PMBUS_HAVE_STATUS_TEMP,
pmbus_add_boolean_cmp(data, "temp", .sbase = PB_STATUS_TEMP_BASE,
"min_alarm", in_index, i1, i0, .gbit = PB_STATUS_TEMPERATURE,
PB_STATUS_TEMP_BASE + page, .limit = temp_limit_attrs,
PB_TEMP_UT_WARNING); .nlimit = ARRAY_SIZE(temp_limit_attrs),
have_alarm = true; }, {
} .reg = PMBUS_READ_TEMPERATURE_3,
} .class = PSC_TEMPERATURE,
if (pmbus_check_word_register(client, page, .paged = true,
PMBUS_UT_FAULT_LIMIT)) { .update = true,
i1 = data->num_sensors; .compare = true,
pmbus_add_sensor(data, "temp", "lcrit", .func = PMBUS_HAVE_TEMP3,
in_index, page, .sfunc = PMBUS_HAVE_STATUS_TEMP,
PMBUS_UT_FAULT_LIMIT, .sbase = PB_STATUS_TEMP_BASE,
PSC_TEMPERATURE, true, false); .gbit = PB_STATUS_TEMPERATURE,
if (info->func[page] & PMBUS_HAVE_STATUS_TEMP) { .limit = temp_limit_attrs,
pmbus_add_boolean_cmp(data, "temp", .nlimit = ARRAY_SIZE(temp_limit_attrs),
"lcrit_alarm", in_index, i1, i0,
PB_STATUS_TEMP_BASE + page,
PB_TEMP_UT_FAULT);
have_alarm = true;
}
}
if (pmbus_check_word_register
(client, page, PMBUS_OT_WARN_LIMIT)) {
i1 = data->num_sensors;
pmbus_add_sensor(data, "temp", "max", in_index,
page, PMBUS_OT_WARN_LIMIT,
PSC_TEMPERATURE, true, false);
if (info->func[page] & PMBUS_HAVE_STATUS_TEMP) {
pmbus_add_boolean_cmp(data, "temp",
"max_alarm", in_index, i0, i1,
PB_STATUS_TEMP_BASE + page,
PB_TEMP_OT_WARNING);
have_alarm = true;
}
}
if (pmbus_check_word_register(client, page,
PMBUS_OT_FAULT_LIMIT)) {
i1 = data->num_sensors;
pmbus_add_sensor(data, "temp", "crit", in_index,
page, PMBUS_OT_FAULT_LIMIT,
PSC_TEMPERATURE, true, false);
if (info->func[page] & PMBUS_HAVE_STATUS_TEMP) {
pmbus_add_boolean_cmp(data, "temp",
"crit_alarm", in_index, i0, i1,
PB_STATUS_TEMP_BASE + page,
PB_TEMP_OT_FAULT);
have_alarm = true;
}
}
/*
* Last resort - we were not able to create any alarm
* registers. Report alarm for all sensors using the
* status register temperature alarm bit.
*/
if (!have_alarm)
pmbus_add_boolean_reg(data, "temp", "alarm",
in_index,
PB_STATUS_BASE + page,
PB_STATUS_TEMPERATURE);
in_index++;
}
} }
};
static const int pmbus_fan_registers[] = {
PMBUS_READ_FAN_SPEED_1,
PMBUS_READ_FAN_SPEED_2,
PMBUS_READ_FAN_SPEED_3,
PMBUS_READ_FAN_SPEED_4
};
static const int pmbus_fan_config_registers[] = {
PMBUS_FAN_CONFIG_12,
PMBUS_FAN_CONFIG_12,
PMBUS_FAN_CONFIG_34,
PMBUS_FAN_CONFIG_34
};
static const int pmbus_fan_status_registers[] = {
PMBUS_STATUS_FAN_12,
PMBUS_STATUS_FAN_12,
PMBUS_STATUS_FAN_34,
PMBUS_STATUS_FAN_34
};
static const u32 pmbus_fan_flags[] = {
PMBUS_HAVE_FAN12,
PMBUS_HAVE_FAN12,
PMBUS_HAVE_FAN34,
PMBUS_HAVE_FAN34
};
static const u32 pmbus_fan_status_flags[] = {
PMBUS_HAVE_STATUS_FAN12,
PMBUS_HAVE_STATUS_FAN12,
PMBUS_HAVE_STATUS_FAN34,
PMBUS_HAVE_STATUS_FAN34
};
/* Fans */
static void pmbus_add_fan_attributes(struct i2c_client *client,
struct pmbus_data *data)
{
const struct pmbus_driver_info *info = data->info;
int index = 1;
int page;
/*
* Fans
*/
in_index = 1;
for (page = 0; page < info->pages; page++) { for (page = 0; page < info->pages; page++) {
int f; int f;
...@@ -1403,9 +1295,7 @@ static void pmbus_find_attributes(struct i2c_client *client, ...@@ -1403,9 +1295,7 @@ static void pmbus_find_attributes(struct i2c_client *client,
break; break;
if (!pmbus_check_word_register(client, page, if (!pmbus_check_word_register(client, page,
pmbus_fan_registers[f]) pmbus_fan_registers[f]))
|| !pmbus_check_byte_register(client, page,
pmbus_fan_config_registers[f]))
break; break;
/* /*
...@@ -1413,14 +1303,13 @@ static void pmbus_find_attributes(struct i2c_client *client, ...@@ -1413,14 +1303,13 @@ static void pmbus_find_attributes(struct i2c_client *client,
* Each fan configuration register covers multiple fans, * Each fan configuration register covers multiple fans,
* so we have to do some magic. * so we have to do some magic.
*/ */
regval = pmbus_read_byte_data(client, page, regval = _pmbus_read_byte_data(client, page,
pmbus_fan_config_registers[f]); pmbus_fan_config_registers[f]);
if (regval < 0 || if (regval < 0 ||
(!(regval & (PB_FAN_1_INSTALLED >> ((f & 1) * 4))))) (!(regval & (PB_FAN_1_INSTALLED >> ((f & 1) * 4)))))
continue; continue;
i0 = data->num_sensors; pmbus_add_sensor(data, "fan", "input", index, page,
pmbus_add_sensor(data, "fan", "input", in_index, page,
pmbus_fan_registers[f], PSC_FAN, true, pmbus_fan_registers[f], PSC_FAN, true,
true); true);
...@@ -1438,17 +1327,40 @@ static void pmbus_find_attributes(struct i2c_client *client, ...@@ -1438,17 +1327,40 @@ static void pmbus_find_attributes(struct i2c_client *client,
else else
base = PB_STATUS_FAN_BASE + page; base = PB_STATUS_FAN_BASE + page;
pmbus_add_boolean_reg(data, "fan", "alarm", pmbus_add_boolean_reg(data, "fan", "alarm",
in_index, base, index, base,
PB_FAN_FAN1_WARNING >> (f & 1)); PB_FAN_FAN1_WARNING >> (f & 1));
pmbus_add_boolean_reg(data, "fan", "fault", pmbus_add_boolean_reg(data, "fan", "fault",
in_index, base, index, base,
PB_FAN_FAN1_FAULT >> (f & 1)); PB_FAN_FAN1_FAULT >> (f & 1));
} }
in_index++; index++;
} }
} }
} }
static void pmbus_find_attributes(struct i2c_client *client,
struct pmbus_data *data)
{
/* Voltage sensors */
pmbus_add_sensor_attrs(client, data, "in", voltage_attributes,
ARRAY_SIZE(voltage_attributes));
/* Current sensors */
pmbus_add_sensor_attrs(client, data, "curr", current_attributes,
ARRAY_SIZE(current_attributes));
/* Power sensors */
pmbus_add_sensor_attrs(client, data, "power", power_attributes,
ARRAY_SIZE(power_attributes));
/* Temperature sensors */
pmbus_add_sensor_attrs(client, data, "temp", temp_attributes,
ARRAY_SIZE(temp_attributes));
/* Fans */
pmbus_add_fan_attributes(client, data);
}
/* /*
* Identify chip parameters. * Identify chip parameters.
* This function is called for all chips. * This function is called for all chips.
......
/* /*
* sht15.c - support for the SHT15 Temperature and Humidity Sensor * sht15.c - support for the SHT15 Temperature and Humidity Sensor
* *
* Portions Copyright (c) 2010-2011 Savoir-faire Linux Inc.
* Jerome Oufella <jerome.oufella@savoirfairelinux.com>
* Vivien Didelot <vivien.didelot@savoirfairelinux.com>
*
* Copyright (c) 2009 Jonathan Cameron * Copyright (c) 2009 Jonathan Cameron
* *
* Copyright (c) 2007 Wouter Horre * Copyright (c) 2007 Wouter Horre
...@@ -9,16 +13,7 @@ ...@@ -9,16 +13,7 @@
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
* *
* Currently ignoring checksum on readings. * For further information, see the Documentation/hwmon/sht15 file.
* Default resolution only (14bit temp, 12bit humidity)
* Ignoring battery status.
* Heater not enabled.
* Timings are all conservative.
*
* Data sheet available (1/2009) at
* http://www.sensirion.ch/en/pdf/product_information/Datasheet-humidity-sensor-SHT1x.pdf
*
* Regulator supply name = vcc
*/ */
#include <linux/interrupt.h> #include <linux/interrupt.h>
...@@ -39,17 +34,31 @@ ...@@ -39,17 +34,31 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <asm/atomic.h> #include <asm/atomic.h>
#define SHT15_MEASURE_TEMP 3 /* Commands */
#define SHT15_MEASURE_RH 5 #define SHT15_MEASURE_TEMP 0x03
#define SHT15_MEASURE_RH 0x05
#define SHT15_READING_NOTHING 0 #define SHT15_WRITE_STATUS 0x06
#define SHT15_READING_TEMP 1 #define SHT15_READ_STATUS 0x07
#define SHT15_READING_HUMID 2 #define SHT15_SOFT_RESET 0x1E
/* Min timings in nsecs */ /* Min timings */
#define SHT15_TSCKL 100 /* clock low */ #define SHT15_TSCKL 100 /* (nsecs) clock low */
#define SHT15_TSCKH 100 /* clock high */ #define SHT15_TSCKH 100 /* (nsecs) clock high */
#define SHT15_TSU 150 /* data setup time */ #define SHT15_TSU 150 /* (nsecs) data setup time */
#define SHT15_TSRST 11 /* (msecs) soft reset time */
/* Status Register Bits */
#define SHT15_STATUS_LOW_RESOLUTION 0x01
#define SHT15_STATUS_NO_OTP_RELOAD 0x02
#define SHT15_STATUS_HEATER 0x04
#define SHT15_STATUS_LOW_BATTERY 0x40
/* Actions the driver may be doing */
enum sht15_state {
SHT15_READING_NOTHING,
SHT15_READING_TEMP,
SHT15_READING_HUMID
};
/** /**
* struct sht15_temppair - elements of voltage dependent temp calc * struct sht15_temppair - elements of voltage dependent temp calc
...@@ -61,9 +70,7 @@ struct sht15_temppair { ...@@ -61,9 +70,7 @@ struct sht15_temppair {
int d1; int d1;
}; };
/* Table 9 from data sheet - relates temperature calculation /* Table 9 from datasheet - relates temperature calculation to supply voltage */
* to supply voltage.
*/
static const struct sht15_temppair temppoints[] = { static const struct sht15_temppair temppoints[] = {
{ 2500000, -39400 }, { 2500000, -39400 },
{ 3000000, -39600 }, { 3000000, -39600 },
...@@ -72,29 +79,70 @@ static const struct sht15_temppair temppoints[] = { ...@@ -72,29 +79,70 @@ static const struct sht15_temppair temppoints[] = {
{ 5000000, -40100 }, { 5000000, -40100 },
}; };
/* Table from CRC datasheet, section 2.4 */
static const u8 sht15_crc8_table[] = {
0, 49, 98, 83, 196, 245, 166, 151,
185, 136, 219, 234, 125, 76, 31, 46,
67, 114, 33, 16, 135, 182, 229, 212,
250, 203, 152, 169, 62, 15, 92, 109,
134, 183, 228, 213, 66, 115, 32, 17,
63, 14, 93, 108, 251, 202, 153, 168,
197, 244, 167, 150, 1, 48, 99, 82,
124, 77, 30, 47, 184, 137, 218, 235,
61, 12, 95, 110, 249, 200, 155, 170,
132, 181, 230, 215, 64, 113, 34, 19,
126, 79, 28, 45, 186, 139, 216, 233,
199, 246, 165, 148, 3, 50, 97, 80,
187, 138, 217, 232, 127, 78, 29, 44,
2, 51, 96, 81, 198, 247, 164, 149,
248, 201, 154, 171, 60, 13, 94, 111,
65, 112, 35, 18, 133, 180, 231, 214,
122, 75, 24, 41, 190, 143, 220, 237,
195, 242, 161, 144, 7, 54, 101, 84,
57, 8, 91, 106, 253, 204, 159, 174,
128, 177, 226, 211, 68, 117, 38, 23,
252, 205, 158, 175, 56, 9, 90, 107,
69, 116, 39, 22, 129, 176, 227, 210,
191, 142, 221, 236, 123, 74, 25, 40,
6, 55, 100, 85, 194, 243, 160, 145,
71, 118, 37, 20, 131, 178, 225, 208,
254, 207, 156, 173, 58, 11, 88, 105,
4, 53, 102, 87, 192, 241, 162, 147,
189, 140, 223, 238, 121, 72, 27, 42,
193, 240, 163, 146, 5, 52, 103, 86,
120, 73, 26, 43, 188, 141, 222, 239,
130, 179, 224, 209, 70, 119, 36, 21,
59, 10, 89, 104, 255, 206, 157, 172
};
/** /**
* struct sht15_data - device instance specific data * struct sht15_data - device instance specific data
* @pdata: platform data (gpio's etc) * @pdata: platform data (gpio's etc).
* @read_work: bh of interrupt handler * @read_work: bh of interrupt handler.
* @wait_queue: wait queue for getting values from device * @wait_queue: wait queue for getting values from device.
* @val_temp: last temperature value read from device * @val_temp: last temperature value read from device.
* @val_humid: last humidity value read from device * @val_humid: last humidity value read from device.
* @flag: status flag used to identify what the last request was * @val_status: last status register value read from device.
* @valid: are the current stored values valid (start condition) * @checksum_ok: last value read from the device passed CRC validation.
* @last_updat: time of last update * @checksumming: flag used to enable the data validation with CRC.
* @read_lock: mutex to ensure only one read in progress * @state: state identifying the action the driver is doing.
* at a time. * @measurements_valid: are the current stored measures valid (start condition).
* @dev: associate device structure * @status_valid: is the current stored status valid (start condition).
* @hwmon_dev: device associated with hwmon subsystem * @last_measurement: time of last measure.
* @reg: associated regulator (if specified) * @last_status: time of last status reading.
* @nb: notifier block to handle notifications of voltage changes * @read_lock: mutex to ensure only one read in progress at a time.
* @supply_uV: local copy of supply voltage used to allow * @dev: associate device structure.
* use of regulator consumer if available * @hwmon_dev: device associated with hwmon subsystem.
* @supply_uV_valid: indicates that an updated value has not yet * @reg: associated regulator (if specified).
* been obtained from the regulator and so any calculations * @nb: notifier block to handle notifications of voltage
* based upon it will be invalid. * changes.
* @update_supply_work: work struct that is used to update the supply_uV * @supply_uV: local copy of supply voltage used to allow use of
* @interrupt_handled: flag used to indicate a hander has been scheduled * regulator consumer if available.
* @supply_uV_valid: indicates that an updated value has not yet been
* obtained from the regulator and so any calculations
* based upon it will be invalid.
* @update_supply_work: work struct that is used to update the supply_uV.
* @interrupt_handled: flag used to indicate a handler has been scheduled.
*/ */
struct sht15_data { struct sht15_data {
struct sht15_platform_data *pdata; struct sht15_platform_data *pdata;
...@@ -102,20 +150,59 @@ struct sht15_data { ...@@ -102,20 +150,59 @@ struct sht15_data {
wait_queue_head_t wait_queue; wait_queue_head_t wait_queue;
uint16_t val_temp; uint16_t val_temp;
uint16_t val_humid; uint16_t val_humid;
u8 flag; u8 val_status;
u8 valid; bool checksum_ok;
unsigned long last_updat; bool checksumming;
enum sht15_state state;
bool measurements_valid;
bool status_valid;
unsigned long last_measurement;
unsigned long last_status;
struct mutex read_lock; struct mutex read_lock;
struct device *dev; struct device *dev;
struct device *hwmon_dev; struct device *hwmon_dev;
struct regulator *reg; struct regulator *reg;
struct notifier_block nb; struct notifier_block nb;
int supply_uV; int supply_uV;
int supply_uV_valid; bool supply_uV_valid;
struct work_struct update_supply_work; struct work_struct update_supply_work;
atomic_t interrupt_handled; atomic_t interrupt_handled;
}; };
/**
* sht15_reverse() - reverse a byte
* @byte: byte to reverse.
*/
static u8 sht15_reverse(u8 byte)
{
u8 i, c;
for (c = 0, i = 0; i < 8; i++)
c |= (!!(byte & (1 << i))) << (7 - i);
return c;
}
/**
* sht15_crc8() - compute crc8
* @data: sht15 specific data.
* @value: sht15 retrieved data.
*
* This implements section 2 of the CRC datasheet.
*/
static u8 sht15_crc8(struct sht15_data *data,
const u8 *value,
int len)
{
u8 crc = sht15_reverse(data->val_status & 0x0F);
while (len--) {
crc = sht15_crc8_table[*value ^ crc];
value++;
}
return crc;
}
/** /**
* sht15_connection_reset() - reset the comms interface * sht15_connection_reset() - reset the comms interface
* @data: sht15 specific data * @data: sht15 specific data
...@@ -125,6 +212,7 @@ struct sht15_data { ...@@ -125,6 +212,7 @@ struct sht15_data {
static void sht15_connection_reset(struct sht15_data *data) static void sht15_connection_reset(struct sht15_data *data)
{ {
int i; int i;
gpio_direction_output(data->pdata->gpio_data, 1); gpio_direction_output(data->pdata->gpio_data, 1);
ndelay(SHT15_TSCKL); ndelay(SHT15_TSCKL);
gpio_set_value(data->pdata->gpio_sck, 0); gpio_set_value(data->pdata->gpio_sck, 0);
...@@ -136,14 +224,14 @@ static void sht15_connection_reset(struct sht15_data *data) ...@@ -136,14 +224,14 @@ static void sht15_connection_reset(struct sht15_data *data)
ndelay(SHT15_TSCKL); ndelay(SHT15_TSCKL);
} }
} }
/** /**
* sht15_send_bit() - send an individual bit to the device * sht15_send_bit() - send an individual bit to the device
* @data: device state data * @data: device state data
* @val: value of bit to be sent * @val: value of bit to be sent
**/ */
static inline void sht15_send_bit(struct sht15_data *data, int val) static inline void sht15_send_bit(struct sht15_data *data, int val)
{ {
gpio_set_value(data->pdata->gpio_data, val); gpio_set_value(data->pdata->gpio_data, val);
ndelay(SHT15_TSU); ndelay(SHT15_TSU);
gpio_set_value(data->pdata->gpio_sck, 1); gpio_set_value(data->pdata->gpio_sck, 1);
...@@ -154,12 +242,12 @@ static inline void sht15_send_bit(struct sht15_data *data, int val) ...@@ -154,12 +242,12 @@ static inline void sht15_send_bit(struct sht15_data *data, int val)
/** /**
* sht15_transmission_start() - specific sequence for new transmission * sht15_transmission_start() - specific sequence for new transmission
*
* @data: device state data * @data: device state data
*
* Timings for this are not documented on the data sheet, so very * Timings for this are not documented on the data sheet, so very
* conservative ones used in implementation. This implements * conservative ones used in implementation. This implements
* figure 12 on the data sheet. * figure 12 on the data sheet.
**/ */
static void sht15_transmission_start(struct sht15_data *data) static void sht15_transmission_start(struct sht15_data *data)
{ {
/* ensure data is high and output */ /* ensure data is high and output */
...@@ -180,23 +268,26 @@ static void sht15_transmission_start(struct sht15_data *data) ...@@ -180,23 +268,26 @@ static void sht15_transmission_start(struct sht15_data *data)
gpio_set_value(data->pdata->gpio_sck, 0); gpio_set_value(data->pdata->gpio_sck, 0);
ndelay(SHT15_TSCKL); ndelay(SHT15_TSCKL);
} }
/** /**
* sht15_send_byte() - send a single byte to the device * sht15_send_byte() - send a single byte to the device
* @data: device state * @data: device state
* @byte: value to be sent * @byte: value to be sent
**/ */
static void sht15_send_byte(struct sht15_data *data, u8 byte) static void sht15_send_byte(struct sht15_data *data, u8 byte)
{ {
int i; int i;
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
sht15_send_bit(data, !!(byte & 0x80)); sht15_send_bit(data, !!(byte & 0x80));
byte <<= 1; byte <<= 1;
} }
} }
/** /**
* sht15_wait_for_response() - checks for ack from device * sht15_wait_for_response() - checks for ack from device
* @data: device state * @data: device state
**/ */
static int sht15_wait_for_response(struct sht15_data *data) static int sht15_wait_for_response(struct sht15_data *data)
{ {
gpio_direction_input(data->pdata->gpio_data); gpio_direction_input(data->pdata->gpio_data);
...@@ -220,27 +311,199 @@ static int sht15_wait_for_response(struct sht15_data *data) ...@@ -220,27 +311,199 @@ static int sht15_wait_for_response(struct sht15_data *data)
* *
* On entry, sck is output low, data is output pull high * On entry, sck is output low, data is output pull high
* and the interrupt disabled. * and the interrupt disabled.
**/ */
static int sht15_send_cmd(struct sht15_data *data, u8 cmd) static int sht15_send_cmd(struct sht15_data *data, u8 cmd)
{ {
int ret = 0; int ret = 0;
sht15_transmission_start(data); sht15_transmission_start(data);
sht15_send_byte(data, cmd); sht15_send_byte(data, cmd);
ret = sht15_wait_for_response(data); ret = sht15_wait_for_response(data);
return ret; return ret;
} }
/**
* sht15_soft_reset() - send a soft reset command
* @data: sht15 specific data.
*
* As described in section 3.2 of the datasheet.
*/
static int sht15_soft_reset(struct sht15_data *data)
{
int ret;
ret = sht15_send_cmd(data, SHT15_SOFT_RESET);
if (ret)
return ret;
msleep(SHT15_TSRST);
/* device resets default hardware status register value */
data->val_status = 0;
return ret;
}
/**
* sht15_ack() - send a ack
* @data: sht15 specific data.
*
* Each byte of data is acknowledged by pulling the data line
* low for one clock pulse.
*/
static void sht15_ack(struct sht15_data *data)
{
gpio_direction_output(data->pdata->gpio_data, 0);
ndelay(SHT15_TSU);
gpio_set_value(data->pdata->gpio_sck, 1);
ndelay(SHT15_TSU);
gpio_set_value(data->pdata->gpio_sck, 0);
ndelay(SHT15_TSU);
gpio_set_value(data->pdata->gpio_data, 1);
gpio_direction_input(data->pdata->gpio_data);
}
/**
* sht15_end_transmission() - notify device of end of transmission
* @data: device state.
*
* This is basically a NAK (single clock pulse, data high).
*/
static void sht15_end_transmission(struct sht15_data *data)
{
gpio_direction_output(data->pdata->gpio_data, 1);
ndelay(SHT15_TSU);
gpio_set_value(data->pdata->gpio_sck, 1);
ndelay(SHT15_TSCKH);
gpio_set_value(data->pdata->gpio_sck, 0);
ndelay(SHT15_TSCKL);
}
/**
* sht15_read_byte() - Read a byte back from the device
* @data: device state.
*/
static u8 sht15_read_byte(struct sht15_data *data)
{
int i;
u8 byte = 0;
for (i = 0; i < 8; ++i) {
byte <<= 1;
gpio_set_value(data->pdata->gpio_sck, 1);
ndelay(SHT15_TSCKH);
byte |= !!gpio_get_value(data->pdata->gpio_data);
gpio_set_value(data->pdata->gpio_sck, 0);
ndelay(SHT15_TSCKL);
}
return byte;
}
/**
* sht15_send_status() - write the status register byte
* @data: sht15 specific data.
* @status: the byte to set the status register with.
*
* As described in figure 14 and table 5 of the datasheet.
*/
static int sht15_send_status(struct sht15_data *data, u8 status)
{
int ret;
ret = sht15_send_cmd(data, SHT15_WRITE_STATUS);
if (ret)
return ret;
gpio_direction_output(data->pdata->gpio_data, 1);
ndelay(SHT15_TSU);
sht15_send_byte(data, status);
ret = sht15_wait_for_response(data);
if (ret)
return ret;
data->val_status = status;
return 0;
}
/**
* sht15_update_status() - get updated status register from device if too old
* @data: device instance specific data.
*
* As described in figure 15 and table 5 of the datasheet.
*/
static int sht15_update_status(struct sht15_data *data)
{
int ret = 0;
u8 status;
u8 previous_config;
u8 dev_checksum = 0;
u8 checksum_vals[2];
int timeout = HZ;
mutex_lock(&data->read_lock);
if (time_after(jiffies, data->last_status + timeout)
|| !data->status_valid) {
ret = sht15_send_cmd(data, SHT15_READ_STATUS);
if (ret)
goto error_ret;
status = sht15_read_byte(data);
if (data->checksumming) {
sht15_ack(data);
dev_checksum = sht15_reverse(sht15_read_byte(data));
checksum_vals[0] = SHT15_READ_STATUS;
checksum_vals[1] = status;
data->checksum_ok = (sht15_crc8(data, checksum_vals, 2)
== dev_checksum);
}
sht15_end_transmission(data);
/*
* Perform checksum validation on the received data.
* Specification mentions that in case a checksum verification
* fails, a soft reset command must be sent to the device.
*/
if (data->checksumming && !data->checksum_ok) {
previous_config = data->val_status & 0x07;
ret = sht15_soft_reset(data);
if (ret)
goto error_ret;
if (previous_config) {
ret = sht15_send_status(data, previous_config);
if (ret) {
dev_err(data->dev,
"CRC validation failed, unable "
"to restore device settings\n");
goto error_ret;
}
}
ret = -EAGAIN;
goto error_ret;
}
data->val_status = status;
data->status_valid = true;
data->last_status = jiffies;
}
error_ret:
mutex_unlock(&data->read_lock);
return ret;
}
/** /**
* sht15_update_single_val() - get a new value from device * sht15_measurement() - get a new value from device
* @data: device instance specific data * @data: device instance specific data
* @command: command sent to request value * @command: command sent to request value
* @timeout_msecs: timeout after which comms are assumed * @timeout_msecs: timeout after which comms are assumed
* to have failed are reset. * to have failed are reset.
**/ */
static inline int sht15_update_single_val(struct sht15_data *data, static int sht15_measurement(struct sht15_data *data,
int command, int command,
int timeout_msecs) int timeout_msecs)
{ {
int ret; int ret;
u8 previous_config;
ret = sht15_send_cmd(data, command); ret = sht15_send_cmd(data, command);
if (ret) if (ret)
return ret; return ret;
...@@ -256,38 +519,61 @@ static inline int sht15_update_single_val(struct sht15_data *data, ...@@ -256,38 +519,61 @@ static inline int sht15_update_single_val(struct sht15_data *data,
schedule_work(&data->read_work); schedule_work(&data->read_work);
} }
ret = wait_event_timeout(data->wait_queue, ret = wait_event_timeout(data->wait_queue,
(data->flag == SHT15_READING_NOTHING), (data->state == SHT15_READING_NOTHING),
msecs_to_jiffies(timeout_msecs)); msecs_to_jiffies(timeout_msecs));
if (ret == 0) {/* timeout occurred */ if (ret == 0) {/* timeout occurred */
disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data)); disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data));
sht15_connection_reset(data); sht15_connection_reset(data);
return -ETIME; return -ETIME;
} }
/*
* Perform checksum validation on the received data.
* Specification mentions that in case a checksum verification fails,
* a soft reset command must be sent to the device.
*/
if (data->checksumming && !data->checksum_ok) {
previous_config = data->val_status & 0x07;
ret = sht15_soft_reset(data);
if (ret)
return ret;
if (previous_config) {
ret = sht15_send_status(data, previous_config);
if (ret) {
dev_err(data->dev,
"CRC validation failed, unable "
"to restore device settings\n");
return ret;
}
}
return -EAGAIN;
}
return 0; return 0;
} }
/** /**
* sht15_update_vals() - get updated readings from device if too old * sht15_update_measurements() - get updated measures from device if too old
* @data: device state * @data: device state
**/ */
static int sht15_update_vals(struct sht15_data *data) static int sht15_update_measurements(struct sht15_data *data)
{ {
int ret = 0; int ret = 0;
int timeout = HZ; int timeout = HZ;
mutex_lock(&data->read_lock); mutex_lock(&data->read_lock);
if (time_after(jiffies, data->last_updat + timeout) if (time_after(jiffies, data->last_measurement + timeout)
|| !data->valid) { || !data->measurements_valid) {
data->flag = SHT15_READING_HUMID; data->state = SHT15_READING_HUMID;
ret = sht15_update_single_val(data, SHT15_MEASURE_RH, 160); ret = sht15_measurement(data, SHT15_MEASURE_RH, 160);
if (ret) if (ret)
goto error_ret; goto error_ret;
data->flag = SHT15_READING_TEMP; data->state = SHT15_READING_TEMP;
ret = sht15_update_single_val(data, SHT15_MEASURE_TEMP, 400); ret = sht15_measurement(data, SHT15_MEASURE_TEMP, 400);
if (ret) if (ret)
goto error_ret; goto error_ret;
data->valid = 1; data->measurements_valid = true;
data->last_updat = jiffies; data->last_measurement = jiffies;
} }
error_ret: error_ret:
mutex_unlock(&data->read_lock); mutex_unlock(&data->read_lock);
...@@ -300,10 +586,11 @@ static int sht15_update_vals(struct sht15_data *data) ...@@ -300,10 +586,11 @@ static int sht15_update_vals(struct sht15_data *data)
* @data: device state * @data: device state
* *
* As per section 4.3 of the data sheet. * As per section 4.3 of the data sheet.
**/ */
static inline int sht15_calc_temp(struct sht15_data *data) static inline int sht15_calc_temp(struct sht15_data *data)
{ {
int d1 = temppoints[0].d1; int d1 = temppoints[0].d1;
int d2 = (data->val_status & SHT15_STATUS_LOW_RESOLUTION) ? 40 : 10;
int i; int i;
for (i = ARRAY_SIZE(temppoints) - 1; i > 0; i--) for (i = ARRAY_SIZE(temppoints) - 1; i > 0; i--)
...@@ -316,7 +603,7 @@ static inline int sht15_calc_temp(struct sht15_data *data) ...@@ -316,7 +603,7 @@ static inline int sht15_calc_temp(struct sht15_data *data)
break; break;
} }
return data->val_temp*10 + d1; return data->val_temp * d2 + d1;
} }
/** /**
...@@ -325,23 +612,102 @@ static inline int sht15_calc_temp(struct sht15_data *data) ...@@ -325,23 +612,102 @@ static inline int sht15_calc_temp(struct sht15_data *data)
* *
* This is the temperature compensated version as per section 4.2 of * This is the temperature compensated version as per section 4.2 of
* the data sheet. * the data sheet.
**/ *
* The sensor is assumed to be V3, which is compatible with V4.
* Humidity conversion coefficients are shown in table 7 of the datasheet.
*/
static inline int sht15_calc_humid(struct sht15_data *data) static inline int sht15_calc_humid(struct sht15_data *data)
{ {
int RHlinear; /* milli percent */ int rh_linear; /* milli percent */
int temp = sht15_calc_temp(data); int temp = sht15_calc_temp(data);
int c2, c3;
int t2;
const int c1 = -4; const int c1 = -4;
const int c2 = 40500; /* x 10 ^ -6 */
const int c3 = -28; /* x 10 ^ -7 */
RHlinear = c1*1000 if (data->val_status & SHT15_STATUS_LOW_RESOLUTION) {
+ c2 * data->val_humid/1000 c2 = 648000; /* x 10 ^ -6 */
c3 = -7200; /* x 10 ^ -7 */
t2 = 1280;
} else {
c2 = 40500; /* x 10 ^ -6 */
c3 = -28; /* x 10 ^ -7 */
t2 = 80;
}
rh_linear = c1 * 1000
+ c2 * data->val_humid / 1000
+ (data->val_humid * data->val_humid * c3) / 10000; + (data->val_humid * data->val_humid * c3) / 10000;
return (temp - 25000) * (10000 + 80 * data->val_humid) return (temp - 25000) * (10000 + t2 * data->val_humid)
/ 1000000 + RHlinear; / 1000000 + rh_linear;
}
/**
* sht15_show_status() - show status information in sysfs
* @dev: device.
* @attr: device attribute.
* @buf: sysfs buffer where information is written to.
*
* Will be called on read access to temp1_fault, humidity1_fault
* and heater_enable sysfs attributes.
* Returns number of bytes written into buffer, negative errno on error.
*/
static ssize_t sht15_show_status(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int ret;
struct sht15_data *data = dev_get_drvdata(dev);
u8 bit = to_sensor_dev_attr(attr)->index;
ret = sht15_update_status(data);
return ret ? ret : sprintf(buf, "%d\n", !!(data->val_status & bit));
}
/**
* sht15_store_heater() - change heater state via sysfs
* @dev: device.
* @attr: device attribute.
* @buf: sysfs buffer to read the new heater state from.
* @count: length of the data.
*
* Will be called on read access to heater_enable sysfs attribute.
* Returns number of bytes actually decoded, negative errno on error.
*/
static ssize_t sht15_store_heater(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
struct sht15_data *data = dev_get_drvdata(dev);
long value;
u8 status;
if (strict_strtol(buf, 10, &value))
return -EINVAL;
mutex_lock(&data->read_lock);
status = data->val_status & 0x07;
if (!!value)
status |= SHT15_STATUS_HEATER;
else
status &= ~SHT15_STATUS_HEATER;
ret = sht15_send_status(data, status);
mutex_unlock(&data->read_lock);
return ret ? ret : count;
} }
/**
* sht15_show_temp() - show temperature measurement value in sysfs
* @dev: device.
* @attr: device attribute.
* @buf: sysfs buffer where measurement values are written to.
*
* Will be called on read access to temp1_input sysfs attribute.
* Returns number of bytes written into buffer, negative errno on error.
*/
static ssize_t sht15_show_temp(struct device *dev, static ssize_t sht15_show_temp(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
...@@ -350,12 +716,21 @@ static ssize_t sht15_show_temp(struct device *dev, ...@@ -350,12 +716,21 @@ static ssize_t sht15_show_temp(struct device *dev,
struct sht15_data *data = dev_get_drvdata(dev); struct sht15_data *data = dev_get_drvdata(dev);
/* Technically no need to read humidity as well */ /* Technically no need to read humidity as well */
ret = sht15_update_vals(data); ret = sht15_update_measurements(data);
return ret ? ret : sprintf(buf, "%d\n", return ret ? ret : sprintf(buf, "%d\n",
sht15_calc_temp(data)); sht15_calc_temp(data));
} }
/**
* sht15_show_humidity() - show humidity measurement value in sysfs
* @dev: device.
* @attr: device attribute.
* @buf: sysfs buffer where measurement values are written to.
*
* Will be called on read access to humidity1_input sysfs attribute.
* Returns number of bytes written into buffer, negative errno on error.
*/
static ssize_t sht15_show_humidity(struct device *dev, static ssize_t sht15_show_humidity(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
...@@ -363,11 +738,11 @@ static ssize_t sht15_show_humidity(struct device *dev, ...@@ -363,11 +738,11 @@ static ssize_t sht15_show_humidity(struct device *dev,
int ret; int ret;
struct sht15_data *data = dev_get_drvdata(dev); struct sht15_data *data = dev_get_drvdata(dev);
ret = sht15_update_vals(data); ret = sht15_update_measurements(data);
return ret ? ret : sprintf(buf, "%d\n", sht15_calc_humid(data)); return ret ? ret : sprintf(buf, "%d\n", sht15_calc_humid(data));
}
};
static ssize_t show_name(struct device *dev, static ssize_t show_name(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
...@@ -376,16 +751,23 @@ static ssize_t show_name(struct device *dev, ...@@ -376,16 +751,23 @@ static ssize_t show_name(struct device *dev,
return sprintf(buf, "%s\n", pdev->name); return sprintf(buf, "%s\n", pdev->name);
} }
static SENSOR_DEVICE_ATTR(temp1_input, static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
S_IRUGO, sht15_show_temp, sht15_show_temp, NULL, 0);
NULL, 0); static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO,
static SENSOR_DEVICE_ATTR(humidity1_input, sht15_show_humidity, NULL, 0);
S_IRUGO, sht15_show_humidity, static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, sht15_show_status, NULL,
NULL, 0); SHT15_STATUS_LOW_BATTERY);
static SENSOR_DEVICE_ATTR(humidity1_fault, S_IRUGO, sht15_show_status, NULL,
SHT15_STATUS_LOW_BATTERY);
static SENSOR_DEVICE_ATTR(heater_enable, S_IRUGO | S_IWUSR, sht15_show_status,
sht15_store_heater, SHT15_STATUS_HEATER);
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
static struct attribute *sht15_attrs[] = { static struct attribute *sht15_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_humidity1_input.dev_attr.attr, &sensor_dev_attr_humidity1_input.dev_attr.attr,
&sensor_dev_attr_temp1_fault.dev_attr.attr,
&sensor_dev_attr_humidity1_fault.dev_attr.attr,
&sensor_dev_attr_heater_enable.dev_attr.attr,
&dev_attr_name.attr, &dev_attr_name.attr,
NULL, NULL,
}; };
...@@ -397,59 +779,31 @@ static const struct attribute_group sht15_attr_group = { ...@@ -397,59 +779,31 @@ static const struct attribute_group sht15_attr_group = {
static irqreturn_t sht15_interrupt_fired(int irq, void *d) static irqreturn_t sht15_interrupt_fired(int irq, void *d)
{ {
struct sht15_data *data = d; struct sht15_data *data = d;
/* First disable the interrupt */ /* First disable the interrupt */
disable_irq_nosync(irq); disable_irq_nosync(irq);
atomic_inc(&data->interrupt_handled); atomic_inc(&data->interrupt_handled);
/* Then schedule a reading work struct */ /* Then schedule a reading work struct */
if (data->flag != SHT15_READING_NOTHING) if (data->state != SHT15_READING_NOTHING)
schedule_work(&data->read_work); schedule_work(&data->read_work);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
/* Each byte of data is acknowledged by pulling the data line
* low for one clock pulse.
*/
static void sht15_ack(struct sht15_data *data)
{
gpio_direction_output(data->pdata->gpio_data, 0);
ndelay(SHT15_TSU);
gpio_set_value(data->pdata->gpio_sck, 1);
ndelay(SHT15_TSU);
gpio_set_value(data->pdata->gpio_sck, 0);
ndelay(SHT15_TSU);
gpio_set_value(data->pdata->gpio_data, 1);
gpio_direction_input(data->pdata->gpio_data);
}
/**
* sht15_end_transmission() - notify device of end of transmission
* @data: device state
*
* This is basically a NAK. (single clock pulse, data high)
**/
static void sht15_end_transmission(struct sht15_data *data)
{
gpio_direction_output(data->pdata->gpio_data, 1);
ndelay(SHT15_TSU);
gpio_set_value(data->pdata->gpio_sck, 1);
ndelay(SHT15_TSCKH);
gpio_set_value(data->pdata->gpio_sck, 0);
ndelay(SHT15_TSCKL);
}
static void sht15_bh_read_data(struct work_struct *work_s) static void sht15_bh_read_data(struct work_struct *work_s)
{ {
int i;
uint16_t val = 0; uint16_t val = 0;
u8 dev_checksum = 0;
u8 checksum_vals[3];
struct sht15_data *data struct sht15_data *data
= container_of(work_s, struct sht15_data, = container_of(work_s, struct sht15_data,
read_work); read_work);
/* Firstly, verify the line is low */ /* Firstly, verify the line is low */
if (gpio_get_value(data->pdata->gpio_data)) { if (gpio_get_value(data->pdata->gpio_data)) {
/* If not, then start the interrupt again - care /*
here as could have gone low in meantime so verify * If not, then start the interrupt again - care here as could
it hasn't! * have gone low in meantime so verify it hasn't!
*/ */
atomic_set(&data->interrupt_handled, 0); atomic_set(&data->interrupt_handled, 0);
enable_irq(gpio_to_irq(data->pdata->gpio_data)); enable_irq(gpio_to_irq(data->pdata->gpio_data));
/* If still not occurred or another handler has been scheduled */ /* If still not occurred or another handler has been scheduled */
...@@ -457,30 +811,43 @@ static void sht15_bh_read_data(struct work_struct *work_s) ...@@ -457,30 +811,43 @@ static void sht15_bh_read_data(struct work_struct *work_s)
|| atomic_read(&data->interrupt_handled)) || atomic_read(&data->interrupt_handled))
return; return;
} }
/* Read the data back from the device */ /* Read the data back from the device */
for (i = 0; i < 16; ++i) { val = sht15_read_byte(data);
val <<= 1; val <<= 8;
gpio_set_value(data->pdata->gpio_sck, 1); sht15_ack(data);
ndelay(SHT15_TSCKH); val |= sht15_read_byte(data);
val |= !!gpio_get_value(data->pdata->gpio_data);
gpio_set_value(data->pdata->gpio_sck, 0); if (data->checksumming) {
ndelay(SHT15_TSCKL); /*
if (i == 7) * Ask the device for a checksum and read it back.
sht15_ack(data); * Note: the device sends the checksum byte reversed.
*/
sht15_ack(data);
dev_checksum = sht15_reverse(sht15_read_byte(data));
checksum_vals[0] = (data->state == SHT15_READING_TEMP) ?
SHT15_MEASURE_TEMP : SHT15_MEASURE_RH;
checksum_vals[1] = (u8) (val >> 8);
checksum_vals[2] = (u8) val;
data->checksum_ok
= (sht15_crc8(data, checksum_vals, 3) == dev_checksum);
} }
/* Tell the device we are done */ /* Tell the device we are done */
sht15_end_transmission(data); sht15_end_transmission(data);
switch (data->flag) { switch (data->state) {
case SHT15_READING_TEMP: case SHT15_READING_TEMP:
data->val_temp = val; data->val_temp = val;
break; break;
case SHT15_READING_HUMID: case SHT15_READING_HUMID:
data->val_humid = val; data->val_humid = val;
break; break;
default:
break;
} }
data->flag = SHT15_READING_NOTHING; data->state = SHT15_READING_NOTHING;
wake_up(&data->wait_queue); wake_up(&data->wait_queue);
} }
...@@ -500,10 +867,10 @@ static void sht15_update_voltage(struct work_struct *work_s) ...@@ -500,10 +867,10 @@ static void sht15_update_voltage(struct work_struct *work_s)
* *
* Note that as the notification code holds the regulator lock, we have * Note that as the notification code holds the regulator lock, we have
* to schedule an update of the supply voltage rather than getting it directly. * to schedule an update of the supply voltage rather than getting it directly.
**/ */
static int sht15_invalidate_voltage(struct notifier_block *nb, static int sht15_invalidate_voltage(struct notifier_block *nb,
unsigned long event, unsigned long event,
void *ignored) void *ignored)
{ {
struct sht15_data *data = container_of(nb, struct sht15_data, nb); struct sht15_data *data = container_of(nb, struct sht15_data, nb);
...@@ -518,10 +885,11 @@ static int __devinit sht15_probe(struct platform_device *pdev) ...@@ -518,10 +885,11 @@ static int __devinit sht15_probe(struct platform_device *pdev)
{ {
int ret = 0; int ret = 0;
struct sht15_data *data = kzalloc(sizeof(*data), GFP_KERNEL); struct sht15_data *data = kzalloc(sizeof(*data), GFP_KERNEL);
u8 status = 0;
if (!data) { if (!data) {
ret = -ENOMEM; ret = -ENOMEM;
dev_err(&pdev->dev, "kzalloc failed"); dev_err(&pdev->dev, "kzalloc failed\n");
goto error_ret; goto error_ret;
} }
...@@ -533,13 +901,22 @@ static int __devinit sht15_probe(struct platform_device *pdev) ...@@ -533,13 +901,22 @@ static int __devinit sht15_probe(struct platform_device *pdev)
init_waitqueue_head(&data->wait_queue); init_waitqueue_head(&data->wait_queue);
if (pdev->dev.platform_data == NULL) { if (pdev->dev.platform_data == NULL) {
dev_err(&pdev->dev, "no platform data supplied"); dev_err(&pdev->dev, "no platform data supplied\n");
goto err_free_data; goto err_free_data;
} }
data->pdata = pdev->dev.platform_data; data->pdata = pdev->dev.platform_data;
data->supply_uV = data->pdata->supply_mv*1000; data->supply_uV = data->pdata->supply_mv * 1000;
if (data->pdata->checksum)
/* If a regulator is available, query what the supply voltage actually is!*/ data->checksumming = true;
if (data->pdata->no_otp_reload)
status |= SHT15_STATUS_NO_OTP_RELOAD;
if (data->pdata->low_resolution)
status |= SHT15_STATUS_LOW_RESOLUTION;
/*
* If a regulator is available,
* query what the supply voltage actually is!
*/
data->reg = regulator_get(data->dev, "vcc"); data->reg = regulator_get(data->dev, "vcc");
if (!IS_ERR(data->reg)) { if (!IS_ERR(data->reg)) {
int voltage; int voltage;
...@@ -549,28 +926,34 @@ static int __devinit sht15_probe(struct platform_device *pdev) ...@@ -549,28 +926,34 @@ static int __devinit sht15_probe(struct platform_device *pdev)
data->supply_uV = voltage; data->supply_uV = voltage;
regulator_enable(data->reg); regulator_enable(data->reg);
/* setup a notifier block to update this if another device /*
* causes the voltage to change */ * Setup a notifier block to update this if another device
* causes the voltage to change
*/
data->nb.notifier_call = &sht15_invalidate_voltage; data->nb.notifier_call = &sht15_invalidate_voltage;
ret = regulator_register_notifier(data->reg, &data->nb); ret = regulator_register_notifier(data->reg, &data->nb);
if (ret) {
dev_err(&pdev->dev,
"regulator notifier request failed\n");
regulator_disable(data->reg);
regulator_put(data->reg);
goto err_free_data;
}
} }
/* Try requesting the GPIOs */
/* Try requesting the GPIOs */
ret = gpio_request(data->pdata->gpio_sck, "SHT15 sck"); ret = gpio_request(data->pdata->gpio_sck, "SHT15 sck");
if (ret) { if (ret) {
dev_err(&pdev->dev, "gpio request failed"); dev_err(&pdev->dev, "gpio request failed\n");
goto err_free_data; goto err_release_reg;
} }
gpio_direction_output(data->pdata->gpio_sck, 0); gpio_direction_output(data->pdata->gpio_sck, 0);
ret = gpio_request(data->pdata->gpio_data, "SHT15 data"); ret = gpio_request(data->pdata->gpio_data, "SHT15 data");
if (ret) { if (ret) {
dev_err(&pdev->dev, "gpio request failed"); dev_err(&pdev->dev, "gpio request failed\n");
goto err_release_gpio_sck; goto err_release_gpio_sck;
} }
ret = sysfs_create_group(&pdev->dev.kobj, &sht15_attr_group);
if (ret) {
dev_err(&pdev->dev, "sysfs create failed");
goto err_release_gpio_data;
}
ret = request_irq(gpio_to_irq(data->pdata->gpio_data), ret = request_irq(gpio_to_irq(data->pdata->gpio_data),
sht15_interrupt_fired, sht15_interrupt_fired,
...@@ -578,30 +961,53 @@ static int __devinit sht15_probe(struct platform_device *pdev) ...@@ -578,30 +961,53 @@ static int __devinit sht15_probe(struct platform_device *pdev)
"sht15 data", "sht15 data",
data); data);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to get irq for data line"); dev_err(&pdev->dev, "failed to get irq for data line\n");
goto err_release_gpio_data; goto err_release_gpio_data;
} }
disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data)); disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data));
sht15_connection_reset(data); sht15_connection_reset(data);
sht15_send_cmd(data, 0x1E); ret = sht15_soft_reset(data);
if (ret)
goto err_release_irq;
/* write status with platform data options */
if (status) {
ret = sht15_send_status(data, status);
if (ret)
goto err_release_irq;
}
ret = sysfs_create_group(&pdev->dev.kobj, &sht15_attr_group);
if (ret) {
dev_err(&pdev->dev, "sysfs create failed\n");
goto err_release_irq;
}
data->hwmon_dev = hwmon_device_register(data->dev); data->hwmon_dev = hwmon_device_register(data->dev);
if (IS_ERR(data->hwmon_dev)) { if (IS_ERR(data->hwmon_dev)) {
ret = PTR_ERR(data->hwmon_dev); ret = PTR_ERR(data->hwmon_dev);
goto err_release_irq; goto err_release_sysfs_group;
} }
return 0; return 0;
err_release_sysfs_group:
sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group);
err_release_irq: err_release_irq:
free_irq(gpio_to_irq(data->pdata->gpio_data), data); free_irq(gpio_to_irq(data->pdata->gpio_data), data);
err_release_gpio_data: err_release_gpio_data:
gpio_free(data->pdata->gpio_data); gpio_free(data->pdata->gpio_data);
err_release_gpio_sck: err_release_gpio_sck:
gpio_free(data->pdata->gpio_sck); gpio_free(data->pdata->gpio_sck);
err_release_reg:
if (!IS_ERR(data->reg)) {
regulator_unregister_notifier(data->reg, &data->nb);
regulator_disable(data->reg);
regulator_put(data->reg);
}
err_free_data: err_free_data:
kfree(data); kfree(data);
error_ret: error_ret:
return ret; return ret;
} }
...@@ -609,9 +1015,15 @@ static int __devexit sht15_remove(struct platform_device *pdev) ...@@ -609,9 +1015,15 @@ static int __devexit sht15_remove(struct platform_device *pdev)
{ {
struct sht15_data *data = platform_get_drvdata(pdev); struct sht15_data *data = platform_get_drvdata(pdev);
/* Make sure any reads from the device are done and /*
* prevent new ones from beginning */ * Make sure any reads from the device are done and
* prevent new ones beginning
*/
mutex_lock(&data->read_lock); mutex_lock(&data->read_lock);
if (sht15_soft_reset(data)) {
mutex_unlock(&data->read_lock);
return -EFAULT;
}
hwmon_device_unregister(data->hwmon_dev); hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group); sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group);
if (!IS_ERR(data->reg)) { if (!IS_ERR(data->reg)) {
...@@ -625,10 +1037,10 @@ static int __devexit sht15_remove(struct platform_device *pdev) ...@@ -625,10 +1037,10 @@ static int __devexit sht15_remove(struct platform_device *pdev)
gpio_free(data->pdata->gpio_sck); gpio_free(data->pdata->gpio_sck);
mutex_unlock(&data->read_lock); mutex_unlock(&data->read_lock);
kfree(data); kfree(data);
return 0; return 0;
} }
/* /*
* sht_drivers simultaneously refers to __devinit and __devexit function * sht_drivers simultaneously refers to __devinit and __devexit function
* which causes spurious section mismatch warning. So use __refdata to * which causes spurious section mismatch warning. So use __refdata to
...@@ -673,7 +1085,6 @@ static struct platform_driver __refdata sht_drivers[] = { ...@@ -673,7 +1085,6 @@ static struct platform_driver __refdata sht_drivers[] = {
}, },
}; };
static int __init sht15_init(void) static int __init sht15_init(void)
{ {
int ret; int ret;
......
/*
* Hardware monitoring driver for UCD90xxx Sequencer and System Health
* Controller series
*
* Copyright (C) 2011 Ericsson AB.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c/pmbus.h>
#include "pmbus.h"
enum chips { ucd9000, ucd90120, ucd90124, ucd9090, ucd90910 };
#define UCD9000_MONITOR_CONFIG 0xd5
#define UCD9000_NUM_PAGES 0xd6
#define UCD9000_FAN_CONFIG_INDEX 0xe7
#define UCD9000_FAN_CONFIG 0xe8
#define UCD9000_DEVICE_ID 0xfd
#define UCD9000_MON_TYPE(x) (((x) >> 5) & 0x07)
#define UCD9000_MON_PAGE(x) ((x) & 0x0f)
#define UCD9000_MON_VOLTAGE 1
#define UCD9000_MON_TEMPERATURE 2
#define UCD9000_MON_CURRENT 3
#define UCD9000_MON_VOLTAGE_HW 4
#define UCD9000_NUM_FAN 4
struct ucd9000_data {
u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX];
struct pmbus_driver_info info;
};
#define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
{
int fan_config = 0;
struct ucd9000_data *data
= to_ucd9000_data(pmbus_get_driver_info(client));
if (data->fan_data[fan][3] & 1)
fan_config |= PB_FAN_2_INSTALLED; /* Use lower bit position */
/* Pulses/revolution */
fan_config |= (data->fan_data[fan][3] & 0x06) >> 1;
return fan_config;
}
static int ucd9000_read_byte_data(struct i2c_client *client, int page, int reg)
{
int ret = 0;
int fan_config;
switch (reg) {
case PMBUS_FAN_CONFIG_12:
if (page)
return -EINVAL;
ret = ucd9000_get_fan_config(client, 0);
if (ret < 0)
return ret;
fan_config = ret << 4;
ret = ucd9000_get_fan_config(client, 1);
if (ret < 0)
return ret;
fan_config |= ret;
ret = fan_config;
break;
case PMBUS_FAN_CONFIG_34:
if (page)
return -EINVAL;
ret = ucd9000_get_fan_config(client, 2);
if (ret < 0)
return ret;
fan_config = ret << 4;
ret = ucd9000_get_fan_config(client, 3);
if (ret < 0)
return ret;
fan_config |= ret;
ret = fan_config;
break;
default:
ret = -ENODATA;
break;
}
return ret;
}
static const struct i2c_device_id ucd9000_id[] = {
{"ucd9000", ucd9000},
{"ucd90120", ucd90120},
{"ucd90124", ucd90124},
{"ucd9090", ucd9090},
{"ucd90910", ucd90910},
{}
};
MODULE_DEVICE_TABLE(i2c, ucd9000_id);
static int ucd9000_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1];
struct ucd9000_data *data;
struct pmbus_driver_info *info;
const struct i2c_device_id *mid;
int i, ret;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA))
return -ENODEV;
ret = i2c_smbus_read_block_data(client, UCD9000_DEVICE_ID,
block_buffer);
if (ret < 0) {
dev_err(&client->dev, "Failed to read device ID\n");
return ret;
}
block_buffer[ret] = '\0';
dev_info(&client->dev, "Device ID %s\n", block_buffer);
mid = NULL;
for (i = 0; i < ARRAY_SIZE(ucd9000_id); i++) {
mid = &ucd9000_id[i];
if (!strncasecmp(mid->name, block_buffer, strlen(mid->name)))
break;
}
if (!mid || !strlen(mid->name)) {
dev_err(&client->dev, "Unsupported device\n");
return -ENODEV;
}
if (id->driver_data != ucd9000 && id->driver_data != mid->driver_data)
dev_notice(&client->dev,
"Device mismatch: Configured %s, detected %s\n",
id->name, mid->name);
data = kzalloc(sizeof(struct ucd9000_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
info = &data->info;
ret = i2c_smbus_read_byte_data(client, UCD9000_NUM_PAGES);
if (ret < 0) {
dev_err(&client->dev,
"Failed to read number of active pages\n");
goto out;
}
info->pages = ret;
if (!info->pages) {
dev_err(&client->dev, "No pages configured\n");
ret = -ENODEV;
goto out;
}
/* The internal temperature sensor is always active */
info->func[0] = PMBUS_HAVE_TEMP;
/* Everything else is configurable */
ret = i2c_smbus_read_block_data(client, UCD9000_MONITOR_CONFIG,
block_buffer);
if (ret <= 0) {
dev_err(&client->dev, "Failed to read configuration data\n");
ret = -ENODEV;
goto out;
}
for (i = 0; i < ret; i++) {
int page = UCD9000_MON_PAGE(block_buffer[i]);
if (page >= info->pages)
continue;
switch (UCD9000_MON_TYPE(block_buffer[i])) {
case UCD9000_MON_VOLTAGE:
case UCD9000_MON_VOLTAGE_HW:
info->func[page] |= PMBUS_HAVE_VOUT
| PMBUS_HAVE_STATUS_VOUT;
break;
case UCD9000_MON_TEMPERATURE:
info->func[page] |= PMBUS_HAVE_TEMP2
| PMBUS_HAVE_STATUS_TEMP;
break;
case UCD9000_MON_CURRENT:
info->func[page] |= PMBUS_HAVE_IOUT
| PMBUS_HAVE_STATUS_IOUT;
break;
default:
break;
}
}
/* Fan configuration */
if (mid->driver_data == ucd90124) {
for (i = 0; i < UCD9000_NUM_FAN; i++) {
i2c_smbus_write_byte_data(client,
UCD9000_FAN_CONFIG_INDEX, i);
ret = i2c_smbus_read_block_data(client,
UCD9000_FAN_CONFIG,
data->fan_data[i]);
if (ret < 0)
goto out;
}
i2c_smbus_write_byte_data(client, UCD9000_FAN_CONFIG_INDEX, 0);
info->read_byte_data = ucd9000_read_byte_data;
info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12
| PMBUS_HAVE_FAN34 | PMBUS_HAVE_STATUS_FAN34;
}
ret = pmbus_do_probe(client, mid, info);
if (ret < 0)
goto out;
return 0;
out:
kfree(data);
return ret;
}
static int ucd9000_remove(struct i2c_client *client)
{
int ret;
struct ucd9000_data *data;
data = to_ucd9000_data(pmbus_get_driver_info(client));
ret = pmbus_do_remove(client);
kfree(data);
return ret;
}
/* This is the driver that will be inserted */
static struct i2c_driver ucd9000_driver = {
.driver = {
.name = "ucd9000",
},
.probe = ucd9000_probe,
.remove = ucd9000_remove,
.id_table = ucd9000_id,
};
static int __init ucd9000_init(void)
{
return i2c_add_driver(&ucd9000_driver);
}
static void __exit ucd9000_exit(void)
{
i2c_del_driver(&ucd9000_driver);
}
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("PMBus driver for TI UCD90xxx");
MODULE_LICENSE("GPL");
module_init(ucd9000_init);
module_exit(ucd9000_exit);
/*
* Hardware monitoring driver for ucd9200 series Digital PWM System Controllers
*
* Copyright (C) 2011 Ericsson AB.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c/pmbus.h>
#include "pmbus.h"
#define UCD9200_PHASE_INFO 0xd2
#define UCD9200_DEVICE_ID 0xfd
enum chips { ucd9200, ucd9220, ucd9222, ucd9224, ucd9240, ucd9244, ucd9246,
ucd9248 };
static const struct i2c_device_id ucd9200_id[] = {
{"ucd9200", ucd9200},
{"ucd9220", ucd9220},
{"ucd9222", ucd9222},
{"ucd9224", ucd9224},
{"ucd9240", ucd9240},
{"ucd9244", ucd9244},
{"ucd9246", ucd9246},
{"ucd9248", ucd9248},
{}
};
MODULE_DEVICE_TABLE(i2c, ucd9200_id);
static int ucd9200_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1];
struct pmbus_driver_info *info;
const struct i2c_device_id *mid;
int i, j, ret;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA))
return -ENODEV;
ret = i2c_smbus_read_block_data(client, UCD9200_DEVICE_ID,
block_buffer);
if (ret < 0) {
dev_err(&client->dev, "Failed to read device ID\n");
return ret;
}
block_buffer[ret] = '\0';
dev_info(&client->dev, "Device ID %s\n", block_buffer);
mid = NULL;
for (i = 0; i < ARRAY_SIZE(ucd9200_id); i++) {
mid = &ucd9200_id[i];
if (!strncasecmp(mid->name, block_buffer, strlen(mid->name)))
break;
}
if (!mid || !strlen(mid->name)) {
dev_err(&client->dev, "Unsupported device\n");
return -ENODEV;
}
if (id->driver_data != ucd9200 && id->driver_data != mid->driver_data)
dev_notice(&client->dev,
"Device mismatch: Configured %s, detected %s\n",
id->name, mid->name);
info = kzalloc(sizeof(struct pmbus_driver_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
ret = i2c_smbus_read_block_data(client, UCD9200_PHASE_INFO,
block_buffer);
if (ret < 0) {
dev_err(&client->dev, "Failed to read phase information\n");
goto out;
}
/*
* Calculate number of configured pages (rails) from PHASE_INFO
* register.
* Rails have to be sequential, so we can abort after finding
* the first unconfigured rail.
*/
info->pages = 0;
for (i = 0; i < ret; i++) {
if (!block_buffer[i])
break;
info->pages++;
}
if (!info->pages) {
dev_err(&client->dev, "No rails configured\n");
ret = -ENODEV;
goto out;
}
dev_info(&client->dev, "%d rails configured\n", info->pages);
/*
* Set PHASE registers on all pages to 0xff to ensure that phase
* specific commands will apply to all phases of a given page (rail).
* This only affects the READ_IOUT and READ_TEMPERATURE2 registers.
* READ_IOUT will return the sum of currents of all phases of a rail,
* and READ_TEMPERATURE2 will return the maximum temperature detected
* for the the phases of the rail.
*/
for (i = 0; i < info->pages; i++) {
/*
* Setting PAGE & PHASE fails once in a while for no obvious
* reason, so we need to retry a couple of times.
*/
for (j = 0; j < 3; j++) {
ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i);
if (ret < 0)
continue;
ret = i2c_smbus_write_byte_data(client, PMBUS_PHASE,
0xff);
if (ret < 0)
continue;
break;
}
if (ret < 0) {
dev_err(&client->dev,
"Failed to initialize PHASE registers\n");
goto out;
}
}
if (info->pages > 1)
i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT |
PMBUS_HAVE_IIN | PMBUS_HAVE_PIN |
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP |
PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
for (i = 1; i < info->pages; i++)
info->func[i] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
PMBUS_HAVE_POUT |
PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
/* ucd9240 supports a single fan */
if (mid->driver_data == ucd9240)
info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12;
ret = pmbus_do_probe(client, mid, info);
if (ret < 0)
goto out;
return 0;
out:
kfree(info);
return ret;
}
static int ucd9200_remove(struct i2c_client *client)
{
int ret;
const struct pmbus_driver_info *info;
info = pmbus_get_driver_info(client);
ret = pmbus_do_remove(client);
kfree(info);
return ret;
}
/* This is the driver that will be inserted */
static struct i2c_driver ucd9200_driver = {
.driver = {
.name = "ucd9200",
},
.probe = ucd9200_probe,
.remove = ucd9200_remove,
.id_table = ucd9200_id,
};
static int __init ucd9200_init(void)
{
return i2c_add_driver(&ucd9200_driver);
}
static void __exit ucd9200_exit(void)
{
i2c_del_driver(&ucd9200_driver);
}
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("PMBus driver for TI UCD922x, UCD924x");
MODULE_LICENSE("GPL");
module_init(ucd9200_init);
module_exit(ucd9200_exit);
...@@ -8,17 +8,27 @@ ...@@ -8,17 +8,27 @@
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*
* For further information, see the Documentation/hwmon/sht15 file.
*/ */
/** /**
* struct sht15_platform_data - sht15 connectivity info * struct sht15_platform_data - sht15 connectivity info
* @gpio_data: no. of gpio to which bidirectional data line is connected * @gpio_data: no. of gpio to which bidirectional data line is
* @gpio_sck: no. of gpio to which the data clock is connected. * connected.
* @supply_mv: supply voltage in mv. Overridden by regulator if available. * @gpio_sck: no. of gpio to which the data clock is connected.
**/ * @supply_mv: supply voltage in mv. Overridden by regulator if
* available.
* @checksum: flag to indicate the checksum should be validated.
* @no_otp_reload: flag to indicate no reload from OTP.
* @low_resolution: flag to indicate the temp/humidity resolution to use.
*/
struct sht15_platform_data { struct sht15_platform_data {
int gpio_data; int gpio_data;
int gpio_sck; int gpio_sck;
int supply_mv; int supply_mv;
bool checksum;
bool no_otp_reload;
bool low_resolution;
}; };
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment