diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-23 09:26:20 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-23 09:26:20 -0700 |
commit | 6fad2b5b649fa1fa6ee7293222815f5b62499889 (patch) | |
tree | c2f9735e8868d3ce5decb5a08c56e1a1cc9730af | |
parent | 19504828b4bee5e471bcd35e214bc6fd0d380692 (diff) | |
parent | 4cc452758fd250bb5968c583d825bb0e68d65db0 (diff) |
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
-rw-r--r-- | Documentation/hwmon/adm1275 | 60 | ||||
-rw-r--r-- | Documentation/hwmon/coretemp | 21 | ||||
-rw-r--r-- | Documentation/hwmon/max16065 | 98 | ||||
-rw-r--r-- | Documentation/hwmon/max6642 | 21 | ||||
-rw-r--r-- | Documentation/hwmon/pkgtemp | 36 | ||||
-rw-r--r-- | Documentation/hwmon/sht15 | 74 | ||||
-rw-r--r-- | Documentation/hwmon/ucd9000 | 110 | ||||
-rw-r--r-- | Documentation/hwmon/ucd9200 | 112 | ||||
-rw-r--r-- | drivers/hwmon/Kconfig | 66 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 6 | ||||
-rw-r--r-- | drivers/hwmon/adm1275.c | 121 | ||||
-rw-r--r-- | drivers/hwmon/coretemp.c | 722 | ||||
-rw-r--r-- | drivers/hwmon/max16065.c | 717 | ||||
-rw-r--r-- | drivers/hwmon/max34440.c | 6 | ||||
-rw-r--r-- | drivers/hwmon/max6642.c | 356 | ||||
-rw-r--r-- | drivers/hwmon/max8688.c | 4 | ||||
-rw-r--r-- | drivers/hwmon/pkgtemp.c | 444 | ||||
-rw-r--r-- | drivers/hwmon/pmbus.h | 10 | ||||
-rw-r--r-- | drivers/hwmon/pmbus_core.c | 970 | ||||
-rw-r--r-- | drivers/hwmon/sht15.c | 747 | ||||
-rw-r--r-- | drivers/hwmon/ucd9000.c | 278 | ||||
-rw-r--r-- | drivers/hwmon/ucd9200.c | 210 | ||||
-rw-r--r-- | include/linux/sht15.h | 18 |
23 files changed, 3765 insertions, 1442 deletions
diff --git a/Documentation/hwmon/adm1275 b/Documentation/hwmon/adm1275 new file mode 100644 index 00000000000..6a3a6476cf2 --- /dev/null +++ b/Documentation/hwmon/adm1275 @@ -0,0 +1,60 @@ +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. diff --git a/Documentation/hwmon/coretemp b/Documentation/hwmon/coretemp index 25568f84480..f85e913a340 100644 --- a/Documentation/hwmon/coretemp +++ b/Documentation/hwmon/coretemp @@ -15,8 +15,13 @@ Author: Rudolf Marek 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 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. @@ -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 the Out-Of-Spec bit. Following table summarizes the exported sysfs files: -temp1_input - Core temperature (in millidegrees Celsius). -temp1_max - All cooling devices should be turned on (on Core2). -temp1_crit - Maximum junction temperature (in millidegrees Celsius). -temp1_crit_alarm - Set when Out-of-spec bit is set, never clears. +All Sysfs entries are named with their core_id (represented here by 'X'). +tempX_input - Core temperature (in millidegrees Celsius). +tempX_max - All cooling devices should be turned on (on Core2). +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. -temp1_label - Contains string "Core X", where X is processor - number. +tempX_label - Contains string "Core X", where X is processor + 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 register (UMSR) 0xee has bit 30 set. If not the TjMax is 100 degrees C as diff --git a/Documentation/hwmon/max16065 b/Documentation/hwmon/max16065 new file mode 100644 index 00000000000..44b4f61e04f --- /dev/null +++ b/Documentation/hwmon/max16065 @@ -0,0 +1,98 @@ +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. diff --git a/Documentation/hwmon/max6642 b/Documentation/hwmon/max6642 new file mode 100644 index 00000000000..afbd3e4942e --- /dev/null +++ b/Documentation/hwmon/max6642 @@ -0,0 +1,21 @@ +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. diff --git a/Documentation/hwmon/pkgtemp b/Documentation/hwmon/pkgtemp deleted file mode 100644 index c8e1fb0fadd..00000000000 --- a/Documentation/hwmon/pkgtemp +++ /dev/null @@ -1,36 +0,0 @@ -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. diff --git a/Documentation/hwmon/sht15 b/Documentation/hwmon/sht15 new file mode 100644 index 00000000000..02850bdfac1 --- /dev/null +++ b/Documentation/hwmon/sht15 @@ -0,0 +1,74 @@ +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. diff --git a/Documentation/hwmon/ucd9000 b/Documentation/hwmon/ucd9000 new file mode 100644 index 00000000000..40ca6db50c4 --- /dev/null +++ b/Documentation/hwmon/ucd9000 @@ -0,0 +1,110 @@ +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. diff --git a/Documentation/hwmon/ucd9200 b/Documentation/hwmon/ucd9200 new file mode 100644 index 00000000000..3c58607f72f --- /dev/null +++ b/Documentation/hwmon/ucd9200 @@ -0,0 +1,112 @@ +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. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 50e40dbd8bb..43221beb9e9 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -408,13 +408,6 @@ config SENSORS_CORETEMP sensor inside your CPU. Most of the family 6 CPUs 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 tristate "IBM Active Energy Manager temperature/power sensors and control" select IPMI_SI @@ -708,6 +701,22 @@ config SENSORS_MAX1111 This driver can also be built as a module. If so, the module 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 tristate "Maxim MAX1619 sensor chip" depends on I2C @@ -727,6 +736,17 @@ config SENSORS_MAX6639 This driver can also be built as a module. If so, the module 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 tristate "Maxim MAX6650 sensor chip" depends on I2C && EXPERIMENTAL @@ -800,6 +820,16 @@ config SENSORS_PMBUS This driver can also be built as a module. If so, the module will be called pmbus. +config SENSORS_ADM1275 + tristate "Analog Devices ADM1275" + default n + help + If you say yes here you get hardware monitoring support for Analog + Devices ADM1275 Hot-Swap Controller and Digital Power Monitor. + + This driver can also be built as a module. If so, the module will + be called adm1275. + config SENSORS_MAX16064 tristate "Maxim MAX16064" default n @@ -830,6 +860,28 @@ config SENSORS_MAX8688 This driver can also be built as a module. If so, the module will be called max8688. +config SENSORS_UCD9000 + tristate "TI UCD90120, UCD90124, UCD9090, UCD90910" + default n + help + If you say yes here you get hardware monitoring support for TI + UCD90120, UCD90124, UCD9090, UCD90910 Sequencer and System Health + Controllers. + + This driver can also be built as a module. If so, the module will + be called ucd9000. + +config SENSORS_UCD9200 + tristate "TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, UCD9248" + default n + help + If you say yes here you get hardware monitoring support for TI + UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and UCD9248 + Digital PWM System Controllers. + + This driver can also be built as a module. If so, the module will + be called ucd9200. + endif # PMBUS config SENSORS_SHT15 diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 967d0ea9447..28e8d52f637 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -40,7 +40,6 @@ obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o -obj-$(CONFIG_SENSORS_PKGTEMP) += pkgtemp.o obj-$(CONFIG_SENSORS_DME1737) += dme1737.o obj-$(CONFIG_SENSORS_DS620) += ds620.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o @@ -83,8 +82,10 @@ obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o +obj-$(CONFIG_SENSORS_MAX16065) += max16065.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX6639) += max6639.o +obj-$(CONFIG_SENSORS_MAX6642) += max6642.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o @@ -118,9 +119,12 @@ obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o # PMBus drivers obj-$(CONFIG_PMBUS) += pmbus_core.o obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o +obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o obj-$(CONFIG_SENSORS_MAX16064) += max16064.o obj-$(CONFIG_SENSORS_MAX34440) += max34440.o obj-$(CONFIG_SENSORS_MAX8688) += max8688.o +obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o +obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG diff --git a/drivers/hwmon/adm1275.c b/drivers/hwmon/adm1275.c new file mode 100644 index 00000000000..c2ee2048ab9 --- /dev/null +++ b/drivers/hwmon/adm1275.c @@ -0,0 +1,121 @@ +/* + * 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); diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 194ca0aa8b0..5c7cd60d5f9 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -35,128 +35,152 @@ #include <linux/platform_device.h> #include <linux/cpu.h> #include <linux/pci.h> +#include <linux/smp.h> #include <asm/msr.h> #include <asm/processor.h> -#include <asm/smp.h> #define DRVNAME "coretemp" -typedef enum { SHOW_TEMP, SHOW_TJMAX, SHOW_TTARGET, SHOW_LABEL, - SHOW_NAME } SHOW; +#define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */ +#define NUM_REAL_CORES 16 /* Number of Real cores per cpu */ +#define CORETEMP_NAME_LENGTH 17 /* String Length of attrs */ +#define MAX_ATTRS 5 /* Maximum no of per-core attrs */ +#define MAX_CORE_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. */ - -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 */ +struct temp_data { int temp; - int tjmax; 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; }; -/* - * Sysfs stuff - */ +/* Platform Data per Physical CPU */ +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 - *devattr, char *buf) +struct pdev_entry { + 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 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) - ret = sprintf(buf, "%s\n", data->name); - else /* show label */ - ret = sprintf(buf, "Core %d\n", data->core_id); - return ret; + if (tdata->is_pkg_data) + return sprintf(buf, "Physical id %u\n", pdata->phys_proc_id); + + return sprintf(buf, "Core %u\n", tdata->cpu_core_id); } -static ssize_t show_alarm(struct device *dev, struct device_attribute - *devattr, char *buf) +static ssize_t show_crit_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) { - struct coretemp_data *data = coretemp_update_device(dev); - /* read the Out-of-spec log, never clear */ - return sprintf(buf, "%d\n", data->alarm); + u32 eax, edx; + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct platform_data *pdata = dev_get_drvdata(dev); + struct temp_data *tdata = pdata->core_data[attr->index]; + + rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); + + return sprintf(buf, "%d\n", (eax >> 5) & 1); } -static ssize_t show_temp(struct device *dev, - struct device_attribute *devattr, char *buf) +static ssize_t show_tjmax(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct coretemp_data *data = coretemp_update_device(dev); - int err; + struct platform_data *pdata = dev_get_drvdata(dev); - 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; + return sprintf(buf, "%d\n", pdata->core_data[attr->index]->tjmax); } -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 *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 ssize_t show_ttarget(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct platform_data *pdata = dev_get_drvdata(dev); -static const struct attribute_group coretemp_group = { - .attrs = coretemp_attributes, -}; + return sprintf(buf, "%d\n", pdata->core_data[attr->index]->ttarget); +} -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); - - mutex_lock(&data->update_lock); + u32 eax, edx; + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct platform_data *pdata = dev_get_drvdata(dev); + struct temp_data *tdata = pdata->core_data[attr->index]; - if (!data->valid || time_after(jiffies, data->last_updated + HZ)) { - u32 eax, edx; + mutex_lock(&tdata->update_lock); - data->valid = 0; - rdmsr_on_cpu(data->id, MSR_IA32_THERM_STATUS, &eax, &edx); - data->alarm = (eax >> 5) & 1; - /* update only if data has been valid */ + /* Check whether the time interval has elapsed */ + if (!tdata->valid || time_after(jiffies, tdata->last_updated + HZ)) { + rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); + tdata->valid = 0; + /* Check whether the data is valid */ if (eax & 0x80000000) { - data->temp = data->tjmax - (((eax >> 16) - & 0x7f) * 1000); - data->valid = 1; - } else { - dev_dbg(dev, "Temperature data invalid (0x%x)\n", eax); + tdata->temp = tdata->tjmax - + ((eax >> 16) & 0x7f) * 1000; + tdata->valid = 1; } - data->last_updated = jiffies; + tdata->last_updated = jiffies; } - mutex_unlock(&data->update_lock); - return data; + mutex_unlock(&tdata->update_lock); + 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 */ @@ -169,9 +193,8 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device * /* 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; - } /* Atom CPUs */ @@ -190,14 +213,14 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device * pci_dev_put(host_bridge); } - if ((c->x86_model > 0xe) && (usemsr_ee)) { + if (c->x86_model > 0xe && usemsr_ee) { u8 platform_id; - /* Now we can detect the mobile CPU using Intel provided table - http://softwarecommunity.intel.com/Wiki/Mobility/720.htm - For Core2 cores, check MSR 0x17, bit 28 1 = Mobile CPU - */ - + /* + * Now we can detect the mobile CPU using Intel provided table + * 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); if (err) { dev_warn(dev, @@ -205,20 +228,26 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device * " CPU\n"); usemsr_ee = 0; } 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 - someone at Intel please ask */ + /* + * Trust bit 28 up to Penryn, I could not find any + * documentation on that; if you happen to know + * someone at Intel please ask + */ usemsr_ee = 0; } else { /* Platform ID bits 52:50 (EDX starts at bit 32) */ platform_id = (edx >> 18) & 0x7; - /* Mobile Penryn CPU seems to be platform ID 7 or 5 - (guesswork) */ - if ((c->x86_model == 0x17) && - ((platform_id == 5) || (platform_id == 7))) { - /* If MSR EE bit is set, set it to 90 degrees C, - otherwise 105 degrees C */ + /* + * Mobile Penryn CPU seems to be platform ID 7 or 5 + * (guesswork) + */ + if (c->x86_model == 0x17 && + (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 = 105000; } @@ -226,7 +255,6 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device * } if (usemsr_ee) { - err = rdmsr_safe_on_cpu(id, 0xee, &eax, &edx); if (err) { dev_warn(dev, @@ -235,25 +263,28 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device * } else if (eax & 0x40000000) { tjmax = tjmax_ee; } - /* if we dont use msr EE it means we are desktop CPU (with exeception - of Atom) */ } 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"); } return tjmax; } -static int __devinit get_tjmax(struct cpuinfo_x86 *c, u32 id, - struct device *dev) +static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) { /* The 100C is default for both mobile and non mobile CPUs */ int err; u32 eax, edx; 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); if (err) { 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, * If the TjMax is not plausible, an assumption * will be used */ - if ((val > 80) && (val < 120)) { + if (val > 80 && val < 120) { dev_info(dev, "TjMax is %d C.\n", val); return val * 1000; } @@ -300,115 +331,293 @@ static void __devinit get_ucode_rev_on_cpu(void *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; - u32 eax, edx; + u32 eax, edx, val; - if (!(data = kzalloc(sizeof(struct coretemp_data), GFP_KERNEL))) { - err = -ENOMEM; - dev_err(&pdev->dev, "Out of memory\n"); - goto exit; + err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); + if (!err) { + val = (eax >> 16) & 0xff; + 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; -#ifdef CONFIG_SMP - data->core_id = c->cpu_core_id; -#endif - data->name = "coretemp"; - mutex_init(&data->update_lock); +static int create_name_attr(struct platform_data *pdata, struct device *dev) +{ + pdata->name_attr.attr.name = "name"; + pdata->name_attr.attr.mode = S_IRUGO; + pdata->name_attr.show = show_name; + return device_create_file(dev, &pdata->name_attr); +} - /* test if we can access the THERM_STATUS MSR */ - err = rdmsr_safe_on_cpu(data->id, MSR_IA32_THERM_STATUS, &eax, &edx); - if (err) { - dev_err(&pdev->dev, - "Unable to access THERM_STATUS MSR, giving up\n"); - goto exit_free; +static int create_core_attrs(struct temp_data *tdata, struct device *dev, + int attr_no) +{ + int err, i; + static ssize_t (*rd_ptr[MAX_ATTRS]) (struct device *dev, + 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: - Readings might stop update when processor visited too deep sleep, - fixed for stepping D0 (6EC). - */ +static int chk_ucode_version(struct platform_device *pdev) +{ + 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 */ - 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); if (err) { dev_err(&pdev->dev, "Cannot determine microcode revision of " - "CPU#%u (%d)!\n", data->id, err); - err = -ENODEV; - goto exit_free; + "CPU#%u (%d)!\n", pdev->id, err); + return -ENODEV; } else if (edx < 0x39) { - err = -ENODEV; dev_err(&pdev->dev, "Errata AE18 not fixed, update BIOS or " "microcode of the CPU!\n"); - goto exit_free; + return -ENODEV; } } + return 0; +} - data->tjmax = get_tjmax(c, data->id, &pdev->dev); - platform_set_drvdata(pdev, data); +static struct platform_device *coretemp_get_pdev(unsigned int cpu) +{ + 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 - * on older CPUs but not in this register, - * Atoms don't have it either. + * Find attr number for sysfs: + * We map the attr number to core id of the CPU + * 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)) { - 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; - } - } + if (attr_no > MAX_CORE_DATA - 1) + return -ERANGE; + + /* Skip if it is a HT core, Not an error */ + if (pdata->core_data[attr_no] != NULL) + return 0; - if ((err = sysfs_create_group(&pdev->dev.kobj, &coretemp_group))) - goto exit_dev; + tdata = init_temp_data(cpu, pkg_flag); + if (!tdata) + return -ENOMEM; - 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; - } + /* Test if we can access the status register */ + err = rdmsr_safe_on_cpu(cpu, tdata->status_reg, &eax, &edx); + if (err) + goto exit_free; + + /* 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; +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: - sysfs_remove_group(&pdev->dev.kobj, &coretemp_group); -exit_dev: - device_remove_file(&pdev->dev, &sensor_dev_attr_temp1_max.dev_attr); + /* Remove the sysfs attributes */ + for (i = 0; i < MAX_ATTRS; i++) + device_remove_file(dev, &tdata->sd_attrs[i].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: - kfree(data); -exit: + kfree(pdata); return err; } 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); - sysfs_remove_group(&pdev->dev.kobj, &coretemp_group); - device_remove_file(&pdev->dev, &sensor_dev_attr_temp1_max.dev_attr); + device_remove_file(&pdev->dev, &pdata->name_attr); + hwmon_device_unregister(pdata->hwmon_dev); platform_set_drvdata(pdev, NULL); - kfree(data); + kfree(pdata); return 0; } @@ -421,50 +630,14 @@ static struct platform_driver coretemp_driver = { .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) { int err; struct platform_device *pdev; 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); -#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); if (!pdev) { err = -ENOMEM; @@ -486,10 +659,9 @@ static int __cpuinit coretemp_device_add(unsigned int cpu) pdev_entry->pdev = pdev; pdev_entry->cpu = cpu; -#ifdef CONFIG_SMP - pdev_entry->phys_proc_id = c->phys_proc_id; - pdev_entry->cpu_core_id = c->cpu_core_id; -#endif + pdev_entry->phys_proc_id = TO_PHYS_ID(cpu); + pdev_entry->cpu_core_id = TO_CORE_ID(cpu); + list_add_tail(&pdev_entry->list, &pdev_list); mutex_unlock(&pdev_list_mutex); @@ -504,28 +676,108 @@ exit: return err; } -static void __cpuinit coretemp_device_remove(unsigned int cpu) +static void coretemp_device_remove(unsigned int cpu) { - struct pdev_entry *p; - unsigned int i; + struct pdev_entry *p, *n; + u16 phys_proc_id = TO_PHYS_ID(cpu); mutex_lock(&pdev_list_mutex); - list_for_each_entry(p, &pdev_list, list) { - if (p->cpu != cpu) + list_for_each_entry_safe(p, n, &pdev_list, list) { + if (p->phys_proc_id != phys_proc_id) continue; - platform_device_unregister(p->pdev); list_del(&p->list); - mutex_unlock(&pdev_list_mutex); kfree(p); - for_each_cpu(i, cpu_sibling_mask(cpu)) - if (i != cpu && !coretemp_device_add(i)) - break; - return; } 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, unsigned long action, void *hcpu) { @@ -534,10 +786,10 @@ static int __cpuinit coretemp_cpu_callback(struct notifier_block *nfb, switch (action) { case CPU_ONLINE: case CPU_DOWN_FAILED: - coretemp_device_add(cpu); + get_core_online(cpu); break; case CPU_DOWN_PREPARE: - coretemp_device_remove(cpu); + put_core_offline(cpu); break; } return NOTIFY_OK; @@ -560,7 +812,7 @@ static int __init coretemp_init(void) goto exit; for_each_online_cpu(i) - coretemp_device_add(i); + get_core_online(i); #ifndef CONFIG_HOTPLUG_CPU if (list_empty(&pdev_list)) { diff --git a/drivers/hwmon/max16065.c b/drivers/hwmon/max16065.c new file mode 100644 index 00000000000..d94a24fdf4b --- /dev/null +++ b/drivers/hwmon/max16065.c @@ -0,0 +1,717 @@ +/* + * 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); diff --git a/drivers/hwmon/max34440.c b/drivers/hwmon/max34440.c index 992b701b4c5..db11e1a175b 100644 --- a/drivers/hwmon/max34440.c +++ b/drivers/hwmon/max34440.c @@ -32,7 +32,7 @@ enum chips { max34440, max34441 }; #define MAX34440_STATUS_OT_FAULT (1 << 5) #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 mfg_status; @@ -108,7 +108,7 @@ static struct pmbus_driver_info max34440_info[] = { .func[11] = 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, - .get_status = max34440_get_status, + .read_byte_data = max34440_read_byte_data, }, [max34441] = { .pages = 12, @@ -149,7 +149,7 @@ static struct pmbus_driver_info max34440_info[] = { .func[9] = 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, - .get_status = max34440_get_status, + .read_byte_data = max34440_read_byte_data, }, }; diff --git a/drivers/hwmon/max6642.c b/drivers/hwmon/max6642.c new file mode 100644 index 00000000000..0f9fc40379c --- /dev/null +++ b/drivers/hwmon/max6642.c @@ -0,0 +1,356 @@ +/* + * 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); diff --git a/drivers/hwmon/max8688.c b/drivers/hwmon/max8688.c index 8ebfef2ecf2..7fb93f4e9f2 100644 --- a/drivers/hwmon/max8688.c +++ b/drivers/hwmon/max8688.c @@ -37,7 +37,7 @@ #define MAX8688_STATUS_OT_FAULT (1 << 13) #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 mfg_status; @@ -110,7 +110,7 @@ static struct pmbus_driver_info max8688_info = { .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_TEMP, - .get_status = max8688_get_status, + .read_byte_data = max8688_read_byte_data, }; static int max8688_probe(struct i2c_client *client, diff --git a/drivers/hwmon/pkgtemp.c b/drivers/hwmon/pkgtemp.c deleted file mode 100644 index 21c817d9812..00000000000 --- a/drivers/hwmon/pkgtemp.c +++ /dev/null @@ -1,444 +0,0 @@ -/* - * 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) diff --git a/drivers/hwmon/pmbus.h b/drivers/hwmon/pmbus.h index a81f7f22876..50647ab7235 100644 --- a/drivers/hwmon/pmbus.h +++ b/drivers/hwmon/pmbus.h @@ -281,13 +281,11 @@ struct pmbus_driver_info { u32 func[PMBUS_PAGES]; /* Functionality, per page */ /* - * The get_status function maps manufacturing specific status values - * into PMBus standard status values. - * This function is optional and only necessary if chip specific status - * register values have to be mapped into standard PMBus status register - * values. + * The following functions map manufacturing specific register values + * to PMBus standard register values. Specify only if mapping is + * necessary. */ - 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. * This function is only necessary if a chip driver supports multiple diff --git a/drivers/hwmon/pmbus_core.c b/drivers/hwmon/pmbus_core.c index 196ffafafd8..98799bab69c 100644 --- a/drivers/hwmon/pmbus_core.c +++ b/drivers/hwmon/pmbus_core.c @@ -270,18 +270,22 @@ const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client) } 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); const struct pmbus_driver_info *info = data->info; int status; - if (info->get_status) { - status = info->get_status(client, page, reg); + if (info->read_byte_data) { + status = info->read_byte_data(client, page, reg); if (status != -ENODATA) return status; } - return pmbus_read_byte_data(client, page, reg); + return pmbus_read_byte_data(client, page, reg); } 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)) continue; 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++) { if (!(info->func[i] & PMBUS_HAVE_STATUS_IOUT)) continue; 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++) { if (!(info->func[i] & PMBUS_HAVE_STATUS_TEMP)) continue; data->status[PB_STATUS_TEMP_BASE + i] - = pmbus_get_status(client, i, - PMBUS_STATUS_TEMPERATURE); + = _pmbus_read_byte_data(client, i, + PMBUS_STATUS_TEMPERATURE); } for (i = 0; i < info->pages; i++) { if (!(info->func[i] & PMBUS_HAVE_STATUS_FAN12)) continue; 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++) { if (!(info->func[i] & PMBUS_HAVE_STATUS_FAN34)) continue; 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) 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++) { struct pmbus_sensor *sensor = &data->sensors[i]; @@ -793,53 +800,6 @@ static void pmbus_add_label(struct pmbus_data *data, 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. * To keep things simple, only make a rough high estimate. @@ -900,499 +860,431 @@ static void pmbus_find_max_attr(struct i2c_client *client, /* * 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 - */ - in_index = 1; - if (info->func[0] & PMBUS_HAVE_VIN) { - bool have_alarm = false; - - i0 = data->num_sensors; - pmbus_add_label(data, "in", in_index, "vin", 0); - 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)) { - i1 = data->num_sensors; - pmbus_add_sensor(data, "in", "min", in_index, - 0, PMBUS_VIN_UV_WARN_LIMIT, - PSC_VOLTAGE_IN, false, false); - if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) { - pmbus_add_boolean_reg(data, "in", "min_alarm", - in_index, - PB_STATUS_INPUT_BASE, - PB_VOLTAGE_UV_WARNING); - have_alarm = true; - } - } - if (pmbus_check_word_register(client, 0, - PMBUS_VIN_UV_FAULT_LIMIT)) { - i1 = data->num_sensors; - 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) { - pmbus_add_boolean_reg(data, "in", "lcrit_alarm", - in_index, - PB_STATUS_INPUT_BASE, - PB_VOLTAGE_UV_FAULT); - have_alarm = true; - } - } - if (pmbus_check_word_register(client, 0, - PMBUS_VIN_OV_WARN_LIMIT)) { - i1 = data->num_sensors; - pmbus_add_sensor(data, "in", "max", in_index, - 0, PMBUS_VIN_OV_WARN_LIMIT, - PSC_VOLTAGE_IN, false, false); - if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) { - pmbus_add_boolean_reg(data, "in", "max_alarm", - in_index, - PB_STATUS_INPUT_BASE, - PB_VOLTAGE_OV_WARNING); - have_alarm = true; - } - } - if (pmbus_check_word_register(client, 0, - PMBUS_VIN_OV_FAULT_LIMIT)) { - i1 = data->num_sensors; - pmbus_add_sensor(data, "in", "crit", in_index, - 0, PMBUS_VIN_OV_FAULT_LIMIT, - PSC_VOLTAGE_IN, false, false); - if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) { - pmbus_add_boolean_reg(data, "in", "crit_alarm", - in_index, - PB_STATUS_INPUT_BASE, - PB_VOLTAGE_OV_FAULT); +/* + * The pmbus_limit_attr structure describes a single limit attribute + * and its associated alarm attribute. + */ +struct pmbus_limit_attr { + u8 reg; /* Limit register */ + const char *attr; /* Attribute name */ + const char *alarm; /* Alarm attribute name */ + u32 sbit; /* Alarm attribute status bit */ +}; + +/* + * The pmbus_sensor_attr structure describes one sensor attribute. This + * description includes a reference to the associated limit attributes. + */ +struct pmbus_sensor_attr { + u8 reg; /* sensor register */ + enum pmbus_sensor_classes class;/* sensor class */ + const char *label; /* sensor label */ + bool paged; /* true if paged sensor */ + bool update; /* true if update needed */ + bool compare; /* true if compare function needed */ + u32 func; /* sensor mask */ + u32 sfunc; /* sensor status mask */ + int sbase; /* status base register */ + u32 gbit; /* generic status bit */ + const struct pmbus_limit_attr *limit;/* limit registers */ + int nlimit; /* # of limit registers */ +}; + +/* + * Add a set of limit attributes and, if supported, the associated + * alarm attributes. + */ +static bool pmbus_add_limit_attrs(struct i2c_client *client, + struct pmbus_data *data, + const struct pmbus_driver_info *info, + const char *name, int index, int page, + int cbase, + const struct pmbus_sensor_attr *attr) +{ + const struct pmbus_limit_attr *l = attr->limit; + int nlimit = attr->nlimit; + bool have_alarm = false; + int i, cindex; + + for (i = 0; i < nlimit; i++) { + if (pmbus_check_word_register(client, page, l->reg)) { + cindex = data->num_sensors; + pmbus_add_sensor(data, name, l->attr, index, page, + l->reg, attr->class, attr->update, + false); + if (info->func[page] & attr->sfunc) { + if (attr->compare) { + pmbus_add_boolean_cmp(data, name, + l->alarm, index, + cbase, cindex, + attr->sbase + page, l->sbit); + } else { + pmbus_add_boolean_reg(data, name, + l->alarm, index, + attr->sbase + page, l->sbit); + } have_alarm = true; } } - /* - * 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++; + l++; } + return have_alarm; +} - /* - * Output voltage sensors - */ - for (page = 0; page < info->pages; page++) { - bool have_alarm = false; - - if (!(info->func[page] & PMBUS_HAVE_VOUT)) - continue; - - i0 = data->num_sensors; - pmbus_add_label(data, "in", in_index, "vout", page + 1); - pmbus_add_sensor(data, "in", "input", in_index, page, - PMBUS_READ_VOUT, PSC_VOLTAGE_OUT, true, true); - if (pmbus_check_word_register(client, page, - PMBUS_VOUT_UV_WARN_LIMIT)) { - i1 = data->num_sensors; - pmbus_add_sensor(data, "in", "min", in_index, page, - PMBUS_VOUT_UV_WARN_LIMIT, - 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; - } - } +static void pmbus_add_sensor_attrs_one(struct i2c_client *client, + struct pmbus_data *data, + const struct pmbus_driver_info *info, + const char *name, + int index, int page, + const struct pmbus_sensor_attr *attr) +{ + bool have_alarm; + int cbase = data->num_sensors; + + if (attr->label) + pmbus_add_label(data, name, index, attr->label, + attr->paged ? page + 1 : 0); + pmbus_add_sensor(data, name, "input", index, page, attr->reg, + attr->class, true, true); + if (attr->sfunc) { + have_alarm = pmbus_add_limit_attrs(client, data, info, name, + index, page, cbase, attr); /* * Add generic alarm attribute only if there are no individual - * attributes. + * alarm attributes, and if there is a global alarm bit. */ - if (!have_alarm) - pmbus_add_boolean_reg(data, "in", "alarm", - in_index, + if (!have_alarm && attr->gbit) + pmbus_add_boolean_reg(data, name, "alarm", index, PB_STATUS_BASE + page, - PB_STATUS_VOUT_OV); - in_index++; + attr->gbit); } +} - /* - * Current sensors - */ +static void pmbus_add_sensor_attrs(struct i2c_client *client, + 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; - /* - * Input current sensors - */ - in_index = 1; - if (info->func[0] & PMBUS_HAVE_IIN) { - i0 = data->num_sensors; - pmbus_add_label(data, "curr", in_index, "iin", 0); - pmbus_add_sensor(data, "curr", "input", in_index, 0, - PMBUS_READ_IIN, PSC_CURRENT_IN, true, true); - if (pmbus_check_word_register(client, 0, - PMBUS_IIN_OC_WARN_LIMIT)) { - 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); + index = 1; + for (i = 0; i < nattrs; i++) { + int page, pages; + + pages = attrs->paged ? info->pages : 1; + for (page = 0; page < pages; page++) { + if (!(info->func[page] & attrs->func)) + continue; + pmbus_add_sensor_attrs_one(client, data, info, name, + index, page, attrs); + index++; } - in_index++; + attrs++; } +} - /* - * Output current sensors - */ - for (page = 0; page < info->pages; page++) { - bool have_alarm = false; - - if (!(info->func[page] & PMBUS_HAVE_IOUT)) - continue; - - i0 = data->num_sensors; - pmbus_add_label(data, "curr", in_index, "iout", page + 1); - pmbus_add_sensor(data, "curr", "input", in_index, page, - PMBUS_READ_IOUT, PSC_CURRENT_OUT, true, true); - if (pmbus_check_word_register(client, page, - PMBUS_IOUT_OC_WARN_LIMIT)) { - i1 = data->num_sensors; - pmbus_add_sensor(data, "curr", "max", in_index, page, - PMBUS_IOUT_OC_WARN_LIMIT, - PSC_CURRENT_OUT, false, false); - if (info->func[page] & PMBUS_HAVE_STATUS_IOUT) { - pmbus_add_boolean_reg(data, "curr", "max_alarm", - in_index, - PB_STATUS_IOUT_BASE + - page, PB_IOUT_OC_WARNING); - have_alarm = true; - } - } - if (pmbus_check_word_register(client, page, - PMBUS_IOUT_UC_FAULT_LIMIT)) { - i1 = data->num_sensors; - pmbus_add_sensor(data, "curr", "lcrit", in_index, page, - PMBUS_IOUT_UC_FAULT_LIMIT, - PSC_CURRENT_OUT, false, false); - if (info->func[page] & PMBUS_HAVE_STATUS_IOUT) { - pmbus_add_boolean_reg(data, "curr", - "lcrit_alarm", - in_index, - PB_STATUS_IOUT_BASE + - page, PB_IOUT_UC_FAULT); - have_alarm = true; - } - } - if (pmbus_check_word_register(client, page, - PMBUS_IOUT_OC_FAULT_LIMIT)) { - i1 = data->num_sensors; - 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_limit_attr vin_limit_attrs[] = { + { + .reg = PMBUS_VIN_UV_WARN_LIMIT, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_VOLTAGE_UV_WARNING, + }, { + .reg = PMBUS_VIN_UV_FAULT_LIMIT, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_VOLTAGE_UV_FAULT, + }, { + .reg = PMBUS_VIN_OV_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_VOLTAGE_OV_WARNING, + }, { + .reg = PMBUS_VIN_OV_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_VOLTAGE_OV_FAULT, + }, +}; + +static const struct pmbus_limit_attr vout_limit_attrs[] = { + { + .reg = PMBUS_VOUT_UV_WARN_LIMIT, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_VOLTAGE_UV_WARNING, + }, { + .reg = PMBUS_VOUT_UV_FAULT_LIMIT, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_VOLTAGE_UV_FAULT, + }, { + .reg = PMBUS_VOUT_OV_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_VOLTAGE_OV_WARNING, + }, { + .reg = PMBUS_VOUT_OV_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_VOLTAGE_OV_FAULT, } +}; - /* - * Power sensors - */ - /* - * Input Power sensors - */ - in_index = 1; - if (info->func[0] & PMBUS_HAVE_PIN) { - i0 = data->num_sensors; - pmbus_add_label(data, "power", in_index, "pin", 0); - pmbus_add_sensor(data, "power", "input", in_index, - 0, PMBUS_READ_PIN, PSC_POWER, true, true); - if (pmbus_check_word_register(client, 0, - PMBUS_PIN_OP_WARN_LIMIT)) { - i1 = data->num_sensors; - pmbus_add_sensor(data, "power", "max", in_index, - 0, PMBUS_PIN_OP_WARN_LIMIT, PSC_POWER, - false, false); - if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) - pmbus_add_boolean_reg(data, "power", - "alarm", - in_index, - PB_STATUS_INPUT_BASE, - PB_PIN_OP_WARNING); - } - in_index++; +static const struct pmbus_sensor_attr voltage_attributes[] = { + { + .reg = PMBUS_READ_VIN, + .class = PSC_VOLTAGE_IN, + .label = "vin", + .func = PMBUS_HAVE_VIN, + .sfunc = PMBUS_HAVE_STATUS_INPUT, + .sbase = PB_STATUS_INPUT_BASE, + .gbit = PB_STATUS_VIN_UV, + .limit = vin_limit_attrs, + .nlimit = ARRAY_SIZE(vin_limit_attrs), + }, { + .reg = PMBUS_READ_VCAP, + .class = PSC_VOLTAGE_IN, + .label = "vcap", + .func = PMBUS_HAVE_VCAP, + }, { + .reg = PMBUS_READ_VOUT, + .class = PSC_VOLTAGE_OUT, + .label = "vout", + .paged = true, + .func = PMBUS_HAVE_VOUT, + .sfunc = PMBUS_HAVE_STATUS_VOUT, + .sbase = PB_STATUS_VOUT_BASE, + .gbit = PB_STATUS_VOUT_OV, + .limit = vout_limit_attrs, + .nlimit = ARRAY_SIZE(vout_limit_attrs), } +}; - /* - * Output Power sensors - */ - for (page = 0; page < info->pages; page++) { - bool need_alarm = false; +/* Current attributes */ + +static const struct pmbus_limit_attr iin_limit_attrs[] = { + { + .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)) - continue; +static const struct pmbus_limit_attr iout_limit_attrs[] = { + { + .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; - pmbus_add_label(data, "power", in_index, "pout", page + 1); - pmbus_add_sensor(data, "power", "input", in_index, page, - PMBUS_READ_POUT, PSC_POWER, true, true); - /* - * Per hwmon sysfs API, power_cap is to be used to limit output - * power. - * We have two registers related to maximum output power, - * PMBUS_POUT_MAX and PMBUS_POUT_OP_WARN_LIMIT. - * PMBUS_POUT_MAX matches the powerX_cap attribute definition. - * There is no attribute in the API to match - * PMBUS_POUT_OP_WARN_LIMIT. We use powerX_max for now. - */ - if (pmbus_check_word_register(client, page, PMBUS_POUT_MAX)) { - i1 = data->num_sensors; - pmbus_add_sensor(data, "power", "cap", in_index, page, - PMBUS_POUT_MAX, PSC_POWER, - false, false); - need_alarm = true; - } - if (pmbus_check_word_register(client, page, - 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++; +static const struct pmbus_sensor_attr current_attributes[] = { + { + .reg = PMBUS_READ_IIN, + .class = PSC_CURRENT_IN, + .label = "iin", + .func = PMBUS_HAVE_IIN, + .sfunc = PMBUS_HAVE_STATUS_INPUT, + .sbase = PB_STATUS_INPUT_BASE, + .limit = iin_limit_attrs, + .nlimit = ARRAY_SIZE(iin_limit_attrs), + }, { + .reg = PMBUS_READ_IOUT, + .class = PSC_CURRENT_OUT, + .label = "iout", + .paged = true, + .func = PMBUS_HAVE_IOUT, + .sfunc = PMBUS_HAVE_STATUS_IOUT, + .sbase = PB_STATUS_IOUT_BASE, + .gbit = PB_STATUS_IOUT_OC, + .limit = iout_limit_attrs, + .nlimit = ARRAY_SIZE(iout_limit_attrs), } +}; - /* - * Temperature sensors - */ - in_index = 1; - for (page = 0; page < info->pages; page++) { - int t; +/* Power attributes */ - for (t = 0; t < ARRAY_SIZE(pmbus_temp_registers); t++) { - bool have_alarm = false; +static const struct pmbus_limit_attr pin_limit_attrs[] = { + { + .reg = PMBUS_PIN_OP_WARN_LIMIT, + .attr = "max", + .alarm = "alarm", + .sbit = PB_PIN_OP_WARNING, + } +}; - /* - * A PMBus chip may support any combination of - * temperature registers on any page. So we can not - * abort after a failure to detect a register, but have - * to continue checking for all registers on all pages. - */ - if (!(info->func[page] & pmbus_temp_flags[t])) - continue; +static const struct pmbus_limit_attr pout_limit_attrs[] = { + { + .reg = PMBUS_POUT_MAX, + .attr = "cap", + .alarm = "cap_alarm", + .sbit = PB_POWER_LIMITING, + }, { + .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 - (client, page, pmbus_temp_registers[t])) - continue; +static const struct pmbus_sensor_attr power_attributes[] = { + { + .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; - pmbus_add_sensor(data, "temp", "input", in_index, page, - pmbus_temp_registers[t], - PSC_TEMPERATURE, true, true); +/* Temperature atributes */ + +static const struct pmbus_limit_attr temp_limit_attrs[] = { + { + .reg = PMBUS_UT_WARN_LIMIT, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_TEMP_UT_WARNING, + }, { + .reg = PMBUS_UT_FAULT_LIMIT, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_TEMP_UT_FAULT, + }, { + .reg = PMBUS_OT_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_TEMP_OT_WARNING, + }, { + .reg = PMBUS_OT_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_TEMP_OT_FAULT, + } +}; - /* - * PMBus provides only one status register for TEMP1-3. - * Thus, we can not use the status register to determine - * which of the three sensors actually caused an alarm. - * Always compare current temperature against the limit - * registers to determine alarm conditions for a - * specific sensor. - * - * Since there is only one set of limit registers for - * up to three temperature sensors, we need to update - * all limit registers after the limit was changed for - * one of the sensors. This ensures that correct limits - * are reported for all temperature sensors. - */ - if (pmbus_check_word_register - (client, page, PMBUS_UT_WARN_LIMIT)) { - i1 = data->num_sensors; - pmbus_add_sensor(data, "temp", "min", in_index, - page, PMBUS_UT_WARN_LIMIT, - PSC_TEMPERATURE, true, false); - if (info->func[page] & PMBUS_HAVE_STATUS_TEMP) { - pmbus_add_boolean_cmp(data, "temp", - "min_alarm", in_index, i1, i0, - PB_STATUS_TEMP_BASE + page, - PB_TEMP_UT_WARNING); - have_alarm = true; - } - } - if (pmbus_check_word_register(client, page, - PMBUS_UT_FAULT_LIMIT)) { - i1 = data->num_sensors; - pmbus_add_sensor(data, "temp", "lcrit", - in_index, page, - PMBUS_UT_FAULT_LIMIT, - PSC_TEMPERATURE, true, false); - if (info->func[page] & PMBUS_HAVE_STATUS_TEMP) { - pmbus_add_boolean_cmp(data, "temp", - "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 struct pmbus_sensor_attr temp_attributes[] = { + { + .reg = PMBUS_READ_TEMPERATURE_1, + .class = PSC_TEMPERATURE, + .paged = true, + .update = true, + .compare = true, + .func = PMBUS_HAVE_TEMP, + .sfunc = PMBUS_HAVE_STATUS_TEMP, + .sbase = PB_STATUS_TEMP_BASE, + .gbit = PB_STATUS_TEMPERATURE, + .limit = temp_limit_attrs, + .nlimit = ARRAY_SIZE(temp_limit_attrs), + }, { + .reg = PMBUS_READ_TEMPERATURE_2, + .class = PSC_TEMPERATURE, + .paged = true, + .update = true, + .compare = true, + .func = PMBUS_HAVE_TEMP2, + .sfunc = PMBUS_HAVE_STATUS_TEMP, + .sbase = PB_STATUS_TEMP_BASE, + .gbit = PB_STATUS_TEMPERATURE, + .limit = temp_limit_attrs, + .nlimit = ARRAY_SIZE(temp_limit_attrs), + }, { + .reg = PMBUS_READ_TEMPERATURE_3, + .class = PSC_TEMPERATURE, + .paged = true, + .update = true, + .compare = true, + .func = PMBUS_HAVE_TEMP3, + .sfunc = PMBUS_HAVE_STATUS_TEMP, + .sbase = PB_STATUS_TEMP_BASE, + .gbit = PB_STATUS_TEMPERATURE, + .limit = temp_limit_attrs, + .nlimit = ARRAY_SIZE(temp_limit_attrs), } +}; + +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++) { int f; @@ -1403,9 +1295,7 @@ static void pmbus_find_attributes(struct i2c_client *client, break; if (!pmbus_check_word_register(client, page, - pmbus_fan_registers[f]) - || !pmbus_check_byte_register(client, page, - pmbus_fan_config_registers[f])) + pmbus_fan_registers[f])) break; /* @@ -1413,14 +1303,13 @@ static void pmbus_find_attributes(struct i2c_client *client, * Each fan configuration register covers multiple fans, * 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]); if (regval < 0 || (!(regval & (PB_FAN_1_INSTALLED >> ((f & 1) * 4))))) continue; - i0 = data->num_sensors; - pmbus_add_sensor(data, "fan", "input", in_index, page, + pmbus_add_sensor(data, "fan", "input", index, page, pmbus_fan_registers[f], PSC_FAN, true, true); @@ -1438,17 +1327,40 @@ static void pmbus_find_attributes(struct i2c_client *client, else base = PB_STATUS_FAN_BASE + page; pmbus_add_boolean_reg(data, "fan", "alarm", - in_index, base, + index, base, PB_FAN_FAN1_WARNING >> (f & 1)); pmbus_add_boolean_reg(data, "fan", "fault", - in_index, base, + index, base, 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. * This function is called for all chips. diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index f4e617adb22..cf4330b352e 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -1,6 +1,10 @@ /* * 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) 2007 Wouter Horre @@ -9,16 +13,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * Currently ignoring checksum on readings. - * 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 + * For further information, see the Documentation/hwmon/sht15 file. */ #include <linux/interrupt.h> @@ -39,17 +34,31 @@ #include <linux/slab.h> #include <asm/atomic.h> -#define SHT15_MEASURE_TEMP 3 -#define SHT15_MEASURE_RH 5 - -#define SHT15_READING_NOTHING 0 -#define SHT15_READING_TEMP 1 -#define SHT15_READING_HUMID 2 - -/* Min timings in nsecs */ -#define SHT15_TSCKL 100 /* clock low */ -#define SHT15_TSCKH 100 /* clock high */ -#define SHT15_TSU 150 /* data setup time */ +/* Commands */ +#define SHT15_MEASURE_TEMP 0x03 +#define SHT15_MEASURE_RH 0x05 +#define SHT15_WRITE_STATUS 0x06 +#define SHT15_READ_STATUS 0x07 +#define SHT15_SOFT_RESET 0x1E + +/* Min timings */ +#define SHT15_TSCKL 100 /* (nsecs) clock low */ +#define SHT15_TSCKH 100 /* (nsecs) clock high */ +#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 @@ -61,9 +70,7 @@ struct sht15_temppair { int d1; }; -/* Table 9 from data sheet - relates temperature calculation - * to supply voltage. - */ +/* Table 9 from datasheet - relates temperature calculation to supply voltage */ static const struct sht15_temppair temppoints[] = { { 2500000, -39400 }, { 3000000, -39600 }, @@ -72,29 +79,70 @@ static const struct sht15_temppair temppoints[] = { { 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 - * @pdata: platform data (gpio's etc) - * @read_work: bh of interrupt handler - * @wait_queue: wait queue for getting values from device - * @val_temp: last temperature value read from device - * @val_humid: last humidity value read from device - * @flag: status flag used to identify what the last request was - * @valid: are the current stored values valid (start condition) - * @last_updat: time of last update - * @read_lock: mutex to ensure only one read in progress - * at a time. - * @dev: associate device structure - * @hwmon_dev: device associated with hwmon subsystem - * @reg: associated regulator (if specified) - * @nb: notifier block to handle notifications of voltage changes - * @supply_uV: local copy of supply voltage used to allow - * use of 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 hander has been scheduled + * @pdata: platform data (gpio's etc). + * @read_work: bh of interrupt handler. + * @wait_queue: wait queue for getting values from device. + * @val_temp: last temperature value read from device. + * @val_humid: last humidity value read from device. + * @val_status: last status register value read from device. + * @checksum_ok: last value read from the device passed CRC validation. + * @checksumming: flag used to enable the data validation with CRC. + * @state: state identifying the action the driver is doing. + * @measurements_valid: are the current stored measures valid (start condition). + * @status_valid: is the current stored status valid (start condition). + * @last_measurement: time of last measure. + * @last_status: time of last status reading. + * @read_lock: mutex to ensure only one read in progress at a time. + * @dev: associate device structure. + * @hwmon_dev: device associated with hwmon subsystem. + * @reg: associated regulator (if specified). + * @nb: notifier block to handle notifications of voltage + * changes. + * @supply_uV: local copy of supply voltage used to allow use of + * 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_platform_data *pdata; @@ -102,21 +150,60 @@ struct sht15_data { wait_queue_head_t wait_queue; uint16_t val_temp; uint16_t val_humid; - u8 flag; - u8 valid; - unsigned long last_updat; + u8 val_status; + bool checksum_ok; + 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 device *dev; struct device *hwmon_dev; struct regulator *reg; struct notifier_block nb; int supply_uV; - int supply_uV_valid; + bool supply_uV_valid; struct work_struct update_supply_work; 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 * @data: sht15 specific data * @@ -125,6 +212,7 @@ struct sht15_data { static void sht15_connection_reset(struct sht15_data *data) { int i; + gpio_direction_output(data->pdata->gpio_data, 1); ndelay(SHT15_TSCKL); gpio_set_value(data->pdata->gpio_sck, 0); @@ -136,14 +224,14 @@ static void sht15_connection_reset(struct sht15_data *data) ndelay(SHT15_TSCKL); } } + /** * sht15_send_bit() - send an individual bit to the device * @data: device state data * @val: value of bit to be sent - **/ + */ static inline void sht15_send_bit(struct sht15_data *data, int val) { - gpio_set_value(data->pdata->gpio_data, val); ndelay(SHT15_TSU); gpio_set_value(data->pdata->gpio_sck, 1); @@ -154,12 +242,12 @@ static inline void sht15_send_bit(struct sht15_data *data, int val) /** * sht15_transmission_start() - specific sequence for new transmission - * * @data: device state data + * * Timings for this are not documented on the data sheet, so very * conservative ones used in implementation. This implements * figure 12 on the data sheet. - **/ + */ static void sht15_transmission_start(struct sht15_data *data) { /* ensure data is high and output */ @@ -180,23 +268,26 @@ static void sht15_transmission_start(struct sht15_data *data) gpio_set_value(data->pdata->gpio_sck, 0); ndelay(SHT15_TSCKL); } + /** * sht15_send_byte() - send a single byte to the device * @data: device state * @byte: value to be sent - **/ + */ static void sht15_send_byte(struct sht15_data *data, u8 byte) { int i; + for (i = 0; i < 8; i++) { sht15_send_bit(data, !!(byte & 0x80)); byte <<= 1; } } + /** * sht15_wait_for_response() - checks for ack from device * @data: device state - **/ + */ static int sht15_wait_for_response(struct sht15_data *data) { gpio_direction_input(data->pdata->gpio_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 * and the interrupt disabled. - **/ + */ static int sht15_send_cmd(struct sht15_data *data, u8 cmd) { int ret = 0; + sht15_transmission_start(data); sht15_send_byte(data, cmd); ret = sht15_wait_for_response(data); 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 * @command: command sent to request value * @timeout_msecs: timeout after which comms are assumed * to have failed are reset. - **/ -static inline int sht15_update_single_val(struct sht15_data *data, - int command, - int timeout_msecs) + */ +static int sht15_measurement(struct sht15_data *data, + int command, + int timeout_msecs) { int ret; + u8 previous_config; + ret = sht15_send_cmd(data, command); if (ret) return ret; @@ -256,38 +519,61 @@ static inline int sht15_update_single_val(struct sht15_data *data, schedule_work(&data->read_work); } ret = wait_event_timeout(data->wait_queue, - (data->flag == SHT15_READING_NOTHING), + (data->state == SHT15_READING_NOTHING), msecs_to_jiffies(timeout_msecs)); if (ret == 0) {/* timeout occurred */ disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data)); sht15_connection_reset(data); 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; } /** - * 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 - **/ -static int sht15_update_vals(struct sht15_data *data) + */ +static int sht15_update_measurements(struct sht15_data *data) { int ret = 0; int timeout = HZ; mutex_lock(&data->read_lock); - if (time_after(jiffies, data->last_updat + timeout) - || !data->valid) { - data->flag = SHT15_READING_HUMID; - ret = sht15_update_single_val(data, SHT15_MEASURE_RH, 160); + if (time_after(jiffies, data->last_measurement + timeout) + || !data->measurements_valid) { + data->state = SHT15_READING_HUMID; + ret = sht15_measurement(data, SHT15_MEASURE_RH, 160); if (ret) goto error_ret; - data->flag = SHT15_READING_TEMP; - ret = sht15_update_single_val(data, SHT15_MEASURE_TEMP, 400); + data->state = SHT15_READING_TEMP; + ret = sht15_measurement(data, SHT15_MEASURE_TEMP, 400); if (ret) goto error_ret; - data->valid = 1; - data->last_updat = jiffies; + data->measurements_valid = true; + data->last_measurement = jiffies; } error_ret: mutex_unlock(&data->read_lock); @@ -300,10 +586,11 @@ error_ret: * @data: device state * * As per section 4.3 of the data sheet. - **/ + */ static inline int sht15_calc_temp(struct sht15_data *data) { int d1 = temppoints[0].d1; + int d2 = (data->val_status & SHT15_STATUS_LOW_RESOLUTION) ? 40 : 10; int i; for (i = ARRAY_SIZE(temppoints) - 1; i > 0; i--) @@ -316,7 +603,7 @@ static inline int sht15_calc_temp(struct sht15_data *data) 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) * * This is the temperature compensated version as per section 4.2 of * 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) { - int RHlinear; /* milli percent */ + int rh_linear; /* milli percent */ int temp = sht15_calc_temp(data); - + int c2, c3; + int t2; const int c1 = -4; - const int c2 = 40500; /* x 10 ^ -6 */ - const int c3 = -28; /* x 10 ^ -7 */ - RHlinear = c1*1000 - + c2 * data->val_humid/1000 + if (data->val_status & SHT15_STATUS_LOW_RESOLUTION) { + 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; - return (temp - 25000) * (10000 + 80 * data->val_humid) - / 1000000 + RHlinear; + return (temp - 25000) * (10000 + t2 * data->val_humid) + / 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, struct device_attribute *attr, char *buf) @@ -350,12 +716,21 @@ static ssize_t sht15_show_temp(struct device *dev, struct sht15_data *data = dev_get_drvdata(dev); /* 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", 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, struct device_attribute *attr, char *buf) @@ -363,11 +738,11 @@ static ssize_t sht15_show_humidity(struct device *dev, int ret; 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)); +} -}; static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) @@ -376,16 +751,23 @@ static ssize_t show_name(struct device *dev, return sprintf(buf, "%s\n", pdev->name); } -static SENSOR_DEVICE_ATTR(temp1_input, - S_IRUGO, sht15_show_temp, - NULL, 0); -static SENSOR_DEVICE_ATTR(humidity1_input, - S_IRUGO, sht15_show_humidity, - NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, + sht15_show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO, + sht15_show_humidity, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, sht15_show_status, NULL, + 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 struct attribute *sht15_attrs[] = { &sensor_dev_attr_temp1_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, NULL, }; @@ -397,59 +779,31 @@ static const struct attribute_group sht15_attr_group = { static irqreturn_t sht15_interrupt_fired(int irq, void *d) { struct sht15_data *data = d; + /* First disable the interrupt */ disable_irq_nosync(irq); atomic_inc(&data->interrupt_handled); /* Then schedule a reading work struct */ - if (data->flag != SHT15_READING_NOTHING) + if (data->state != SHT15_READING_NOTHING) schedule_work(&data->read_work); 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) { - int i; uint16_t val = 0; + u8 dev_checksum = 0; + u8 checksum_vals[3]; struct sht15_data *data = container_of(work_s, struct sht15_data, read_work); + /* Firstly, verify the line is low */ 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 - it hasn't! - */ + /* + * If not, then start the interrupt again - care here as could + * have gone low in meantime so verify it hasn't! + */ atomic_set(&data->interrupt_handled, 0); enable_irq(gpio_to_irq(data->pdata->gpio_data)); /* 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) || atomic_read(&data->interrupt_handled)) return; } + /* Read the data back from the device */ - for (i = 0; i < 16; ++i) { - val <<= 1; - gpio_set_value(data->pdata->gpio_sck, 1); - ndelay(SHT15_TSCKH); - val |= !!gpio_get_value(data->pdata->gpio_data); - gpio_set_value(data->pdata->gpio_sck, 0); - ndelay(SHT15_TSCKL); - if (i == 7) - sht15_ack(data); + val = sht15_read_byte(data); + val <<= 8; + sht15_ack(data); + val |= sht15_read_byte(data); + + if (data->checksumming) { + /* + * Ask the device for a checksum and read it back. + * 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 */ sht15_end_transmission(data); - switch (data->flag) { + switch (data->state) { case SHT15_READING_TEMP: data->val_temp = val; break; case SHT15_READING_HUMID: data->val_humid = val; break; + default: + break; } - data->flag = SHT15_READING_NOTHING; + data->state = SHT15_READING_NOTHING; wake_up(&data->wait_queue); } @@ -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 * to schedule an update of the supply voltage rather than getting it directly. - **/ + */ static int sht15_invalidate_voltage(struct notifier_block *nb, - unsigned long event, - void *ignored) + unsigned long event, + void *ignored) { struct sht15_data *data = container_of(nb, struct sht15_data, nb); @@ -518,10 +885,11 @@ static int __devinit sht15_probe(struct platform_device *pdev) { int ret = 0; struct sht15_data *data = kzalloc(sizeof(*data), GFP_KERNEL); + u8 status = 0; if (!data) { ret = -ENOMEM; - dev_err(&pdev->dev, "kzalloc failed"); + dev_err(&pdev->dev, "kzalloc failed\n"); goto error_ret; } @@ -533,13 +901,22 @@ static int __devinit sht15_probe(struct platform_device *pdev) init_waitqueue_head(&data->wait_queue); 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; } data->pdata = pdev->dev.platform_data; - data->supply_uV = data->pdata->supply_mv*1000; - -/* If a regulator is available, query what the supply voltage actually is!*/ + data->supply_uV = data->pdata->supply_mv * 1000; + if (data->pdata->checksum) + 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"); if (!IS_ERR(data->reg)) { int voltage; @@ -549,28 +926,34 @@ static int __devinit sht15_probe(struct platform_device *pdev) data->supply_uV = voltage; 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; 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"); if (ret) { - dev_err(&pdev->dev, "gpio request failed"); - goto err_free_data; + dev_err(&pdev->dev, "gpio request failed\n"); + goto err_release_reg; } gpio_direction_output(data->pdata->gpio_sck, 0); + ret = gpio_request(data->pdata->gpio_data, "SHT15 data"); if (ret) { - dev_err(&pdev->dev, "gpio request failed"); + dev_err(&pdev->dev, "gpio request failed\n"); 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), sht15_interrupt_fired, @@ -578,30 +961,53 @@ static int __devinit sht15_probe(struct platform_device *pdev) "sht15 data", data); 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; } disable_irq_nosync(gpio_to_irq(data->pdata->gpio_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); if (IS_ERR(data->hwmon_dev)) { ret = PTR_ERR(data->hwmon_dev); - goto err_release_irq; + goto err_release_sysfs_group; } + return 0; +err_release_sysfs_group: + sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group); err_release_irq: free_irq(gpio_to_irq(data->pdata->gpio_data), data); err_release_gpio_data: gpio_free(data->pdata->gpio_data); err_release_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: kfree(data); error_ret: - return ret; } @@ -609,9 +1015,15 @@ static int __devexit sht15_remove(struct platform_device *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); + if (sht15_soft_reset(data)) { + mutex_unlock(&data->read_lock); + return -EFAULT; + } hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group); if (!IS_ERR(data->reg)) { @@ -625,10 +1037,10 @@ static int __devexit sht15_remove(struct platform_device *pdev) gpio_free(data->pdata->gpio_sck); mutex_unlock(&data->read_lock); kfree(data); + return 0; } - /* * sht_drivers simultaneously refers to __devinit and __devexit function * which causes spurious section mismatch warning. So use __refdata to @@ -673,7 +1085,6 @@ static struct platform_driver __refdata sht_drivers[] = { }, }; - static int __init sht15_init(void) { int ret; diff --git a/drivers/hwmon/ucd9000.c b/drivers/hwmon/ucd9000.c new file mode 100644 index 00000000000..ace1c731973 --- /dev/null +++ b/drivers/hwmon/ucd9000.c @@ -0,0 +1,278 @@ +/* + * 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); diff --git a/drivers/hwmon/ucd9200.c b/drivers/hwmon/ucd9200.c new file mode 100644 index 00000000000..ffcc1cf3609 --- /dev/null +++ b/drivers/hwmon/ucd9200.c @@ -0,0 +1,210 @@ +/* + * 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); diff --git a/include/linux/sht15.h b/include/linux/sht15.h index 046bce05eca..f85c7c523da 100644 --- a/include/linux/sht15.h +++ b/include/linux/sht15.h @@ -8,17 +8,27 @@ * 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 * published by the Free Software Foundation. + * + * For further information, see the Documentation/hwmon/sht15 file. */ /** * struct sht15_platform_data - sht15 connectivity info - * @gpio_data: no. of gpio to which bidirectional data line is connected - * @gpio_sck: no. of gpio to which the data clock is connected. - * @supply_mv: supply voltage in mv. Overridden by regulator if available. - **/ + * @gpio_data: no. of gpio to which bidirectional data line is + * connected. + * @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 { int gpio_data; int gpio_sck; int supply_mv; + bool checksum; + bool no_otp_reload; + bool low_resolution; }; |