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: (44 commits)
  hwmon: (lineage-pem): Fix in1 voltage alarm sysfs attributes
  hwmon/f71882fg: Add support for f71808e
  hwmon/f71882fg: Add support for f71869f and f71869e
  hwmon/f71882fg: Add support for f71889ed
  hwmon/f71882fg: Break out test for auto pwm's controlled by digital readings
  hwmon/f71882fg: Separate temp beep sysfs attr from the other temp sysfs attr
  hwmon/f71882fg: Remove bogus temp2_type for certain models
  hwmon/f71882fg: Make number of temps configurable
  hwmon/f71882fg: Make creation of in sysfs attributes more generic
  hwmon/f71882fg: Only allow negative auto point temps if fan_neg_temp is enabled
  hwmon/f71882fg: Fix temp1 sensor type reporting
  hwmon: (w83627ehf) Display correct temperature sensor labels for systems with NCT6775F
  hwmon: (w83627ehf) Add fan debounce support for NCT6775F and NCT6776F
  hwmon: (w83627ehf) Update Kconfig for W83677HG-B, NCT6775F and NCT6776F
  hwmon: (w83627ehf) Store rpm instead of raw fan speed data
  hwmon: (w83627ehf) Use 16 bit fan count registers if supported
  hwmon: (w83627ehf) Add support for Nuvoton NCT6775F and NCT6776F
  hwmon: (w83627ehf) Permit enabling SmartFan IV mode if configured at startup
  hwmon: (w83627ehf) Convert register arrays to 16 bit, and convert access to pointers
  hwmon: (w83627ehf) Remove references to datasheets which no longer exist
  ...
This commit is contained in:
Linus Torvalds 2011-03-17 16:59:38 -07:00
commit 19520fc1ee
25 changed files with 6208 additions and 585 deletions

View file

@ -10,6 +10,10 @@ Supported chips:
Prefix: 'f71862fg'
Addresses scanned: none, address read from Super I/O config space
Datasheet: Available from the Fintek website
* Fintek F71869F and F71869E
Prefix: 'f71869'
Addresses scanned: none, address read from Super I/O config space
Datasheet: Available from the Fintek website
* Fintek F71882FG and F71883FG
Prefix: 'f71882fg'
Addresses scanned: none, address read from Super I/O config space
@ -17,6 +21,10 @@ Supported chips:
* Fintek F71889FG
Prefix: 'f71889fg'
Addresses scanned: none, address read from Super I/O config space
Datasheet: Available from the Fintek website
* Fintek F71889ED
Prefix: 'f71889ed'
Addresses scanned: none, address read from Super I/O config space
Datasheet: Should become available on the Fintek website soon
* Fintek F8000
Prefix: 'f8000'
@ -29,9 +37,9 @@ Author: Hans de Goede <hdegoede@redhat.com>
Description
-----------
Fintek F718xxFG/F8000 Super I/O chips include complete hardware monitoring
capabilities. They can monitor up to 9 voltages (3 for the F8000), 4 fans and
3 temperature sensors.
Fintek F718xx/F8000 Super I/O chips include complete hardware monitoring
capabilities. They can monitor up to 9 voltages, 4 fans and 3 temperature
sensors.
These chips also have fan controlling features, using either DC or PWM, in
three different modes (one manual, two automatic).
@ -99,5 +107,5 @@ Writing an unsupported mode will result in an invalid parameter error.
The fan speed is regulated to keep the temp the fan is mapped to between
temp#_auto_point2_temp and temp#_auto_point3_temp.
Both of the automatic modes require that pwm1 corresponds to fan1, pwm2 to
All of the automatic modes require that pwm1 corresponds to fan1, pwm2 to
fan2 and pwm3 to fan3.

View file

@ -0,0 +1,77 @@
Kernel driver lineage-pem
=========================
Supported devices:
* Lineage Compact Power Line Power Entry Modules
Prefix: 'lineage-pem'
Addresses scanned: -
Documentation:
http://www.lineagepower.com/oem/pdf/CPLI2C.pdf
Author: Guenter Roeck <guenter.roeck@ericsson.com>
Description
-----------
This driver supports various Lineage Compact Power Line DC/DC and AC/DC
converters such as CP1800, CP2000AC, CP2000DC, CP2100DC, and others.
Lineage CPL power entry modules are nominally PMBus compliant. However, most
standard PMBus commands are not supported. Specifically, all hardware monitoring
and status reporting commands are non-standard. For this reason, a standard
PMBus driver can not be used.
Usage Notes
-----------
This driver does not probe for Lineage CPL devices, since there is no register
which can be safely used to identify the chip. You will have to instantiate
the devices explicitly.
Example: the following will load the driver for a Lineage PEM at address 0x40
on I2C bus #1:
$ modprobe lineage-pem
$ echo lineage-pem 0x40 > /sys/bus/i2c/devices/i2c-1/new_device
All Lineage CPL power entry modules have a built-in I2C bus master selector
(PCA9541). To ensure device access, this driver should only be used as client
driver to the pca9541 I2C master selector driver.
Sysfs entries
-------------
All Lineage CPL devices report output voltage and device temperature as well as
alarms for output voltage, temperature, input voltage, input current, input power,
and fan status.
Input voltage, input current, input power, and fan speed measurement is only
supported on newer devices. The driver detects if those attributes are supported,
and only creates respective sysfs entries if they are.
in1_input Output voltage (mV)
in1_min_alarm Output undervoltage alarm
in1_max_alarm Output overvoltage alarm
in1_crit Output voltage critical alarm
in2_input Input voltage (mV, optional)
in2_alarm Input voltage alarm
curr1_input Input current (mA, optional)
curr1_alarm Input overcurrent alarm
power1_input Input power (uW, optional)
power1_alarm Input power alarm
fan1_input Fan 1 speed (rpm, optional)
fan2_input Fan 2 speed (rpm, optional)
fan3_input Fan 3 speed (rpm, optional)
temp1_input
temp1_max
temp1_crit
temp1_alarm
temp1_crit_alarm
temp1_fault

View file

@ -26,6 +26,14 @@ Supported chips:
Prefix: 'emc6d102'
Addresses scanned: I2C 0x2c, 0x2d, 0x2e
Datasheet: http://www.smsc.com/main/catalog/emc6d102.html
* SMSC EMC6D103
Prefix: 'emc6d103'
Addresses scanned: I2C 0x2c, 0x2d, 0x2e
Datasheet: http://www.smsc.com/main/catalog/emc6d103.html
* SMSC EMC6D103S
Prefix: 'emc6d103s'
Addresses scanned: I2C 0x2c, 0x2d, 0x2e
Datasheet: http://www.smsc.com/main/catalog/emc6d103s.html
Authors:
Philip Pokorny <ppokorny@penguincomputing.com>,
@ -122,9 +130,11 @@ to be register compatible. The EMC6D100 offers all the features of the
EMC6D101 plus additional voltage monitoring and system control features.
Unfortunately it is not possible to distinguish between the package
versions on register level so these additional voltage inputs may read
zero. The EMC6D102 features addtional ADC bits thus extending precision
zero. EMC6D102 and EMC6D103 feature additional ADC bits thus extending precision
of voltage and temperature channels.
SMSC EMC6D103S is similar to EMC6D103, but does not support pwm#_auto_pwm_minctl
and temp#_auto_temp_off.
Hardware Configurations
-----------------------

View file

@ -0,0 +1,47 @@
Kernel driver ltc4151
=====================
Supported chips:
* Linear Technology LTC4151
Prefix: 'ltc4151'
Addresses scanned: -
Datasheet:
http://www.linear.com/docs/Datasheet/4151fc.pdf
Author: Per Dalen <per.dalen@appeartv.com>
Description
-----------
The LTC4151 is a High Voltage I2C Current and Voltage Monitor.
Usage Notes
-----------
This driver does not probe for LTC4151 devices, since there is no register
which can be safely used to identify the chip. You will have to instantiate
the devices explicitly.
Example: the following will load the driver for an LTC4151 at address 0x6f
on I2C bus #0:
# modprobe ltc4151
# echo ltc4151 0x6f > /sys/bus/i2c/devices/i2c-0/new_device
Sysfs entries
-------------
Voltage readings provided by this driver are reported as obtained from the ADIN
and VIN registers.
Current reading provided by this driver is reported as obtained from the Current
Sense register. The reported value assumes that a 1 mOhm sense resistor is
installed.
in1_input VDIN voltage (mV)
in2_input ADIN voltage (mV)
curr1_input SENSE current (mA)

View file

@ -0,0 +1,49 @@
Kernel driver max6639
=====================
Supported chips:
* Maxim MAX6639
Prefix: 'max6639'
Addresses scanned: I2C 0x2c, 0x2e, 0x2f
Datasheet: http://pdfserv.maxim-ic.com/en/ds/MAX6639.pdf
Authors:
He Changqing <hechangqing@semptian.com>
Roland Stigge <stigge@antcom.de>
Description
-----------
This driver implements support for the Maxim MAX6639. This chip is a 2-channel
temperature monitor with dual PWM fan speed controller. It can monitor its own
temperature and one external diode-connected transistor or two external
diode-connected transistors.
The following device attributes are implemented via sysfs:
Attribute R/W Contents
----------------------------------------------------------------------------
temp1_input R Temperature channel 1 input (0..150 C)
temp2_input R Temperature channel 2 input (0..150 C)
temp1_fault R Temperature channel 1 diode fault
temp2_fault R Temperature channel 2 diode fault
temp1_max RW Set THERM temperature for input 1
(in C, see datasheet)
temp2_max RW Set THERM temperature for input 2
temp1_crit RW Set ALERT temperature for input 1
temp2_crit RW Set ALERT temperature for input 2
temp1_emergency RW Set OT temperature for input 1
(in C, see datasheet)
temp2_emergency RW Set OT temperature for input 2
pwm1 RW Fan 1 target duty cycle (0..255)
pwm2 RW Fan 2 target duty cycle (0..255)
fan1_input R TACH1 fan tachometer input (in RPM)
fan2_input R TACH2 fan tachometer input (in RPM)
fan1_fault R Fan 1 fault
fan2_fault R Fan 2 fault
temp1_max_alarm R Alarm on THERM temperature on channel 1
temp2_max_alarm R Alarm on THERM temperature on channel 2
temp1_crit_alarm R Alarm on ALERT temperature on channel 1
temp2_crit_alarm R Alarm on ALERT temperature on channel 2
temp1_emergency_alarm R Alarm on OT temperature on channel 1
temp2_emergency_alarm R Alarm on OT temperature on channel 2

215
Documentation/hwmon/pmbus Normal file
View file

@ -0,0 +1,215 @@
Kernel driver pmbus
====================
Supported chips:
* Ericsson BMR45X series
DC/DC Converter
Prefixes: 'bmr450', 'bmr451', 'bmr453', 'bmr454'
Addresses scanned: -
Datasheet:
http://archive.ericsson.net/service/internet/picov/get?DocNo=28701-EN/LZT146395
* Linear Technology LTC2978
Octal PMBus Power Supply Monitor and Controller
Prefix: 'ltc2978'
Addresses scanned: -
Datasheet: http://cds.linear.com/docs/Datasheet/2978fa.pdf
* Maxim MAX16064
Quad Power-Supply Controller
Prefix: 'max16064'
Addresses scanned: -
Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX16064.pdf
* Maxim MAX34440
PMBus 6-Channel Power-Supply Manager
Prefixes: 'max34440'
Addresses scanned: -
Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX34440.pdf
* Maxim MAX34441
PMBus 5-Channel Power-Supply Manager and Intelligent Fan Controller
Prefixes: 'max34441'
Addresses scanned: -
Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX34441.pdf
* Maxim MAX8688
Digital Power-Supply Controller/Monitor
Prefix: 'max8688'
Addresses scanned: -
Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX8688.pdf
* Generic PMBus devices
Prefix: 'pmbus'
Addresses scanned: -
Datasheet: n.a.
Author: Guenter Roeck <guenter.roeck@ericsson.com>
Description
-----------
This driver supports hardware montoring for various PMBus compliant devices.
It supports voltage, current, power, and temperature sensors as supported
by the device.
Each monitored channel has its own high and low limits, plus a critical
limit.
Fan support will be added in a later version of this driver.
Usage Notes
-----------
This driver does not probe for PMBus devices, since there is no register
which can be safely used to identify the chip (The MFG_ID register is not
supported by all chips), and since there is no well defined address range for
PMBus devices. You will have to instantiate the devices explicitly.
Example: the following will load the driver for an LTC2978 at address 0x60
on I2C bus #1:
$ modprobe pmbus
$ echo ltc2978 0x60 > /sys/bus/i2c/devices/i2c-1/new_device
Platform data support
---------------------
Support for additional PMBus chips can be added by defining chip parameters in
a new chip specific driver file. For example, (untested) code to add support for
Emerson DS1200 power modules might look as follows.
static struct pmbus_driver_info ds1200_info = {
.pages = 1,
/* Note: All other sensors are in linear mode */
.direct[PSC_VOLTAGE_OUT] = true,
.direct[PSC_TEMPERATURE] = true,
.direct[PSC_CURRENT_OUT] = true,
.m[PSC_VOLTAGE_IN] = 1,
.b[PSC_VOLTAGE_IN] = 0,
.R[PSC_VOLTAGE_IN] = 3,
.m[PSC_VOLTAGE_OUT] = 1,
.b[PSC_VOLTAGE_OUT] = 0,
.R[PSC_VOLTAGE_OUT] = 3,
.m[PSC_TEMPERATURE] = 1,
.b[PSC_TEMPERATURE] = 0,
.R[PSC_TEMPERATURE] = 3,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT
| PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
| PMBUS_HAVE_PIN | PMBUS_HAVE_POUT
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP
| PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
};
static int ds1200_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
return pmbus_do_probe(client, id, &ds1200_info);
}
static int ds1200_remove(struct i2c_client *client)
{
return pmbus_do_remove(client);
}
static const struct i2c_device_id ds1200_id[] = {
{"ds1200", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, ds1200_id);
/* This is the driver that will be inserted */
static struct i2c_driver ds1200_driver = {
.driver = {
.name = "ds1200",
},
.probe = ds1200_probe,
.remove = ds1200_remove,
.id_table = ds1200_id,
};
static int __init ds1200_init(void)
{
return i2c_add_driver(&ds1200_driver);
}
static void __exit ds1200_exit(void)
{
i2c_del_driver(&ds1200_driver);
}
Sysfs entries
-------------
When probing the chip, the driver identifies which PMBus registers are
supported, and determines available sensors from this information.
Attribute files only exist if respective sensors are suported by the chip.
Labels are provided to inform the user about the sensor associated with
a given sysfs entry.
The following attributes are supported. Limits are read-write; all other
attributes are read-only.
inX_input Measured voltage. From READ_VIN or READ_VOUT register.
inX_min Minumum Voltage.
From VIN_UV_WARN_LIMIT or VOUT_UV_WARN_LIMIT register.
inX_max Maximum voltage.
From VIN_OV_WARN_LIMIT or VOUT_OV_WARN_LIMIT register.
inX_lcrit Critical minumum Voltage.
From VIN_UV_FAULT_LIMIT or VOUT_UV_FAULT_LIMIT register.
inX_crit Critical maximum voltage.
From VIN_OV_FAULT_LIMIT or VOUT_OV_FAULT_LIMIT register.
inX_min_alarm Voltage low alarm. From VOLTAGE_UV_WARNING status.
inX_max_alarm Voltage high alarm. From VOLTAGE_OV_WARNING status.
inX_lcrit_alarm Voltage critical low alarm.
From VOLTAGE_UV_FAULT status.
inX_crit_alarm Voltage critical high alarm.
From VOLTAGE_OV_FAULT status.
inX_label "vin", "vcap", or "voutY"
currX_input Measured current. From READ_IIN or READ_IOUT register.
currX_max Maximum current.
From IIN_OC_WARN_LIMIT or IOUT_OC_WARN_LIMIT register.
currX_lcrit Critical minumum output current.
From IOUT_UC_FAULT_LIMIT register.
currX_crit Critical maximum current.
From IIN_OC_FAULT_LIMIT or IOUT_OC_FAULT_LIMIT register.
currX_alarm Current high alarm.
From IIN_OC_WARNING or IOUT_OC_WARNING status.
currX_lcrit_alarm Output current critical low alarm.
From IOUT_UC_FAULT status.
currX_crit_alarm Current critical high alarm.
From IIN_OC_FAULT or IOUT_OC_FAULT status.
currX_label "iin" or "vinY"
powerX_input Measured power. From READ_PIN or READ_POUT register.
powerX_cap Output power cap. From POUT_MAX register.
powerX_max Power limit. From PIN_OP_WARN_LIMIT or
POUT_OP_WARN_LIMIT register.
powerX_crit Critical output power limit.
From POUT_OP_FAULT_LIMIT register.
powerX_alarm Power high alarm.
From PIN_OP_WARNING or POUT_OP_WARNING status.
powerX_crit_alarm Output power critical high alarm.
From POUT_OP_FAULT status.
powerX_label "pin" or "poutY"
tempX_input Measured tempererature.
From READ_TEMPERATURE_X register.
tempX_min Mimimum tempererature. From UT_WARN_LIMIT register.
tempX_max Maximum tempererature. From OT_WARN_LIMIT register.
tempX_lcrit Critical low tempererature.
From UT_FAULT_LIMIT register.
tempX_crit Critical high tempererature.
From OT_FAULT_LIMIT register.
tempX_min_alarm Chip temperature low alarm. Set by comparing
READ_TEMPERATURE_X with UT_WARN_LIMIT if
TEMP_UT_WARNING status is set.
tempX_max_alarm Chip temperature high alarm. Set by comparing
READ_TEMPERATURE_X with OT_WARN_LIMIT if
TEMP_OT_WARNING status is set.
tempX_lcrit_alarm Chip temperature critical low alarm. Set by comparing
READ_TEMPERATURE_X with UT_FAULT_LIMIT if
TEMP_UT_FAULT status is set.
tempX_crit_alarm Chip temperature critical high alarm. Set by comparing
READ_TEMPERATURE_X with OT_FAULT_LIMIT if
TEMP_OT_FAULT status is set.

View file

@ -187,6 +187,17 @@ fan[1-*]_div Fan divisor.
Note that this is actually an internal clock divisor, which
affects the measurable speed range, not the read value.
fan[1-*]_pulses Number of tachometer pulses per fan revolution.
Integer value, typically between 1 and 4.
RW
This value is a characteristic of the fan connected to the
device's input, so it has to be set in accordance with the fan
model.
Should only be created if the chip has a register to configure
the number of pulses. In the absence of such a register (and
thus attribute) the value assumed by all devices is 2 pulses
per fan revolution.
fan[1-*]_target
Desired fan speed
Unit: revolution/min (RPM)

View file

@ -5,13 +5,11 @@ Supported chips:
* Winbond W83627EHF/EHG (ISA access ONLY)
Prefix: 'w83627ehf'
Addresses scanned: ISA address retrieved from Super I/O registers
Datasheet:
http://www.nuvoton.com.tw/NR/rdonlyres/A6A258F0-F0C9-4F97-81C0-C4D29E7E943E/0/W83627EHF.pdf
Datasheet: not available
* Winbond W83627DHG
Prefix: 'w83627dhg'
Addresses scanned: ISA address retrieved from Super I/O registers
Datasheet:
http://www.nuvoton.com.tw/NR/rdonlyres/7885623D-A487-4CF9-A47F-30C5F73D6FE6/0/W83627DHG.pdf
Datasheet: not available
* Winbond W83627DHG-P
Prefix: 'w83627dhg'
Addresses scanned: ISA address retrieved from Super I/O registers
@ -24,6 +22,14 @@ Supported chips:
Prefix: 'w83667hg'
Addresses scanned: ISA address retrieved from Super I/O registers
Datasheet: Available from Nuvoton upon request
* Nuvoton NCT6775F/W83667HG-I
Prefix: 'nct6775'
Addresses scanned: ISA address retrieved from Super I/O registers
Datasheet: Available from Nuvoton upon request
* Nuvoton NCT6776F
Prefix: 'nct6776'
Addresses scanned: ISA address retrieved from Super I/O registers
Datasheet: Available from Nuvoton upon request
Authors:
Jean Delvare <khali@linux-fr.org>
@ -36,19 +42,28 @@ Description
-----------
This driver implements support for the Winbond W83627EHF, W83627EHG,
W83627DHG, W83627DHG-P, W83667HG and W83667HG-B super I/O chips.
We will refer to them collectively as Winbond chips.
W83627DHG, W83627DHG-P, W83667HG, W83667HG-B, W83667HG-I (NCT6775F),
and NCT6776F super I/O chips. We will refer to them collectively as
Winbond chips.
The chips implement three temperature sensors, five fan rotation
speed sensors, ten analog voltage sensors (only nine for the 627DHG), one
VID (6 pins for the 627EHF/EHG, 8 pins for the 627DHG and 667HG), alarms
with beep warnings (control unimplemented), and some automatic fan
regulation strategies (plus manual fan control mode).
The chips implement three temperature sensors (up to four for 667HG-B, and nine
for NCT6775F and NCT6776F), five fan rotation speed sensors, ten analog voltage
sensors (only nine for the 627DHG), one VID (6 pins for the 627EHF/EHG, 8 pins
for the 627DHG and 667HG), alarms with beep warnings (control unimplemented),
and some automatic fan regulation strategies (plus manual fan control mode).
The temperature sensor sources on W82677HG-B, NCT6775F, and NCT6776F are
configurable. temp4 and higher attributes are only reported if its temperature
source differs from the temperature sources of the already reported temperature
sensors. The configured source for each of the temperature sensors is provided
in tempX_label.
Temperatures are measured in degrees Celsius and measurement resolution is 1
degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when
the temperature gets higher than high limit; it stays on until the temperature
falls below the hysteresis value.
degC for temp1 and and 0.5 degC for temp2 and temp3. For temp4 and higher,
resolution is 1 degC for W83667HG-B and 0.0 degC for NCT6775F and NCT6776F.
An alarm is triggered when the temperature gets higher than high limit;
it stays on until the temperature falls below the hysteresis value.
Alarms are only supported for temp1, temp2, and temp3.
Fan rotation speeds are reported in RPM (rotations per minute). An alarm is
triggered if the rotation speed has dropped below a programmable limit. Fan
@ -80,7 +95,8 @@ prog -> pwm4 (not on 667HG and 667HG-B; the programmable setting is not
name - this is a standard hwmon device entry. For the W83627EHF and W83627EHG,
it is set to "w83627ehf", for the W83627DHG it is set to "w83627dhg",
and for the W83667HG it is set to "w83667hg".
for the W83667HG and W83667HG-B it is set to "w83667hg", for NCT6775F it
is set to "nct6775", and for NCT6776F it is set to "nct6776".
pwm[1-4] - this file stores PWM duty cycle or DC value (fan speed) in range:
0 (stop) to 255 (full)
@ -90,6 +106,18 @@ pwm[1-4]_enable - this file controls mode of fan/temperature control:
* 2 "Thermal Cruise" mode
* 3 "Fan Speed Cruise" mode
* 4 "Smart Fan III" mode
* 5 "Smart Fan IV" mode
SmartFan III mode is not supported on NCT6776F.
SmartFan IV mode is configurable only if it was configured at system
startup, and is only supported for W83677HG-B, NCT6775F, and NCT6776F.
SmartFan IV operational parameters can not be configured at this time,
and the various pwm attributes are not used in SmartFan IV mode.
The attributes can be written to, which is useful if you plan to
configure the system for a different pwm mode. However, the information
returned when reading pwm attributes is unrelated to SmartFan IV
operation.
pwm[1-4]_mode - controls if output is PWM or DC level
* 0 DC output (0 - 12v)

View file

@ -467,6 +467,17 @@ config SENSORS_JC42
This driver can also be built as a module. If so, the module
will be called jc42.
config SENSORS_LINEAGE
tristate "Lineage Compact Power Line Power Entry Module"
depends on I2C && EXPERIMENTAL
help
If you say yes here you get support for the Lineage Compact Power Line
series of DC/DC and AC/DC converters such as CP1800, CP2000AC,
CP2000DC, CP2725, and others.
This driver can also be built as a module. If so, the module
will be called lineage-pem.
config SENSORS_LM63
tristate "National Semiconductor LM63 and LM64"
depends on I2C
@ -625,6 +636,17 @@ config SENSORS_LM93
This driver can also be built as a module. If so, the module
will be called lm93.
config SENSORS_LTC4151
tristate "Linear Technology LTC4151"
depends on I2C
default n
help
If you say yes here you get support for Linear Technology LTC4151
High Voltage I2C Current and Voltage Monitor interface.
This driver can also be built as a module. If so, the module will
be called ltc4151.
config SENSORS_LTC4215
tristate "Linear Technology LTC4215"
depends on I2C && EXPERIMENTAL
@ -685,6 +707,16 @@ config SENSORS_MAX1619
This driver can also be built as a module. If so, the module
will be called max1619.
config SENSORS_MAX6639
tristate "Maxim MAX6639 sensor chip"
depends on I2C && EXPERIMENTAL
help
If you say yes here you get support for the MAX6639
sensor chips.
This driver can also be built as a module. If so, the module
will be called max6639.
config SENSORS_MAX6650
tristate "Maxim MAX6650 sensor chip"
depends on I2C && EXPERIMENTAL
@ -735,6 +767,61 @@ config SENSORS_PCF8591
These devices are hard to detect and rarely found on mainstream
hardware. If unsure, say N.
config PMBUS
tristate "PMBus support"
depends on I2C && EXPERIMENTAL
default n
help
Say yes here if you want to enable PMBus support.
This driver can also be built as a module. If so, the module will
be called pmbus_core.
if PMBUS
config SENSORS_PMBUS
tristate "Generic PMBus devices"
default n
help
If you say yes here you get hardware monitoring support for generic
PMBus devices, including but not limited to BMR450, BMR451, BMR453,
BMR454, and LTC2978.
This driver can also be built as a module. If so, the module will
be called pmbus.
config SENSORS_MAX16064
tristate "Maxim MAX16064"
default n
help
If you say yes here you get hardware monitoring support for Maxim
MAX16064.
This driver can also be built as a module. If so, the module will
be called max16064.
config SENSORS_MAX34440
tristate "Maxim MAX34440/MAX34441"
default n
help
If you say yes here you get hardware monitoring support for Maxim
MAX34440 and MAX34441.
This driver can also be built as a module. If so, the module will
be called max34440.
config SENSORS_MAX8688
tristate "Maxim MAX8688"
default n
help
If you say yes here you get hardware monitoring support for Maxim
MAX8688.
This driver can also be built as a module. If so, the module will
be called max8688.
endif # PMBUS
config SENSORS_SHT15
tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
depends on GENERIC_GPIO
@ -1083,7 +1170,7 @@ config SENSORS_W83627HF
will be called w83627hf.
config SENSORS_W83627EHF
tristate "Winbond W83627EHF/EHG/DHG, W83667HG"
tristate "Winbond W83627EHF/EHG/DHG, W83667HG, NCT6775F, NCT6776F"
select HWMON_VID
help
If you say yes here you get support for the hardware
@ -1094,7 +1181,8 @@ config SENSORS_W83627EHF
chip suited for specific Intel processors that use PECI such as
the Core 2 Duo.
This driver also supports the W83667HG chip.
This driver also supports Nuvoton W83667HG, W83667HG-B, NCT6775F
(also known as W83667HG-I), and NCT6776F.
This driver can also be built as a module. If so, the module
will be called w83627ehf.

View file

@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_JC42) += jc42.o
obj-$(CONFIG_SENSORS_JZ4740) += jz4740-hwmon.o
obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o
obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o
obj-$(CONFIG_SENSORS_LINEAGE) += lineage-pem.o
obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o
obj-$(CONFIG_SENSORS_LIS3_SPI) += lis3lv02d.o lis3lv02d_spi.o
obj-$(CONFIG_SENSORS_LIS3_I2C) += lis3lv02d.o lis3lv02d_i2c.o
@ -79,11 +80,13 @@ obj-$(CONFIG_SENSORS_LM90) += lm90.o
obj-$(CONFIG_SENSORS_LM92) += lm92.o
obj-$(CONFIG_SENSORS_LM93) += lm93.o
obj-$(CONFIG_SENSORS_LM95241) += lm95241.o
obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o
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_MAX1619) += max1619.o
obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
@ -112,6 +115,13 @@ obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
# PMBus drivers
obj-$(CONFIG_PMBUS) += pmbus_core.o
obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o
obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y)
EXTRA_CFLAGS += -DDEBUG
endif

View file

@ -1,6 +1,6 @@
/***************************************************************************
* Copyright (C) 2006 by Hans Edgington <hans@edgington.nl> *
* Copyright (C) 2007-2009 Hans de Goede <hdegoede@redhat.com> *
* Copyright (C) 2007-2011 Hans de Goede <hdegoede@redhat.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@ -47,22 +47,23 @@
#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
#define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */
#define SIO_F71808E_ID 0x0901 /* Chipset ID */
#define SIO_F71858_ID 0x0507 /* Chipset ID */
#define SIO_F71862_ID 0x0601 /* Chipset ID */
#define SIO_F71869_ID 0x0814 /* Chipset ID */
#define SIO_F71882_ID 0x0541 /* Chipset ID */
#define SIO_F71889_ID 0x0723 /* Chipset ID */
#define SIO_F71889E_ID 0x0909 /* Chipset ID */
#define SIO_F8000_ID 0x0581 /* Chipset ID */
#define REGION_LENGTH 8
#define ADDR_REG_OFFSET 5
#define DATA_REG_OFFSET 6
#define F71882FG_REG_PECI 0x0A
#define F71882FG_REG_IN_STATUS 0x12 /* f71882fg only */
#define F71882FG_REG_IN_BEEP 0x13 /* f71882fg only */
#define F71882FG_REG_IN_STATUS 0x12 /* f7188x only */
#define F71882FG_REG_IN_BEEP 0x13 /* f7188x only */
#define F71882FG_REG_IN(nr) (0x20 + (nr))
#define F71882FG_REG_IN1_HIGH 0x32 /* f71882fg only */
#define F71882FG_REG_IN1_HIGH 0x32 /* f7188x only */
#define F71882FG_REG_FAN(nr) (0xA0 + (16 * (nr)))
#define F71882FG_REG_FAN_TARGET(nr) (0xA2 + (16 * (nr)))
@ -86,28 +87,71 @@
#define F71882FG_REG_FAN_HYST(nr) (0x98 + (nr))
#define F71882FG_REG_FAN_FAULT_T 0x9F
#define F71882FG_FAN_NEG_TEMP_EN 0x20
#define F71882FG_FAN_PROG_SEL 0x80
#define F71882FG_REG_POINT_PWM(pwm, point) (0xAA + (point) + (16 * (pwm)))
#define F71882FG_REG_POINT_TEMP(pwm, point) (0xA6 + (point) + (16 * (pwm)))
#define F71882FG_REG_POINT_MAPPING(nr) (0xAF + 16 * (nr))
#define F71882FG_REG_START 0x01
#define F71882FG_MAX_INS 9
#define FAN_MIN_DETECT 366 /* Lowest detectable fanspeed */
static unsigned short force_id;
module_param(force_id, ushort, 0);
MODULE_PARM_DESC(force_id, "Override the detected device ID");
enum chips { f71858fg, f71862fg, f71882fg, f71889fg, f8000 };
enum chips { f71808e, f71858fg, f71862fg, f71869, f71882fg, f71889fg,
f71889ed, f8000 };
static const char *f71882fg_names[] = {
"f71808e",
"f71858fg",
"f71862fg",
"f71869", /* Both f71869f and f71869e, reg. compatible and same id */
"f71882fg",
"f71889fg",
"f71889ed",
"f8000",
};
static const char f71882fg_has_in[8][F71882FG_MAX_INS] = {
{ 1, 1, 1, 1, 1, 1, 0, 1, 1 }, /* f71808e */
{ 1, 1, 1, 0, 0, 0, 0, 0, 0 }, /* f71858fg */
{ 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* f71862fg */
{ 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* f71869 */
{ 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* f71882fg */
{ 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* f71889fg */
{ 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* f71889ed */
{ 1, 1, 1, 0, 0, 0, 0, 0, 0 }, /* f8000 */
};
static const char f71882fg_has_in1_alarm[8] = {
0, /* f71808e */
0, /* f71858fg */
0, /* f71862fg */
0, /* f71869 */
1, /* f71882fg */
1, /* f71889fg */
1, /* f71889ed */
0, /* f8000 */
};
static const char f71882fg_has_beep[8] = {
0, /* f71808e */
0, /* f71858fg */
1, /* f71862fg */
1, /* f71869 */
1, /* f71882fg */
1, /* f71889fg */
1, /* f71889ed */
0, /* f8000 */
};
static struct platform_device *f71882fg_pdev;
/* Super-I/O Function prototypes */
@ -129,11 +173,12 @@ struct f71882fg_data {
struct mutex update_lock;
int temp_start; /* temp numbering start (0 or 1) */
char valid; /* !=0 if following fields are valid */
char auto_point_temp_signed;
unsigned long last_updated; /* In jiffies */
unsigned long last_limits; /* In jiffies */
/* Register Values */
u8 in[9];
u8 in[F71882FG_MAX_INS];
u8 in1_max;
u8 in_status;
u8 in_beep;
@ -142,7 +187,7 @@ struct f71882fg_data {
u16 fan_full_speed[4];
u8 fan_status;
u8 fan_beep;
/* Note: all models have only 3 temperature channels, but on some
/* Note: all models have max 3 temperature channels, but on some
they are addressed as 0-2 and on others as 1-3, so for coding
convenience we reserve space for 4 channels */
u16 temp[4];
@ -262,13 +307,9 @@ static struct platform_driver f71882fg_driver = {
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
/* Temp and in attr for the f71858fg, the f71858fg is special as it
has its temperature indexes start at 0 (the others start at 1) and
it only has 3 voltage inputs */
static struct sensor_device_attribute_2 f71858fg_in_temp_attr[] = {
SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1),
SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2),
/* Temp attr for the f71858fg, the f71858fg is special as it has its
temperature indexes start at 0 (the others start at 1) */
static struct sensor_device_attribute_2 f71858fg_temp_attr[] = {
SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0),
SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max,
store_temp_max, 0, 0),
@ -292,7 +333,6 @@ static struct sensor_device_attribute_2 f71858fg_in_temp_attr[] = {
SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
0, 1),
SENSOR_ATTR_2(temp2_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5),
SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 1),
SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1),
SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2),
SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max,
@ -308,17 +348,8 @@ static struct sensor_device_attribute_2 f71858fg_in_temp_attr[] = {
SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2),
};
/* Temp and in attr common to the f71862fg, f71882fg and f71889fg */
static struct sensor_device_attribute_2 fxxxx_in_temp_attr[] = {
SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1),
SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2),
SENSOR_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 0, 3),
SENSOR_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 0, 4),
SENSOR_ATTR_2(in5_input, S_IRUGO, show_in, NULL, 0, 5),
SENSOR_ATTR_2(in6_input, S_IRUGO, show_in, NULL, 0, 6),
SENSOR_ATTR_2(in7_input, S_IRUGO, show_in, NULL, 0, 7),
SENSOR_ATTR_2(in8_input, S_IRUGO, show_in, NULL, 0, 8),
/* Temp attr for the standard models */
static struct sensor_device_attribute_2 fxxxx_temp_attr[3][9] = { {
SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 1),
SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max,
store_temp_max, 0, 1),
@ -328,17 +359,14 @@ static struct sensor_device_attribute_2 fxxxx_in_temp_attr[] = {
the max and crit alarms separately and lm_sensors v2 depends on the
presence of temp#_alarm files. The same goes for temp2/3 _alarm. */
SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 1),
SENSOR_ATTR_2(temp1_max_beep, S_IRUGO|S_IWUSR, show_temp_beep,
store_temp_beep, 0, 1),
SENSOR_ATTR_2(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit,
store_temp_crit, 0, 1),
SENSOR_ATTR_2(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
0, 1),
SENSOR_ATTR_2(temp1_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5),
SENSOR_ATTR_2(temp1_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep,
store_temp_beep, 0, 5),
SENSOR_ATTR_2(temp1_type, S_IRUGO, show_temp_type, NULL, 0, 1),
SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 1),
}, {
SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 2),
SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_max,
store_temp_max, 0, 2),
@ -346,17 +374,14 @@ static struct sensor_device_attribute_2 fxxxx_in_temp_attr[] = {
store_temp_max_hyst, 0, 2),
/* Should be temp2_max_alarm, see temp1_alarm note */
SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 2),
SENSOR_ATTR_2(temp2_max_beep, S_IRUGO|S_IWUSR, show_temp_beep,
store_temp_beep, 0, 2),
SENSOR_ATTR_2(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit,
store_temp_crit, 0, 2),
SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
0, 2),
SENSOR_ATTR_2(temp2_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6),
SENSOR_ATTR_2(temp2_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep,
store_temp_beep, 0, 6),
SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 2),
SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 2),
}, {
SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 3),
SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max,
store_temp_max, 0, 3),
@ -364,37 +389,39 @@ static struct sensor_device_attribute_2 fxxxx_in_temp_attr[] = {
store_temp_max_hyst, 0, 3),
/* Should be temp3_max_alarm, see temp1_alarm note */
SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 3),
SENSOR_ATTR_2(temp3_max_beep, S_IRUGO|S_IWUSR, show_temp_beep,
store_temp_beep, 0, 3),
SENSOR_ATTR_2(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit,
store_temp_crit, 0, 3),
SENSOR_ATTR_2(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
0, 3),
SENSOR_ATTR_2(temp3_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 7),
SENSOR_ATTR_2(temp3_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep,
store_temp_beep, 0, 7),
SENSOR_ATTR_2(temp3_type, S_IRUGO, show_temp_type, NULL, 0, 3),
SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 3),
};
} };
/* For models with in1 alarm capability */
static struct sensor_device_attribute_2 fxxxx_in1_alarm_attr[] = {
SENSOR_ATTR_2(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max,
0, 1),
SENSOR_ATTR_2(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep,
0, 1),
SENSOR_ATTR_2(in1_alarm, S_IRUGO, show_in_alarm, NULL, 0, 1),
};
/* Temp attr for models which can beep on temp alarm */
static struct sensor_device_attribute_2 fxxxx_temp_beep_attr[3][2] = { {
SENSOR_ATTR_2(temp1_max_beep, S_IRUGO|S_IWUSR, show_temp_beep,
store_temp_beep, 0, 1),
SENSOR_ATTR_2(temp1_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep,
store_temp_beep, 0, 5),
}, {
SENSOR_ATTR_2(temp2_max_beep, S_IRUGO|S_IWUSR, show_temp_beep,
store_temp_beep, 0, 2),
SENSOR_ATTR_2(temp2_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep,
store_temp_beep, 0, 6),
}, {
SENSOR_ATTR_2(temp3_max_beep, S_IRUGO|S_IWUSR, show_temp_beep,
store_temp_beep, 0, 3),
SENSOR_ATTR_2(temp3_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep,
store_temp_beep, 0, 7),
} };
/* Temp and in attr for the f8000
/* Temp attr for the f8000
Note on the f8000 temp_ovt (crit) is used as max, and temp_high (max)
is used as hysteresis value to clear alarms
Also like the f71858fg its temperature indexes start at 0
*/
static struct sensor_device_attribute_2 f8000_in_temp_attr[] = {
SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1),
SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2),
static struct sensor_device_attribute_2 f8000_temp_attr[] = {
SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0),
SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_crit,
store_temp_crit, 0, 0),
@ -408,7 +435,6 @@ static struct sensor_device_attribute_2 f8000_in_temp_attr[] = {
SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max,
store_temp_max, 0, 1),
SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5),
SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 1),
SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1),
SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2),
SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_crit,
@ -419,6 +445,28 @@ static struct sensor_device_attribute_2 f8000_in_temp_attr[] = {
SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2),
};
/* in attr for all models */
static struct sensor_device_attribute_2 fxxxx_in_attr[] = {
SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1),
SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2),
SENSOR_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 0, 3),
SENSOR_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 0, 4),
SENSOR_ATTR_2(in5_input, S_IRUGO, show_in, NULL, 0, 5),
SENSOR_ATTR_2(in6_input, S_IRUGO, show_in, NULL, 0, 6),
SENSOR_ATTR_2(in7_input, S_IRUGO, show_in, NULL, 0, 7),
SENSOR_ATTR_2(in8_input, S_IRUGO, show_in, NULL, 0, 8),
};
/* For models with in1 alarm capability */
static struct sensor_device_attribute_2 fxxxx_in1_alarm_attr[] = {
SENSOR_ATTR_2(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max,
0, 1),
SENSOR_ATTR_2(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep,
0, 1),
SENSOR_ATTR_2(in1_alarm, S_IRUGO, show_in_alarm, NULL, 0, 1),
};
/* Fan / PWM attr common to all models */
static struct sensor_device_attribute_2 fxxxx_fan_attr[4][6] = { {
SENSOR_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0),
@ -479,7 +527,7 @@ static struct sensor_device_attribute_2 fxxxx_fan_beep_attr[] = {
};
/* PWM attr for the f71862fg, fewer pwms and fewer zones per pwm than the
f71858fg / f71882fg / f71889fg */
standard models */
static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = {
SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_channel,
@ -548,7 +596,87 @@ static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = {
show_pwm_auto_point_temp_hyst, NULL, 3, 2),
};
/* PWM attr common to the f71858fg, f71882fg and f71889fg */
/* PWM attr for the f71808e/f71869, almost identical to the f71862fg, but the
pwm setting when the temperature is above the pwmX_auto_point1_temp can be
programmed instead of being hardcoded to 0xff */
static struct sensor_device_attribute_2 f71869_auto_pwm_attr[] = {
SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_channel,
store_pwm_auto_point_channel, 0, 0),
SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
0, 0),
SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
1, 0),
SENSOR_ATTR_2(pwm1_auto_point3_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
4, 0),
SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_temp, store_pwm_auto_point_temp,
0, 0),
SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_temp, store_pwm_auto_point_temp,
3, 0),
SENSOR_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR,
show_pwm_auto_point_temp_hyst,
store_pwm_auto_point_temp_hyst,
0, 0),
SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 0),
SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_channel,
store_pwm_auto_point_channel, 0, 1),
SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
0, 1),
SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
1, 1),
SENSOR_ATTR_2(pwm2_auto_point3_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
4, 1),
SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_temp, store_pwm_auto_point_temp,
0, 1),
SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_temp, store_pwm_auto_point_temp,
3, 1),
SENSOR_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR,
show_pwm_auto_point_temp_hyst,
store_pwm_auto_point_temp_hyst,
0, 1),
SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 1),
SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_channel,
store_pwm_auto_point_channel, 0, 2),
SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
0, 2),
SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
1, 2),
SENSOR_ATTR_2(pwm3_auto_point3_pwm, S_IRUGO|S_IWUSR,
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
4, 2),
SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_temp, store_pwm_auto_point_temp,
0, 2),
SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_temp, store_pwm_auto_point_temp,
3, 2),
SENSOR_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR,
show_pwm_auto_point_temp_hyst,
store_pwm_auto_point_temp_hyst,
0, 2),
SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO,
show_pwm_auto_point_temp_hyst, NULL, 3, 2),
};
/* PWM attr for the standard models */
static struct sensor_device_attribute_2 fxxxx_auto_pwm_attr[4][14] = { {
SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
show_pwm_auto_point_channel,
@ -943,16 +1071,16 @@ static u16 f71882fg_read_temp(struct f71882fg_data *data, int nr)
static struct f71882fg_data *f71882fg_update_device(struct device *dev)
{
struct f71882fg_data *data = dev_get_drvdata(dev);
int nr, reg = 0, reg2;
int nr, reg, point;
int nr_fans = (data->type == f71882fg) ? 4 : 3;
int nr_ins = (data->type == f71858fg || data->type == f8000) ? 3 : 9;
int nr_temps = (data->type == f71808e) ? 2 : 3;
mutex_lock(&data->update_lock);
/* Update once every 60 seconds */
if (time_after(jiffies, data->last_limits + 60 * HZ) ||
!data->valid) {
if (data->type == f71882fg || data->type == f71889fg) {
if (f71882fg_has_in1_alarm[data->type]) {
data->in1_max =
f71882fg_read8(data, F71882FG_REG_IN1_HIGH);
data->in_beep =
@ -960,7 +1088,8 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
}
/* Get High & boundary temps*/
for (nr = data->temp_start; nr < 3 + data->temp_start; nr++) {
for (nr = data->temp_start; nr < nr_temps + data->temp_start;
nr++) {
data->temp_ovt[nr] = f71882fg_read8(data,
F71882FG_REG_TEMP_OVT(nr));
data->temp_high[nr] = f71882fg_read8(data,
@ -973,44 +1102,19 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
data->temp_hyst[1] = f71882fg_read8(data,
F71882FG_REG_TEMP_HYST(1));
}
/* All but the f71858fg / f8000 have this register */
if ((data->type != f71858fg) && (data->type != f8000)) {
reg = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE);
data->temp_type[1] = (reg & 0x02) ? 2 : 4;
data->temp_type[2] = (reg & 0x04) ? 2 : 4;
data->temp_type[3] = (reg & 0x08) ? 2 : 4;
}
if (data->type == f71862fg || data->type == f71882fg ||
data->type == f71889fg) {
if (f71882fg_has_beep[data->type]) {
data->fan_beep = f71882fg_read8(data,
F71882FG_REG_FAN_BEEP);
data->temp_beep = f71882fg_read8(data,
F71882FG_REG_TEMP_BEEP);
/* Have to hardcode type, because temp1 is special */
reg = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE);
data->temp_type[2] = (reg & 0x04) ? 2 : 4;
data->temp_type[3] = (reg & 0x08) ? 2 : 4;
}
/* Determine temp index 1 sensor type */
if (data->type == f71889fg) {
reg2 = f71882fg_read8(data, F71882FG_REG_START);
switch ((reg2 & 0x60) >> 5) {
case 0x00: /* BJT / Thermistor */
data->temp_type[1] = (reg & 0x02) ? 2 : 4;
break;
case 0x01: /* AMDSI */
data->temp_type[1] = 5;
break;
case 0x02: /* PECI */
case 0x03: /* Ibex Peak ?? Report as PECI for now */
data->temp_type[1] = 6;
break;
}
} else {
reg2 = f71882fg_read8(data, F71882FG_REG_PECI);
if ((reg2 & 0x03) == 0x01)
data->temp_type[1] = 6; /* PECI */
else if ((reg2 & 0x03) == 0x02)
data->temp_type[1] = 5; /* AMDSI */
else if (data->type == f71862fg ||
data->type == f71882fg)
data->temp_type[1] = (reg & 0x02) ? 2 : 4;
else /* f71858fg and f8000 only support BJT */
data->temp_type[1] = 2;
}
data->pwm_enable = f71882fg_read8(data,
@ -1025,8 +1129,8 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
f71882fg_read8(data,
F71882FG_REG_POINT_MAPPING(nr));
if (data->type != f71862fg) {
int point;
switch (data->type) {
default:
for (point = 0; point < 5; point++) {
data->pwm_auto_point_pwm[nr][point] =
f71882fg_read8(data,
@ -1039,7 +1143,14 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
F71882FG_REG_POINT_TEMP
(nr, point));
}
} else {
break;
case f71808e:
case f71869:
data->pwm_auto_point_pwm[nr][0] =
f71882fg_read8(data,
F71882FG_REG_POINT_PWM(nr, 0));
/* Fall through */
case f71862fg:
data->pwm_auto_point_pwm[nr][1] =
f71882fg_read8(data,
F71882FG_REG_POINT_PWM
@ -1056,6 +1167,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
f71882fg_read8(data,
F71882FG_REG_POINT_TEMP
(nr, 3));
break;
}
}
data->last_limits = jiffies;
@ -1067,7 +1179,8 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
F71882FG_REG_TEMP_STATUS);
data->temp_diode_open = f71882fg_read8(data,
F71882FG_REG_TEMP_DIODE_OPEN);
for (nr = data->temp_start; nr < 3 + data->temp_start; nr++)
for (nr = data->temp_start; nr < nr_temps + data->temp_start;
nr++)
data->temp[nr] = f71882fg_read_temp(data, nr);
data->fan_status = f71882fg_read8(data,
@ -1083,17 +1196,18 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
data->pwm[nr] =
f71882fg_read8(data, F71882FG_REG_PWM(nr));
}
/* The f8000 can monitor 1 more fan, but has no pwm for it */
if (data->type == f8000)
data->fan[3] = f71882fg_read16(data,
F71882FG_REG_FAN(3));
if (data->type == f71882fg || data->type == f71889fg)
if (f71882fg_has_in1_alarm[data->type])
data->in_status = f71882fg_read8(data,
F71882FG_REG_IN_STATUS);
for (nr = 0; nr < nr_ins; nr++)
data->in[nr] = f71882fg_read8(data,
F71882FG_REG_IN(nr));
for (nr = 0; nr < F71882FG_MAX_INS; nr++)
if (f71882fg_has_in[data->type][nr])
data->in[nr] = f71882fg_read8(data,
F71882FG_REG_IN(nr));
data->last_updated = jiffies;
data->valid = 1;
@ -1882,7 +1996,7 @@ static ssize_t store_pwm_auto_point_temp(struct device *dev,
val /= 1000;
if (data->type == f71889fg)
if (data->auto_point_temp_signed)
val = SENSORS_LIMIT(val, -128, 127);
else
val = SENSORS_LIMIT(val, 0, 127);
@ -1929,7 +2043,8 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
struct f71882fg_data *data;
struct f71882fg_sio_data *sio_data = pdev->dev.platform_data;
int err, i, nr_fans = (sio_data->type == f71882fg) ? 4 : 3;
u8 start_reg;
int nr_temps = (sio_data->type == f71808e) ? 2 : 3;
u8 start_reg, reg;
data = kzalloc(sizeof(struct f71882fg_data), GFP_KERNEL);
if (!data)
@ -1968,37 +2083,72 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
/* The f71858fg temperature alarms behave as
the f8000 alarms in this mode */
err = f71882fg_create_sysfs_files(pdev,
f8000_in_temp_attr,
ARRAY_SIZE(f8000_in_temp_attr));
f8000_temp_attr,
ARRAY_SIZE(f8000_temp_attr));
else
err = f71882fg_create_sysfs_files(pdev,
f71858fg_in_temp_attr,
ARRAY_SIZE(f71858fg_in_temp_attr));
f71858fg_temp_attr,
ARRAY_SIZE(f71858fg_temp_attr));
break;
case f71882fg:
case f71889fg:
case f8000:
err = f71882fg_create_sysfs_files(pdev,
f8000_temp_attr,
ARRAY_SIZE(f8000_temp_attr));
break;
default:
err = f71882fg_create_sysfs_files(pdev,
&fxxxx_temp_attr[0][0],
ARRAY_SIZE(fxxxx_temp_attr[0]) * nr_temps);
}
if (err)
goto exit_unregister_sysfs;
if (f71882fg_has_beep[data->type]) {
err = f71882fg_create_sysfs_files(pdev,
&fxxxx_temp_beep_attr[0][0],
ARRAY_SIZE(fxxxx_temp_beep_attr[0])
* nr_temps);
if (err)
goto exit_unregister_sysfs;
}
for (i = 0; i < F71882FG_MAX_INS; i++) {
if (f71882fg_has_in[data->type][i]) {
err = device_create_file(&pdev->dev,
&fxxxx_in_attr[i].dev_attr);
if (err)
goto exit_unregister_sysfs;
}
}
if (f71882fg_has_in1_alarm[data->type]) {
err = f71882fg_create_sysfs_files(pdev,
fxxxx_in1_alarm_attr,
ARRAY_SIZE(fxxxx_in1_alarm_attr));
if (err)
goto exit_unregister_sysfs;
/* fall through! */
case f71862fg:
err = f71882fg_create_sysfs_files(pdev,
fxxxx_in_temp_attr,
ARRAY_SIZE(fxxxx_in_temp_attr));
break;
case f8000:
err = f71882fg_create_sysfs_files(pdev,
f8000_in_temp_attr,
ARRAY_SIZE(f8000_in_temp_attr));
break;
}
if (err)
goto exit_unregister_sysfs;
}
if (start_reg & 0x02) {
switch (data->type) {
case f71808e:
case f71869:
/* These always have signed auto point temps */
data->auto_point_temp_signed = 1;
/* Fall through to select correct fan/pwm reg bank! */
case f71889fg:
case f71889ed:
reg = f71882fg_read8(data, F71882FG_REG_FAN_FAULT_T);
if (reg & F71882FG_FAN_NEG_TEMP_EN)
data->auto_point_temp_signed = 1;
/* Ensure banked pwm registers point to right bank */
reg &= ~F71882FG_FAN_PROG_SEL;
f71882fg_write8(data, F71882FG_REG_FAN_FAULT_T, reg);
break;
default:
break;
}
data->pwm_enable =
f71882fg_read8(data, F71882FG_REG_PWM_ENABLE);
@ -2013,8 +2163,11 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
case f71862fg:
err = (data->pwm_enable & 0x15) != 0x15;
break;
case f71808e:
case f71869:
case f71882fg:
case f71889fg:
case f71889ed:
err = 0;
break;
case f8000:
@ -2034,20 +2187,50 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
if (err)
goto exit_unregister_sysfs;
if (data->type == f71862fg || data->type == f71882fg ||
data->type == f71889fg) {
if (f71882fg_has_beep[data->type]) {
err = f71882fg_create_sysfs_files(pdev,
fxxxx_fan_beep_attr, nr_fans);
if (err)
goto exit_unregister_sysfs;
}
switch (data->type) {
case f71808e:
case f71869:
case f71889fg:
case f71889ed:
for (i = 0; i < nr_fans; i++) {
data->pwm_auto_point_mapping[i] =
f71882fg_read8(data,
F71882FG_REG_POINT_MAPPING(i));
if ((data->pwm_auto_point_mapping[i] & 0x80) ||
(data->pwm_auto_point_mapping[i] & 3) == 0)
break;
}
if (i != nr_fans) {
dev_warn(&pdev->dev,
"Auto pwm controlled by raw digital "
"data, disabling pwm auto_point "
"sysfs attributes\n");
goto no_pwm_auto_point;
}
break;
default:
break;
}
switch (data->type) {
case f71862fg:
err = f71882fg_create_sysfs_files(pdev,
f71862fg_auto_pwm_attr,
ARRAY_SIZE(f71862fg_auto_pwm_attr));
break;
case f71808e:
case f71869:
err = f71882fg_create_sysfs_files(pdev,
f71869_auto_pwm_attr,
ARRAY_SIZE(f71869_auto_pwm_attr));
break;
case f8000:
err = f71882fg_create_sysfs_files(pdev,
f8000_fan_attr,
@ -2058,23 +2241,7 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
f8000_auto_pwm_attr,
ARRAY_SIZE(f8000_auto_pwm_attr));
break;
case f71889fg:
for (i = 0; i < nr_fans; i++) {
data->pwm_auto_point_mapping[i] =
f71882fg_read8(data,
F71882FG_REG_POINT_MAPPING(i));
if (data->pwm_auto_point_mapping[i] & 0x80)
break;
}
if (i != nr_fans) {
dev_warn(&pdev->dev,
"Auto pwm controlled by raw digital "
"data, disabling pwm auto_point "
"sysfs attributes\n");
break;
}
/* fall through */
default: /* f71858fg / f71882fg */
default:
err = f71882fg_create_sysfs_files(pdev,
&fxxxx_auto_pwm_attr[0][0],
ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
@ -2082,6 +2249,7 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
if (err)
goto exit_unregister_sysfs;
no_pwm_auto_point:
for (i = 0; i < nr_fans; i++)
dev_info(&pdev->dev, "Fan: %d is in %s mode\n", i + 1,
(data->pwm_enable & (1 << 2 * i)) ?
@ -2108,7 +2276,8 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
static int f71882fg_remove(struct platform_device *pdev)
{
struct f71882fg_data *data = platform_get_drvdata(pdev);
int nr_fans = (data->type == f71882fg) ? 4 : 3;
int i, nr_fans = (data->type == f71882fg) ? 4 : 3;
int nr_temps = (data->type == f71808e) ? 2 : 3;
u8 start_reg = f71882fg_read8(data, F71882FG_REG_START);
if (data->hwmon_dev)
@ -2121,29 +2290,39 @@ static int f71882fg_remove(struct platform_device *pdev)
case f71858fg:
if (data->temp_config & 0x10)
f71882fg_remove_sysfs_files(pdev,
f8000_in_temp_attr,
ARRAY_SIZE(f8000_in_temp_attr));
f8000_temp_attr,
ARRAY_SIZE(f8000_temp_attr));
else
f71882fg_remove_sysfs_files(pdev,
f71858fg_in_temp_attr,
ARRAY_SIZE(f71858fg_in_temp_attr));
break;
case f71882fg:
case f71889fg:
f71882fg_remove_sysfs_files(pdev,
fxxxx_in1_alarm_attr,
ARRAY_SIZE(fxxxx_in1_alarm_attr));
/* fall through! */
case f71862fg:
f71882fg_remove_sysfs_files(pdev,
fxxxx_in_temp_attr,
ARRAY_SIZE(fxxxx_in_temp_attr));
f71858fg_temp_attr,
ARRAY_SIZE(f71858fg_temp_attr));
break;
case f8000:
f71882fg_remove_sysfs_files(pdev,
f8000_in_temp_attr,
ARRAY_SIZE(f8000_in_temp_attr));
f8000_temp_attr,
ARRAY_SIZE(f8000_temp_attr));
break;
default:
f71882fg_remove_sysfs_files(pdev,
&fxxxx_temp_attr[0][0],
ARRAY_SIZE(fxxxx_temp_attr[0]) * nr_temps);
}
if (f71882fg_has_beep[data->type]) {
f71882fg_remove_sysfs_files(pdev,
&fxxxx_temp_beep_attr[0][0],
ARRAY_SIZE(fxxxx_temp_beep_attr[0]) * nr_temps);
}
for (i = 0; i < F71882FG_MAX_INS; i++) {
if (f71882fg_has_in[data->type][i]) {
device_remove_file(&pdev->dev,
&fxxxx_in_attr[i].dev_attr);
}
}
if (f71882fg_has_in1_alarm[data->type]) {
f71882fg_remove_sysfs_files(pdev,
fxxxx_in1_alarm_attr,
ARRAY_SIZE(fxxxx_in1_alarm_attr));
}
}
@ -2151,10 +2330,10 @@ static int f71882fg_remove(struct platform_device *pdev)
f71882fg_remove_sysfs_files(pdev, &fxxxx_fan_attr[0][0],
ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans);
if (data->type == f71862fg || data->type == f71882fg ||
data->type == f71889fg)
if (f71882fg_has_beep[data->type]) {
f71882fg_remove_sysfs_files(pdev,
fxxxx_fan_beep_attr, nr_fans);
}
switch (data->type) {
case f71862fg:
@ -2162,6 +2341,12 @@ static int f71882fg_remove(struct platform_device *pdev)
f71862fg_auto_pwm_attr,
ARRAY_SIZE(f71862fg_auto_pwm_attr));
break;
case f71808e:
case f71869:
f71882fg_remove_sysfs_files(pdev,
f71869_auto_pwm_attr,
ARRAY_SIZE(f71869_auto_pwm_attr));
break;
case f8000:
f71882fg_remove_sysfs_files(pdev,
f8000_fan_attr,
@ -2170,7 +2355,7 @@ static int f71882fg_remove(struct platform_device *pdev)
f8000_auto_pwm_attr,
ARRAY_SIZE(f8000_auto_pwm_attr));
break;
default: /* f71858fg / f71882fg / f71889fg */
default:
f71882fg_remove_sysfs_files(pdev,
&fxxxx_auto_pwm_attr[0][0],
ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
@ -2200,18 +2385,27 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID);
switch (devid) {
case SIO_F71808E_ID:
sio_data->type = f71808e;
break;
case SIO_F71858_ID:
sio_data->type = f71858fg;
break;
case SIO_F71862_ID:
sio_data->type = f71862fg;
break;
case SIO_F71869_ID:
sio_data->type = f71869;
break;
case SIO_F71882_ID:
sio_data->type = f71882fg;
break;
case SIO_F71889_ID:
sio_data->type = f71889fg;
break;
case SIO_F71889E_ID:
sio_data->type = f71889ed;
break;
case SIO_F8000_ID:
sio_data->type = f8000;
break;

586
drivers/hwmon/lineage-pem.c Normal file
View file

@ -0,0 +1,586 @@
/*
* Driver for Lineage Compact Power Line series of power entry modules.
*
* Copyright (C) 2010, 2011 Ericsson AB.
*
* Documentation:
* http://www.lineagepower.com/oem/pdf/CPLI2C.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/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>
/*
* This driver supports various Lineage Compact Power Line DC/DC and AC/DC
* converters such as CP1800, CP2000AC, CP2000DC, CP2100DC, and others.
*
* The devices are nominally PMBus compliant. However, most standard PMBus
* commands are not supported. Specifically, all hardware monitoring and
* status reporting commands are non-standard. For this reason, a standard
* PMBus driver can not be used.
*
* All Lineage CPL devices have a built-in I2C bus master selector (PCA9541).
* To ensure device access, this driver should only be used as client driver
* to the pca9541 I2C master selector driver.
*/
/* Command codes */
#define PEM_OPERATION 0x01
#define PEM_CLEAR_INFO_FLAGS 0x03
#define PEM_VOUT_COMMAND 0x21
#define PEM_VOUT_OV_FAULT_LIMIT 0x40
#define PEM_READ_DATA_STRING 0xd0
#define PEM_READ_INPUT_STRING 0xdc
#define PEM_READ_FIRMWARE_REV 0xdd
#define PEM_READ_RUN_TIMER 0xde
#define PEM_FAN_HI_SPEED 0xdf
#define PEM_FAN_NORMAL_SPEED 0xe0
#define PEM_READ_FAN_SPEED 0xe1
/* offsets in data string */
#define PEM_DATA_STATUS_2 0
#define PEM_DATA_STATUS_1 1
#define PEM_DATA_ALARM_2 2
#define PEM_DATA_ALARM_1 3
#define PEM_DATA_VOUT_LSB 4
#define PEM_DATA_VOUT_MSB 5
#define PEM_DATA_CURRENT 6
#define PEM_DATA_TEMP 7
/* Virtual entries, to report constants */
#define PEM_DATA_TEMP_MAX 10
#define PEM_DATA_TEMP_CRIT 11
/* offsets in input string */
#define PEM_INPUT_VOLTAGE 0
#define PEM_INPUT_POWER_LSB 1
#define PEM_INPUT_POWER_MSB 2
/* offsets in fan data */
#define PEM_FAN_ADJUSTMENT 0
#define PEM_FAN_FAN1 1
#define PEM_FAN_FAN2 2
#define PEM_FAN_FAN3 3
/* Status register bits */
#define STS1_OUTPUT_ON (1 << 0)
#define STS1_LEDS_FLASHING (1 << 1)
#define STS1_EXT_FAULT (1 << 2)
#define STS1_SERVICE_LED_ON (1 << 3)
#define STS1_SHUTDOWN_OCCURRED (1 << 4)
#define STS1_INT_FAULT (1 << 5)
#define STS1_ISOLATION_TEST_OK (1 << 6)
#define STS2_ENABLE_PIN_HI (1 << 0)
#define STS2_DATA_OUT_RANGE (1 << 1)
#define STS2_RESTARTED_OK (1 << 1)
#define STS2_ISOLATION_TEST_FAIL (1 << 3)
#define STS2_HIGH_POWER_CAP (1 << 4)
#define STS2_INVALID_INSTR (1 << 5)
#define STS2_WILL_RESTART (1 << 6)
#define STS2_PEC_ERR (1 << 7)
/* Alarm register bits */
#define ALRM1_VIN_OUT_LIMIT (1 << 0)
#define ALRM1_VOUT_OUT_LIMIT (1 << 1)
#define ALRM1_OV_VOLT_SHUTDOWN (1 << 2)
#define ALRM1_VIN_OVERCURRENT (1 << 3)
#define ALRM1_TEMP_WARNING (1 << 4)
#define ALRM1_TEMP_SHUTDOWN (1 << 5)
#define ALRM1_PRIMARY_FAULT (1 << 6)
#define ALRM1_POWER_LIMIT (1 << 7)
#define ALRM2_5V_OUT_LIMIT (1 << 1)
#define ALRM2_TEMP_FAULT (1 << 2)
#define ALRM2_OV_LOW (1 << 3)
#define ALRM2_DCDC_TEMP_HIGH (1 << 4)
#define ALRM2_PRI_TEMP_HIGH (1 << 5)
#define ALRM2_NO_PRIMARY (1 << 6)
#define ALRM2_FAN_FAULT (1 << 7)
#define FIRMWARE_REV_LEN 4
#define DATA_STRING_LEN 9
#define INPUT_STRING_LEN 5 /* 4 for most devices */
#define FAN_SPEED_LEN 5
struct pem_data {
struct device *hwmon_dev;
struct mutex update_lock;
bool valid;
bool fans_supported;
int input_length;
unsigned long last_updated; /* in jiffies */
u8 firmware_rev[FIRMWARE_REV_LEN];
u8 data_string[DATA_STRING_LEN];
u8 input_string[INPUT_STRING_LEN];
u8 fan_speed[FAN_SPEED_LEN];
};
static int pem_read_block(struct i2c_client *client, u8 command, u8 *data,
int data_len)
{
u8 block_buffer[I2C_SMBUS_BLOCK_MAX];
int result;
result = i2c_smbus_read_block_data(client, command, block_buffer);
if (unlikely(result < 0))
goto abort;
if (unlikely(result == 0xff || result != data_len)) {
result = -EIO;
goto abort;
}
memcpy(data, block_buffer, data_len);
result = 0;
abort:
return result;
}
static struct pem_data *pem_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct pem_data *data = i2c_get_clientdata(client);
struct pem_data *ret = data;
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
int result;
/* Read data string */
result = pem_read_block(client, PEM_READ_DATA_STRING,
data->data_string,
sizeof(data->data_string));
if (unlikely(result < 0)) {
ret = ERR_PTR(result);
goto abort;
}
/* Read input string */
if (data->input_length) {
result = pem_read_block(client, PEM_READ_INPUT_STRING,
data->input_string,
data->input_length);
if (unlikely(result < 0)) {
ret = ERR_PTR(result);
goto abort;
}
}
/* Read fan speeds */
if (data->fans_supported) {
result = pem_read_block(client, PEM_READ_FAN_SPEED,
data->fan_speed,
sizeof(data->fan_speed));
if (unlikely(result < 0)) {
ret = ERR_PTR(result);
goto abort;
}
}
i2c_smbus_write_byte(client, PEM_CLEAR_INFO_FLAGS);
data->last_updated = jiffies;
data->valid = 1;
}
abort:
mutex_unlock(&data->update_lock);
return ret;
}
static long pem_get_data(u8 *data, int len, int index)
{
long val;
switch (index) {
case PEM_DATA_VOUT_LSB:
val = (data[index] + (data[index+1] << 8)) * 5 / 2;
break;
case PEM_DATA_CURRENT:
val = data[index] * 200;
break;
case PEM_DATA_TEMP:
val = data[index] * 1000;
break;
case PEM_DATA_TEMP_MAX:
val = 97 * 1000; /* 97 degrees C per datasheet */
break;
case PEM_DATA_TEMP_CRIT:
val = 107 * 1000; /* 107 degrees C per datasheet */
break;
default:
WARN_ON_ONCE(1);
val = 0;
}
return val;
}
static long pem_get_input(u8 *data, int len, int index)
{
long val;
switch (index) {
case PEM_INPUT_VOLTAGE:
if (len == INPUT_STRING_LEN)
val = (data[index] + (data[index+1] << 8) - 75) * 1000;
else
val = (data[index] - 75) * 1000;
break;
case PEM_INPUT_POWER_LSB:
if (len == INPUT_STRING_LEN)
index++;
val = (data[index] + (data[index+1] << 8)) * 1000000L;
break;
default:
WARN_ON_ONCE(1);
val = 0;
}
return val;
}
static long pem_get_fan(u8 *data, int len, int index)
{
long val;
switch (index) {
case PEM_FAN_FAN1:
case PEM_FAN_FAN2:
case PEM_FAN_FAN3:
val = data[index] * 100;
break;
default:
WARN_ON_ONCE(1);
val = 0;
}
return val;
}
/*
* Show boolean, either a fault or an alarm.
* .nr points to the register, .index is the bit mask to check
*/
static ssize_t pem_show_bool(struct device *dev,
struct device_attribute *da, char *buf)
{
struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da);
struct pem_data *data = pem_update_device(dev);
u8 status;
if (IS_ERR(data))
return PTR_ERR(data);
status = data->data_string[attr->nr] & attr->index;
return snprintf(buf, PAGE_SIZE, "%d\n", !!status);
}
static ssize_t pem_show_data(struct device *dev, struct device_attribute *da,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct pem_data *data = pem_update_device(dev);
long value;
if (IS_ERR(data))
return PTR_ERR(data);
value = pem_get_data(data->data_string, sizeof(data->data_string),
attr->index);
return snprintf(buf, PAGE_SIZE, "%ld\n", value);
}
static ssize_t pem_show_input(struct device *dev, struct device_attribute *da,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct pem_data *data = pem_update_device(dev);
long value;
if (IS_ERR(data))
return PTR_ERR(data);
value = pem_get_input(data->input_string, sizeof(data->input_string),
attr->index);
return snprintf(buf, PAGE_SIZE, "%ld\n", value);
}
static ssize_t pem_show_fan(struct device *dev, struct device_attribute *da,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct pem_data *data = pem_update_device(dev);
long value;
if (IS_ERR(data))
return PTR_ERR(data);
value = pem_get_fan(data->fan_speed, sizeof(data->fan_speed),
attr->index);
return snprintf(buf, PAGE_SIZE, "%ld\n", value);
}
/* Voltages */
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, pem_show_data, NULL,
PEM_DATA_VOUT_LSB);
static SENSOR_DEVICE_ATTR_2(in1_alarm, S_IRUGO, pem_show_bool, NULL,
PEM_DATA_ALARM_1, ALRM1_VOUT_OUT_LIMIT);
static SENSOR_DEVICE_ATTR_2(in1_crit_alarm, S_IRUGO, pem_show_bool, NULL,
PEM_DATA_ALARM_1, ALRM1_OV_VOLT_SHUTDOWN);
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, pem_show_input, NULL,
PEM_INPUT_VOLTAGE);
static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, pem_show_bool, NULL,
PEM_DATA_ALARM_1,
ALRM1_VIN_OUT_LIMIT | ALRM1_PRIMARY_FAULT);
/* Currents */
static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, pem_show_data, NULL,
PEM_DATA_CURRENT);
static SENSOR_DEVICE_ATTR_2(curr1_alarm, S_IRUGO, pem_show_bool, NULL,
PEM_DATA_ALARM_1, ALRM1_VIN_OVERCURRENT);
/* Power */
static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, pem_show_input, NULL,
PEM_INPUT_POWER_LSB);
static SENSOR_DEVICE_ATTR_2(power1_alarm, S_IRUGO, pem_show_bool, NULL,
PEM_DATA_ALARM_1, ALRM1_POWER_LIMIT);
/* Fans */
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, pem_show_fan, NULL,
PEM_FAN_FAN1);
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, pem_show_fan, NULL,
PEM_FAN_FAN2);
static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, pem_show_fan, NULL,
PEM_FAN_FAN3);
static SENSOR_DEVICE_ATTR_2(fan1_alarm, S_IRUGO, pem_show_bool, NULL,
PEM_DATA_ALARM_2, ALRM2_FAN_FAULT);
/* Temperatures */
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, pem_show_data, NULL,
PEM_DATA_TEMP);
static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, pem_show_data, NULL,
PEM_DATA_TEMP_MAX);
static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, pem_show_data, NULL,
PEM_DATA_TEMP_CRIT);
static SENSOR_DEVICE_ATTR_2(temp1_alarm, S_IRUGO, pem_show_bool, NULL,
PEM_DATA_ALARM_1, ALRM1_TEMP_WARNING);
static SENSOR_DEVICE_ATTR_2(temp1_crit_alarm, S_IRUGO, pem_show_bool, NULL,
PEM_DATA_ALARM_1, ALRM1_TEMP_SHUTDOWN);
static SENSOR_DEVICE_ATTR_2(temp1_fault, S_IRUGO, pem_show_bool, NULL,
PEM_DATA_ALARM_2, ALRM2_TEMP_FAULT);
static struct attribute *pem_attributes[] = {
&sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in1_alarm.dev_attr.attr,
&sensor_dev_attr_in1_crit_alarm.dev_attr.attr,
&sensor_dev_attr_in2_alarm.dev_attr.attr,
&sensor_dev_attr_curr1_alarm.dev_attr.attr,
&sensor_dev_attr_power1_alarm.dev_attr.attr,
&sensor_dev_attr_fan1_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp1_crit.dev_attr.attr,
&sensor_dev_attr_temp1_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_fault.dev_attr.attr,
NULL,
};
static const struct attribute_group pem_group = {
.attrs = pem_attributes,
};
static struct attribute *pem_input_attributes[] = {
&sensor_dev_attr_in2_input.dev_attr.attr,
&sensor_dev_attr_curr1_input.dev_attr.attr,
&sensor_dev_attr_power1_input.dev_attr.attr,
};
static const struct attribute_group pem_input_group = {
.attrs = pem_input_attributes,
};
static struct attribute *pem_fan_attributes[] = {
&sensor_dev_attr_fan1_input.dev_attr.attr,
&sensor_dev_attr_fan2_input.dev_attr.attr,
&sensor_dev_attr_fan3_input.dev_attr.attr,
};
static const struct attribute_group pem_fan_group = {
.attrs = pem_fan_attributes,
};
static int pem_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = client->adapter;
struct pem_data *data;
int ret;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BLOCK_DATA
| I2C_FUNC_SMBUS_WRITE_BYTE))
return -ENODEV;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
/*
* We use the next two commands to determine if the device is really
* there.
*/
ret = pem_read_block(client, PEM_READ_FIRMWARE_REV,
data->firmware_rev, sizeof(data->firmware_rev));
if (ret < 0)
goto out_kfree;
ret = i2c_smbus_write_byte(client, PEM_CLEAR_INFO_FLAGS);
if (ret < 0)
goto out_kfree;
dev_info(&client->dev, "Firmware revision %d.%d.%d\n",
data->firmware_rev[0], data->firmware_rev[1],
data->firmware_rev[2]);
/* Register sysfs hooks */
ret = sysfs_create_group(&client->dev.kobj, &pem_group);
if (ret)
goto out_kfree;
/*
* Check if input readings are supported.
* This is the case if we can read input data,
* and if the returned data is not all zeros.
* Note that input alarms are always supported.
*/
ret = pem_read_block(client, PEM_READ_INPUT_STRING,
data->input_string,
sizeof(data->input_string) - 1);
if (!ret && (data->input_string[0] || data->input_string[1] ||
data->input_string[2]))
data->input_length = sizeof(data->input_string) - 1;
else if (ret < 0) {
/* Input string is one byte longer for some devices */
ret = pem_read_block(client, PEM_READ_INPUT_STRING,
data->input_string,
sizeof(data->input_string));
if (!ret && (data->input_string[0] || data->input_string[1] ||
data->input_string[2] || data->input_string[3]))
data->input_length = sizeof(data->input_string);
}
ret = 0;
if (data->input_length) {
ret = sysfs_create_group(&client->dev.kobj, &pem_input_group);
if (ret)
goto out_remove_groups;
}
/*
* Check if fan speed readings are supported.
* This is the case if we can read fan speed data,
* and if the returned data is not all zeros.
* Note that the fan alarm is always supported.
*/
ret = pem_read_block(client, PEM_READ_FAN_SPEED,
data->fan_speed,
sizeof(data->fan_speed));
if (!ret && (data->fan_speed[0] || data->fan_speed[1] ||
data->fan_speed[2] || data->fan_speed[3])) {
data->fans_supported = true;
ret = sysfs_create_group(&client->dev.kobj, &pem_fan_group);
if (ret)
goto out_remove_groups;
}
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
ret = PTR_ERR(data->hwmon_dev);
goto out_remove_groups;
}
return 0;
out_remove_groups:
sysfs_remove_group(&client->dev.kobj, &pem_input_group);
sysfs_remove_group(&client->dev.kobj, &pem_fan_group);
sysfs_remove_group(&client->dev.kobj, &pem_group);
out_kfree:
kfree(data);
return ret;
}
static int pem_remove(struct i2c_client *client)
{
struct pem_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &pem_input_group);
sysfs_remove_group(&client->dev.kobj, &pem_fan_group);
sysfs_remove_group(&client->dev.kobj, &pem_group);
kfree(data);
return 0;
}
static const struct i2c_device_id pem_id[] = {
{"lineage_pem", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, pem_id);
static struct i2c_driver pem_driver = {
.driver = {
.name = "lineage_pem",
},
.probe = pem_probe,
.remove = pem_remove,
.id_table = pem_id,
};
static int __init pem_init(void)
{
return i2c_add_driver(&pem_driver);
}
static void __exit pem_exit(void)
{
i2c_del_driver(&pem_driver);
}
MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>");
MODULE_DESCRIPTION("Lineage CPL PEM hardware monitoring driver");
MODULE_LICENSE("GPL");
module_init(pem_init);
module_exit(pem_exit);

View file

@ -16,6 +16,7 @@
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/spi/spi.h>
#include <linux/pm.h>
#include "lis3lv02d.h"
@ -88,9 +89,10 @@ static int __devexit lis302dl_spi_remove(struct spi_device *spi)
return lis3lv02d_remove_fs(&lis3_dev);
}
#ifdef CONFIG_PM
static int lis3lv02d_spi_suspend(struct spi_device *spi, pm_message_t mesg)
#ifdef CONFIG_PM_SLEEP
static int lis3lv02d_spi_suspend(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
struct lis3lv02d *lis3 = spi_get_drvdata(spi);
if (!lis3->pdata || !lis3->pdata->wakeup_flags)
@ -99,8 +101,9 @@ static int lis3lv02d_spi_suspend(struct spi_device *spi, pm_message_t mesg)
return 0;
}
static int lis3lv02d_spi_resume(struct spi_device *spi)
static int lis3lv02d_spi_resume(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
struct lis3lv02d *lis3 = spi_get_drvdata(spi);
if (!lis3->pdata || !lis3->pdata->wakeup_flags)
@ -108,21 +111,19 @@ static int lis3lv02d_spi_resume(struct spi_device *spi)
return 0;
}
#else
#define lis3lv02d_spi_suspend NULL
#define lis3lv02d_spi_resume NULL
#endif
static SIMPLE_DEV_PM_OPS(lis3lv02d_spi_pm, lis3lv02d_spi_suspend,
lis3lv02d_spi_resume);
static struct spi_driver lis302dl_spi_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.pm = &lis3lv02d_spi_pm,
},
.probe = lis302dl_spi_probe,
.remove = __devexit_p(lis302dl_spi_remove),
.suspend = lis3lv02d_spi_suspend,
.resume = lis3lv02d_spi_resume,
};
static int __init lis302dl_init(void)

View file

@ -41,7 +41,7 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
enum chips {
any_chip, lm85b, lm85c,
adm1027, adt7463, adt7468,
emc6d100, emc6d102, emc6d103
emc6d100, emc6d102, emc6d103, emc6d103s
};
/* The LM85 registers */
@ -283,10 +283,6 @@ struct lm85_zone {
u8 hyst; /* Low limit hysteresis. (0-15) */
u8 range; /* Temp range, encoded */
s8 critical; /* "All fans ON" temp limit */
u8 off_desired; /* Actual "off" temperature specified. Preserved
* to prevent "drift" as other autofan control
* values change.
*/
u8 max_desired; /* Actual "max" temperature specified. Preserved
* to prevent "drift" as other autofan control
* values change.
@ -306,6 +302,8 @@ struct lm85_data {
const int *freq_map;
enum chips type;
bool has_vid5; /* true if VID5 is configured for ADT7463 or ADT7468 */
struct mutex update_lock;
int valid; /* !=0 if following fields are valid */
unsigned long last_reading; /* In jiffies */
@ -352,6 +350,7 @@ static const struct i2c_device_id lm85_id[] = {
{ "emc6d101", emc6d100 },
{ "emc6d102", emc6d102 },
{ "emc6d103", emc6d103 },
{ "emc6d103s", emc6d103s },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm85_id);
@ -420,8 +419,7 @@ static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr,
struct lm85_data *data = lm85_update_device(dev);
int vid;
if ((data->type == adt7463 || data->type == adt7468) &&
(data->vid & 0x80)) {
if (data->has_vid5) {
/* 6-pin VID (VRM 10) */
vid = vid_from_reg(data->vid & 0x3f, data->vrm);
} else {
@ -891,7 +889,6 @@ static ssize_t set_temp_auto_temp_off(struct device *dev,
mutex_lock(&data->update_lock);
min = TEMP_FROM_REG(data->zone[nr].limit);
data->zone[nr].off_desired = TEMP_TO_REG(val);
data->zone[nr].hyst = HYST_TO_REG(min - val);
if (nr == 0 || nr == 1) {
lm85_write_value(client, LM85_REG_AFAN_HYST1,
@ -934,18 +931,6 @@ static ssize_t set_temp_auto_temp_min(struct device *dev,
((data->zone[nr].range & 0x0f) << 4)
| (data->pwm_freq[nr] & 0x07));
/* Update temp_auto_hyst and temp_auto_off */
data->zone[nr].hyst = HYST_TO_REG(TEMP_FROM_REG(
data->zone[nr].limit) - TEMP_FROM_REG(
data->zone[nr].off_desired));
if (nr == 0 || nr == 1) {
lm85_write_value(client, LM85_REG_AFAN_HYST1,
(data->zone[0].hyst << 4)
| data->zone[1].hyst);
} else {
lm85_write_value(client, LM85_REG_AFAN_HYST2,
(data->zone[2].hyst << 4));
}
mutex_unlock(&data->update_lock);
return count;
}
@ -1084,13 +1069,7 @@ static struct attribute *lm85_attributes[] = {
&sensor_dev_attr_pwm1_auto_pwm_min.dev_attr.attr,
&sensor_dev_attr_pwm2_auto_pwm_min.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_pwm_min.dev_attr.attr,
&sensor_dev_attr_pwm1_auto_pwm_minctl.dev_attr.attr,
&sensor_dev_attr_pwm2_auto_pwm_minctl.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_pwm_minctl.dev_attr.attr,
&sensor_dev_attr_temp1_auto_temp_off.dev_attr.attr,
&sensor_dev_attr_temp2_auto_temp_off.dev_attr.attr,
&sensor_dev_attr_temp3_auto_temp_off.dev_attr.attr,
&sensor_dev_attr_temp1_auto_temp_min.dev_attr.attr,
&sensor_dev_attr_temp2_auto_temp_min.dev_attr.attr,
&sensor_dev_attr_temp3_auto_temp_min.dev_attr.attr,
@ -1111,6 +1090,26 @@ static const struct attribute_group lm85_group = {
.attrs = lm85_attributes,
};
static struct attribute *lm85_attributes_minctl[] = {
&sensor_dev_attr_pwm1_auto_pwm_minctl.dev_attr.attr,
&sensor_dev_attr_pwm2_auto_pwm_minctl.dev_attr.attr,
&sensor_dev_attr_pwm3_auto_pwm_minctl.dev_attr.attr,
};
static const struct attribute_group lm85_group_minctl = {
.attrs = lm85_attributes_minctl,
};
static struct attribute *lm85_attributes_temp_off[] = {
&sensor_dev_attr_temp1_auto_temp_off.dev_attr.attr,
&sensor_dev_attr_temp2_auto_temp_off.dev_attr.attr,
&sensor_dev_attr_temp3_auto_temp_off.dev_attr.attr,
};
static const struct attribute_group lm85_group_temp_off = {
.attrs = lm85_attributes_temp_off,
};
static struct attribute *lm85_attributes_in4[] = {
&sensor_dev_attr_in4_input.dev_attr.attr,
&sensor_dev_attr_in4_min.dev_attr.attr,
@ -1258,16 +1257,9 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info)
case LM85_VERSTEP_EMC6D103_A1:
type_name = "emc6d103";
break;
/*
* Registers apparently missing in EMC6D103S/EMC6D103:A2
* compared to EMC6D103:A0, EMC6D103:A1, and EMC6D102
* (according to the data sheets), but used unconditionally
* in the driver: 62[5:7], 6D[0:7], and 6E[0:7].
* So skip EMC6D103S for now.
case LM85_VERSTEP_EMC6D103S:
type_name = "emc6d103s";
break;
*/
}
} else {
dev_dbg(&adapter->dev,
@ -1280,6 +1272,19 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info)
return 0;
}
static void lm85_remove_files(struct i2c_client *client, struct lm85_data *data)
{
sysfs_remove_group(&client->dev.kobj, &lm85_group);
if (data->type != emc6d103s) {
sysfs_remove_group(&client->dev.kobj, &lm85_group_minctl);
sysfs_remove_group(&client->dev.kobj, &lm85_group_temp_off);
}
if (!data->has_vid5)
sysfs_remove_group(&client->dev.kobj, &lm85_group_in4);
if (data->type == emc6d100)
sysfs_remove_group(&client->dev.kobj, &lm85_group_in567);
}
static int lm85_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@ -1302,6 +1307,7 @@ static int lm85_probe(struct i2c_client *client,
case emc6d100:
case emc6d102:
case emc6d103:
case emc6d103s:
data->freq_map = adm1027_freq_map;
break;
default:
@ -1319,11 +1325,26 @@ static int lm85_probe(struct i2c_client *client,
if (err)
goto err_kfree;
/* minctl and temp_off exist on all chips except emc6d103s */
if (data->type != emc6d103s) {
err = sysfs_create_group(&client->dev.kobj, &lm85_group_minctl);
if (err)
goto err_kfree;
err = sysfs_create_group(&client->dev.kobj,
&lm85_group_temp_off);
if (err)
goto err_kfree;
}
/* The ADT7463/68 have an optional VRM 10 mode where pin 21 is used
as a sixth digital VID input rather than an analog input. */
data->vid = lm85_read_value(client, LM85_REG_VID);
if (!((data->type == adt7463 || data->type == adt7468) &&
(data->vid & 0x80)))
if (data->type == adt7463 || data->type == adt7468) {
u8 vid = lm85_read_value(client, LM85_REG_VID);
if (vid & 0x80)
data->has_vid5 = true;
}
if (!data->has_vid5)
if ((err = sysfs_create_group(&client->dev.kobj,
&lm85_group_in4)))
goto err_remove_files;
@ -1344,10 +1365,7 @@ static int lm85_probe(struct i2c_client *client,
/* Error out and cleanup code */
err_remove_files:
sysfs_remove_group(&client->dev.kobj, &lm85_group);
sysfs_remove_group(&client->dev.kobj, &lm85_group_in4);
if (data->type == emc6d100)
sysfs_remove_group(&client->dev.kobj, &lm85_group_in567);
lm85_remove_files(client, data);
err_kfree:
kfree(data);
return err;
@ -1357,10 +1375,7 @@ static int lm85_remove(struct i2c_client *client)
{
struct lm85_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &lm85_group);
sysfs_remove_group(&client->dev.kobj, &lm85_group_in4);
if (data->type == emc6d100)
sysfs_remove_group(&client->dev.kobj, &lm85_group_in567);
lm85_remove_files(client, data);
kfree(data);
return 0;
}
@ -1457,11 +1472,8 @@ static struct lm85_data *lm85_update_device(struct device *dev)
lm85_read_value(client, LM85_REG_FAN(i));
}
if (!((data->type == adt7463 || data->type == adt7468) &&
(data->vid & 0x80))) {
data->in[4] = lm85_read_value(client,
LM85_REG_IN(4));
}
if (!data->has_vid5)
data->in[4] = lm85_read_value(client, LM85_REG_IN(4));
if (data->type == adt7468)
data->cfg5 = lm85_read_value(client, ADT7468_REG_CFG5);
@ -1487,7 +1499,8 @@ static struct lm85_data *lm85_update_device(struct device *dev)
/* More alarm bits */
data->alarms |= lm85_read_value(client,
EMC6D100_REG_ALARM3) << 16;
} else if (data->type == emc6d102 || data->type == emc6d103) {
} else if (data->type == emc6d102 || data->type == emc6d103 ||
data->type == emc6d103s) {
/* Have to read LSB bits after the MSB ones because
the reading of the MSB bits has frozen the
LSBs (backward from the ADM1027).
@ -1528,8 +1541,7 @@ static struct lm85_data *lm85_update_device(struct device *dev)
lm85_read_value(client, LM85_REG_FAN_MIN(i));
}
if (!((data->type == adt7463 || data->type == adt7468) &&
(data->vid & 0x80))) {
if (!data->has_vid5) {
data->in_min[4] = lm85_read_value(client,
LM85_REG_IN_MIN(4));
data->in_max[4] = lm85_read_value(client,
@ -1573,17 +1585,19 @@ static struct lm85_data *lm85_update_device(struct device *dev)
}
}
i = lm85_read_value(client, LM85_REG_AFAN_SPIKE1);
data->autofan[0].min_off = (i & 0x20) != 0;
data->autofan[1].min_off = (i & 0x40) != 0;
data->autofan[2].min_off = (i & 0x80) != 0;
if (data->type != emc6d103s) {
i = lm85_read_value(client, LM85_REG_AFAN_SPIKE1);
data->autofan[0].min_off = (i & 0x20) != 0;
data->autofan[1].min_off = (i & 0x40) != 0;
data->autofan[2].min_off = (i & 0x80) != 0;
i = lm85_read_value(client, LM85_REG_AFAN_HYST1);
data->zone[0].hyst = i >> 4;
data->zone[1].hyst = i & 0x0f;
i = lm85_read_value(client, LM85_REG_AFAN_HYST1);
data->zone[0].hyst = i >> 4;
data->zone[1].hyst = i & 0x0f;
i = lm85_read_value(client, LM85_REG_AFAN_HYST2);
data->zone[2].hyst = i >> 4;
i = lm85_read_value(client, LM85_REG_AFAN_HYST2);
data->zone[2].hyst = i >> 4;
}
data->last_config = jiffies;
} /* last_config */

256
drivers/hwmon/ltc4151.c Normal file
View file

@ -0,0 +1,256 @@
/*
* Driver for Linear Technology LTC4151 High Voltage I2C Current
* and Voltage Monitor
*
* Copyright (C) 2011 AppearTV AS
*
* Derived from:
*
* Driver for Linear Technology LTC4261 I2C Negative Voltage Hot
* Swap Controller
* Copyright (C) 2010 Ericsson AB.
*
* Datasheet: http://www.linear.com/docs/Datasheet/4151fc.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/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>
/* chip registers */
#define LTC4151_SENSE_H 0x00
#define LTC4151_SENSE_L 0x01
#define LTC4151_VIN_H 0x02
#define LTC4151_VIN_L 0x03
#define LTC4151_ADIN_H 0x04
#define LTC4151_ADIN_L 0x05
struct ltc4151_data {
struct device *hwmon_dev;
struct mutex update_lock;
bool valid;
unsigned long last_updated; /* in jiffies */
/* Registers */
u8 regs[6];
};
static struct ltc4151_data *ltc4151_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct ltc4151_data *data = i2c_get_clientdata(client);
struct ltc4151_data *ret = data;
mutex_lock(&data->update_lock);
/*
* The chip's A/D updates 6 times per second
* (Conversion Rate 6 - 9 Hz)
*/
if (time_after(jiffies, data->last_updated + HZ / 6) || !data->valid) {
int i;
dev_dbg(&client->dev, "Starting ltc4151 update\n");
/* Read all registers */
for (i = 0; i < ARRAY_SIZE(data->regs); i++) {
int val;
val = i2c_smbus_read_byte_data(client, i);
if (unlikely(val < 0)) {
dev_dbg(dev,
"Failed to read ADC value: error %d\n",
val);
ret = ERR_PTR(val);
goto abort;
}
data->regs[i] = val;
}
data->last_updated = jiffies;
data->valid = 1;
}
abort:
mutex_unlock(&data->update_lock);
return ret;
}
/* Return the voltage from the given register in mV */
static int ltc4151_get_value(struct ltc4151_data *data, u8 reg)
{
u32 val;
val = (data->regs[reg] << 4) + (data->regs[reg + 1] >> 4);
switch (reg) {
case LTC4151_ADIN_H:
/* 500uV resolution. Convert to mV. */
val = val * 500 / 1000;
break;
case LTC4151_SENSE_H:
/*
* 20uV resolution. Convert to current as measured with
* an 1 mOhm sense resistor, in mA.
*/
val = val * 20;
break;
case LTC4151_VIN_H:
/* 25 mV per increment */
val = val * 25;
break;
default:
/* If we get here, the developer messed up */
WARN_ON_ONCE(1);
val = 0;
break;
}
return val;
}
static ssize_t ltc4151_show_value(struct device *dev,
struct device_attribute *da, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct ltc4151_data *data = ltc4151_update_device(dev);
int value;
if (IS_ERR(data))
return PTR_ERR(data);
value = ltc4151_get_value(data, attr->index);
return snprintf(buf, PAGE_SIZE, "%d\n", value);
}
/*
* Input voltages.
*/
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, \
ltc4151_show_value, NULL, LTC4151_VIN_H);
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, \
ltc4151_show_value, NULL, LTC4151_ADIN_H);
/* Currents (via sense resistor) */
static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, \
ltc4151_show_value, NULL, LTC4151_SENSE_H);
/* Finally, construct an array of pointers to members of the above objects,
* as required for sysfs_create_group()
*/
static struct attribute *ltc4151_attributes[] = {
&sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in2_input.dev_attr.attr,
&sensor_dev_attr_curr1_input.dev_attr.attr,
NULL,
};
static const struct attribute_group ltc4151_group = {
.attrs = ltc4151_attributes,
};
static int ltc4151_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = client->adapter;
struct ltc4151_data *data;
int ret;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
goto out_kzalloc;
}
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
/* Register sysfs hooks */
ret = sysfs_create_group(&client->dev.kobj, &ltc4151_group);
if (ret)
goto out_sysfs_create_group;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
ret = PTR_ERR(data->hwmon_dev);
goto out_hwmon_device_register;
}
return 0;
out_hwmon_device_register:
sysfs_remove_group(&client->dev.kobj, &ltc4151_group);
out_sysfs_create_group:
kfree(data);
out_kzalloc:
return ret;
}
static int ltc4151_remove(struct i2c_client *client)
{
struct ltc4151_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &ltc4151_group);
kfree(data);
return 0;
}
static const struct i2c_device_id ltc4151_id[] = {
{ "ltc4151", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ltc4151_id);
/* This is the driver that will be inserted */
static struct i2c_driver ltc4151_driver = {
.driver = {
.name = "ltc4151",
},
.probe = ltc4151_probe,
.remove = ltc4151_remove,
.id_table = ltc4151_id,
};
static int __init ltc4151_init(void)
{
return i2c_add_driver(&ltc4151_driver);
}
static void __exit ltc4151_exit(void)
{
i2c_del_driver(&ltc4151_driver);
}
MODULE_AUTHOR("Per Dalen <per.dalen@appeartv.com>");
MODULE_DESCRIPTION("LTC4151 driver");
MODULE_LICENSE("GPL");
module_init(ltc4151_init);
module_exit(ltc4151_exit);

91
drivers/hwmon/max16064.c Normal file
View file

@ -0,0 +1,91 @@
/*
* Hardware monitoring driver for Maxim MAX16064
*
* 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/i2c.h>
#include "pmbus.h"
static struct pmbus_driver_info max16064_info = {
.pages = 4,
.direct[PSC_VOLTAGE_IN] = true,
.direct[PSC_VOLTAGE_OUT] = true,
.direct[PSC_TEMPERATURE] = true,
.m[PSC_VOLTAGE_IN] = 19995,
.b[PSC_VOLTAGE_IN] = 0,
.R[PSC_VOLTAGE_IN] = -1,
.m[PSC_VOLTAGE_OUT] = 19995,
.b[PSC_VOLTAGE_OUT] = 0,
.R[PSC_VOLTAGE_OUT] = -1,
.m[PSC_TEMPERATURE] = -7612,
.b[PSC_TEMPERATURE] = 335,
.R[PSC_TEMPERATURE] = -3,
.func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_TEMP
| PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_TEMP,
.func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
.func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
.func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
};
static int max16064_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
return pmbus_do_probe(client, id, &max16064_info);
}
static int max16064_remove(struct i2c_client *client)
{
return pmbus_do_remove(client);
}
static const struct i2c_device_id max16064_id[] = {
{"max16064", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, max16064_id);
/* This is the driver that will be inserted */
static struct i2c_driver max16064_driver = {
.driver = {
.name = "max16064",
},
.probe = max16064_probe,
.remove = max16064_remove,
.id_table = max16064_id,
};
static int __init max16064_init(void)
{
return i2c_add_driver(&max16064_driver);
}
static void __exit max16064_exit(void)
{
i2c_del_driver(&max16064_driver);
}
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("PMBus driver for Maxim MAX16064");
MODULE_LICENSE("GPL");
module_init(max16064_init);
module_exit(max16064_exit);

199
drivers/hwmon/max34440.c Normal file
View file

@ -0,0 +1,199 @@
/*
* Hardware monitoring driver for Maxim MAX34440/MAX34441
*
* 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/i2c.h>
#include "pmbus.h"
enum chips { max34440, max34441 };
#define MAX34440_STATUS_OC_WARN (1 << 0)
#define MAX34440_STATUS_OC_FAULT (1 << 1)
#define MAX34440_STATUS_OT_FAULT (1 << 5)
#define MAX34440_STATUS_OT_WARN (1 << 6)
static int max34440_get_status(struct i2c_client *client, int page, int reg)
{
int ret;
int mfg_status;
ret = pmbus_set_page(client, page);
if (ret < 0)
return ret;
switch (reg) {
case PMBUS_STATUS_IOUT:
mfg_status = pmbus_read_word_data(client, 0,
PMBUS_STATUS_MFR_SPECIFIC);
if (mfg_status < 0)
return mfg_status;
if (mfg_status & MAX34440_STATUS_OC_WARN)
ret |= PB_IOUT_OC_WARNING;
if (mfg_status & MAX34440_STATUS_OC_FAULT)
ret |= PB_IOUT_OC_FAULT;
break;
case PMBUS_STATUS_TEMPERATURE:
mfg_status = pmbus_read_word_data(client, 0,
PMBUS_STATUS_MFR_SPECIFIC);
if (mfg_status < 0)
return mfg_status;
if (mfg_status & MAX34440_STATUS_OT_WARN)
ret |= PB_TEMP_OT_WARNING;
if (mfg_status & MAX34440_STATUS_OT_FAULT)
ret |= PB_TEMP_OT_FAULT;
break;
default:
ret = -ENODATA;
break;
}
return ret;
}
static struct pmbus_driver_info max34440_info[] = {
[max34440] = {
.pages = 14,
.direct[PSC_VOLTAGE_IN] = true,
.direct[PSC_VOLTAGE_OUT] = true,
.direct[PSC_TEMPERATURE] = true,
.direct[PSC_CURRENT_OUT] = true,
.m[PSC_VOLTAGE_IN] = 1,
.b[PSC_VOLTAGE_IN] = 0,
.R[PSC_VOLTAGE_IN] = 3, /* R = 0 in datasheet reflects mV */
.m[PSC_VOLTAGE_OUT] = 1,
.b[PSC_VOLTAGE_OUT] = 0,
.R[PSC_VOLTAGE_OUT] = 3, /* R = 0 in datasheet reflects mV */
.m[PSC_CURRENT_OUT] = 1,
.b[PSC_CURRENT_OUT] = 0,
.R[PSC_CURRENT_OUT] = 3, /* R = 0 in datasheet reflects mA */
.m[PSC_TEMPERATURE] = 1,
.b[PSC_TEMPERATURE] = 0,
.R[PSC_TEMPERATURE] = 2,
.func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
.func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
.func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
.func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
.func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
.func[5] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
.func[6] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[7] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[8] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[9] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[10] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[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,
},
[max34441] = {
.pages = 12,
.direct[PSC_VOLTAGE_IN] = true,
.direct[PSC_VOLTAGE_OUT] = true,
.direct[PSC_TEMPERATURE] = true,
.direct[PSC_CURRENT_OUT] = true,
.direct[PSC_FAN] = true,
.m[PSC_VOLTAGE_IN] = 1,
.b[PSC_VOLTAGE_IN] = 0,
.R[PSC_VOLTAGE_IN] = 3,
.m[PSC_VOLTAGE_OUT] = 1,
.b[PSC_VOLTAGE_OUT] = 0,
.R[PSC_VOLTAGE_OUT] = 3,
.m[PSC_CURRENT_OUT] = 1,
.b[PSC_CURRENT_OUT] = 0,
.R[PSC_CURRENT_OUT] = 3,
.m[PSC_TEMPERATURE] = 1,
.b[PSC_TEMPERATURE] = 0,
.R[PSC_TEMPERATURE] = 2,
.m[PSC_FAN] = 1,
.b[PSC_FAN] = 0,
.R[PSC_FAN] = 0,
.func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
.func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
.func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
.func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
.func[4] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
.func[5] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
.func[6] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[7] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[8] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[9] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[10] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.func[11] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.get_status = max34440_get_status,
},
};
static int max34440_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
return pmbus_do_probe(client, id, &max34440_info[id->driver_data]);
}
static int max34440_remove(struct i2c_client *client)
{
return pmbus_do_remove(client);
}
static const struct i2c_device_id max34440_id[] = {
{"max34440", max34440},
{"max34441", max34441},
{}
};
MODULE_DEVICE_TABLE(i2c, max34440_id);
/* This is the driver that will be inserted */
static struct i2c_driver max34440_driver = {
.driver = {
.name = "max34440",
},
.probe = max34440_probe,
.remove = max34440_remove,
.id_table = max34440_id,
};
static int __init max34440_init(void)
{
return i2c_add_driver(&max34440_driver);
}
static void __exit max34440_exit(void)
{
i2c_del_driver(&max34440_driver);
}
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("PMBus driver for Maxim MAX34440/MAX34441");
MODULE_LICENSE("GPL");
module_init(max34440_init);
module_exit(max34440_exit);

653
drivers/hwmon/max6639.c Normal file
View file

@ -0,0 +1,653 @@
/*
* max6639.c - Support for Maxim MAX6639
*
* 2-Channel Temperature Monitor with Dual PWM Fan-Speed Controller
*
* Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de>
*
* based on the initial MAX6639 support from semptian.net
* by He Changqing <hechangqing@semptian.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 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/i2c/max6639.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2c, 0x2e, 0x2f, I2C_CLIENT_END };
/* The MAX6639 registers, valid channel numbers: 0, 1 */
#define MAX6639_REG_TEMP(ch) (0x00 + (ch))
#define MAX6639_REG_STATUS 0x02
#define MAX6639_REG_OUTPUT_MASK 0x03
#define MAX6639_REG_GCONFIG 0x04
#define MAX6639_REG_TEMP_EXT(ch) (0x05 + (ch))
#define MAX6639_REG_ALERT_LIMIT(ch) (0x08 + (ch))
#define MAX6639_REG_OT_LIMIT(ch) (0x0A + (ch))
#define MAX6639_REG_THERM_LIMIT(ch) (0x0C + (ch))
#define MAX6639_REG_FAN_CONFIG1(ch) (0x10 + (ch) * 4)
#define MAX6639_REG_FAN_CONFIG2a(ch) (0x11 + (ch) * 4)
#define MAX6639_REG_FAN_CONFIG2b(ch) (0x12 + (ch) * 4)
#define MAX6639_REG_FAN_CONFIG3(ch) (0x13 + (ch) * 4)
#define MAX6639_REG_FAN_CNT(ch) (0x20 + (ch))
#define MAX6639_REG_TARGET_CNT(ch) (0x22 + (ch))
#define MAX6639_REG_FAN_PPR(ch) (0x24 + (ch))
#define MAX6639_REG_TARGTDUTY(ch) (0x26 + (ch))
#define MAX6639_REG_FAN_START_TEMP(ch) (0x28 + (ch))
#define MAX6639_REG_DEVID 0x3D
#define MAX6639_REG_MANUID 0x3E
#define MAX6639_REG_DEVREV 0x3F
/* Register bits */
#define MAX6639_GCONFIG_STANDBY 0x80
#define MAX6639_GCONFIG_POR 0x40
#define MAX6639_GCONFIG_DISABLE_TIMEOUT 0x20
#define MAX6639_GCONFIG_CH2_LOCAL 0x10
#define MAX6639_GCONFIG_PWM_FREQ_HI 0x08
#define MAX6639_FAN_CONFIG1_PWM 0x80
#define MAX6639_FAN_CONFIG3_THERM_FULL_SPEED 0x40
static const int rpm_ranges[] = { 2000, 4000, 8000, 16000 };
#define FAN_FROM_REG(val, div, rpm_range) ((val) == 0 ? -1 : \
(val) == 255 ? 0 : (rpm_ranges[rpm_range] * 30) / ((div + 1) * (val)))
#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT((val) / 1000, 0, 255)
/*
* Client data (each client gets its own)
*/
struct max6639_data {
struct device *hwmon_dev;
struct mutex update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
/* Register values sampled regularly */
u16 temp[2]; /* Temperature, in 1/8 C, 0..255 C */
bool temp_fault[2]; /* Detected temperature diode failure */
u8 fan[2]; /* Register value: TACH count for fans >=30 */
u8 status; /* Detected channel alarms and fan failures */
/* Register values only written to */
u8 pwm[2]; /* Register value: Duty cycle 0..120 */
u8 temp_therm[2]; /* THERM Temperature, 0..255 C (->_max) */
u8 temp_alert[2]; /* ALERT Temperature, 0..255 C (->_crit) */
u8 temp_ot[2]; /* OT Temperature, 0..255 C (->_emergency) */
/* Register values initialized only once */
u8 ppr; /* Pulses per rotation 0..3 for 1..4 ppr */
u8 rpm_range; /* Index in above rpm_ranges table */
};
static struct max6639_data *max6639_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct max6639_data *data = i2c_get_clientdata(client);
struct max6639_data *ret = data;
int i;
int status_reg;
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) {
int res;
dev_dbg(&client->dev, "Starting max6639 update\n");
status_reg = i2c_smbus_read_byte_data(client,
MAX6639_REG_STATUS);
if (status_reg < 0) {
ret = ERR_PTR(status_reg);
goto abort;
}
data->status = status_reg;
for (i = 0; i < 2; i++) {
res = i2c_smbus_read_byte_data(client,
MAX6639_REG_FAN_CNT(i));
if (res < 0) {
ret = ERR_PTR(res);
goto abort;
}
data->fan[i] = res;
res = i2c_smbus_read_byte_data(client,
MAX6639_REG_TEMP_EXT(i));
if (res < 0) {
ret = ERR_PTR(res);
goto abort;
}
data->temp[i] = res >> 5;
data->temp_fault[i] = res & 0x01;
res = i2c_smbus_read_byte_data(client,
MAX6639_REG_TEMP(i));
if (res < 0) {
ret = ERR_PTR(res);
goto abort;
}
data->temp[i] |= res << 3;
}
data->last_updated = jiffies;
data->valid = 1;
}
abort:
mutex_unlock(&data->update_lock);
return ret;
}
static ssize_t show_temp_input(struct device *dev,
struct device_attribute *dev_attr, char *buf)
{
long temp;
struct max6639_data *data = max6639_update_device(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
if (IS_ERR(data))
return PTR_ERR(data);
temp = data->temp[attr->index] * 125;
return sprintf(buf, "%ld\n", temp);
}
static ssize_t show_temp_fault(struct device *dev,
struct device_attribute *dev_attr, char *buf)
{
struct max6639_data *data = max6639_update_device(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%d\n", data->temp_fault[attr->index]);
}
static ssize_t show_temp_max(struct device *dev,
struct device_attribute *dev_attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct max6639_data *data = i2c_get_clientdata(client);
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
return sprintf(buf, "%d\n", (data->temp_therm[attr->index] * 1000));
}
static ssize_t set_temp_max(struct device *dev,
struct device_attribute *dev_attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct max6639_data *data = i2c_get_clientdata(client);
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
unsigned long val;
int res;
res = strict_strtoul(buf, 10, &val);
if (res)
return res;
mutex_lock(&data->update_lock);
data->temp_therm[attr->index] = TEMP_LIMIT_TO_REG(val);
i2c_smbus_write_byte_data(client,
MAX6639_REG_THERM_LIMIT(attr->index),
data->temp_therm[attr->index]);
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t show_temp_crit(struct device *dev,
struct device_attribute *dev_attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct max6639_data *data = i2c_get_clientdata(client);
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
return sprintf(buf, "%d\n", (data->temp_alert[attr->index] * 1000));
}
static ssize_t set_temp_crit(struct device *dev,
struct device_attribute *dev_attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct max6639_data *data = i2c_get_clientdata(client);
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
unsigned long val;
int res;
res = strict_strtoul(buf, 10, &val);
if (res)
return res;
mutex_lock(&data->update_lock);
data->temp_alert[attr->index] = TEMP_LIMIT_TO_REG(val);
i2c_smbus_write_byte_data(client,
MAX6639_REG_ALERT_LIMIT(attr->index),
data->temp_alert[attr->index]);
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t show_temp_emergency(struct device *dev,
struct device_attribute *dev_attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct max6639_data *data = i2c_get_clientdata(client);
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
return sprintf(buf, "%d\n", (data->temp_ot[attr->index] * 1000));
}
static ssize_t set_temp_emergency(struct device *dev,
struct device_attribute *dev_attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct max6639_data *data = i2c_get_clientdata(client);
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
unsigned long val;
int res;
res = strict_strtoul(buf, 10, &val);
if (res)
return res;
mutex_lock(&data->update_lock);
data->temp_ot[attr->index] = TEMP_LIMIT_TO_REG(val);
i2c_smbus_write_byte_data(client,
MAX6639_REG_OT_LIMIT(attr->index),
data->temp_ot[attr->index]);
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t show_pwm(struct device *dev,
struct device_attribute *dev_attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct max6639_data *data = i2c_get_clientdata(client);
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
return sprintf(buf, "%d\n", data->pwm[attr->index] * 255 / 120);
}
static ssize_t set_pwm(struct device *dev,
struct device_attribute *dev_attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct max6639_data *data = i2c_get_clientdata(client);
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
unsigned long val;
int res;
res = strict_strtoul(buf, 10, &val);
if (res)
return res;
val = SENSORS_LIMIT(val, 0, 255);
mutex_lock(&data->update_lock);
data->pwm[attr->index] = (u8)(val * 120 / 255);
i2c_smbus_write_byte_data(client,
MAX6639_REG_TARGTDUTY(attr->index),
data->pwm[attr->index]);
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t show_fan_input(struct device *dev,
struct device_attribute *dev_attr, char *buf)
{
struct max6639_data *data = max6639_update_device(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[attr->index],
data->ppr, data->rpm_range));
}
static ssize_t show_alarm(struct device *dev,
struct device_attribute *dev_attr, char *buf)
{
struct max6639_data *data = max6639_update_device(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%d\n", !!(data->status & (1 << attr->index)));
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input, NULL, 1);
static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1);
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
set_temp_max, 0);
static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_max,
set_temp_max, 1);
static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp_crit,
set_temp_crit, 0);
static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp_crit,
set_temp_crit, 1);
static SENSOR_DEVICE_ATTR(temp1_emergency, S_IWUSR | S_IRUGO,
show_temp_emergency, set_temp_emergency, 0);
static SENSOR_DEVICE_ATTR(temp2_emergency, S_IWUSR | S_IRUGO,
show_temp_emergency, set_temp_emergency, 1);
static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0);
static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1);
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input, NULL, 0);
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input, NULL, 1);
static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, show_alarm, NULL, 1);
static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, show_alarm, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 3);
static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 2);
static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 7);
static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 6);
static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO, show_alarm, NULL, 5);
static SENSOR_DEVICE_ATTR(temp2_emergency_alarm, S_IRUGO, show_alarm, NULL, 4);
static struct attribute *max6639_attributes[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp1_fault.dev_attr.attr,
&sensor_dev_attr_temp2_fault.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp2_max.dev_attr.attr,
&sensor_dev_attr_temp1_crit.dev_attr.attr,
&sensor_dev_attr_temp2_crit.dev_attr.attr,
&sensor_dev_attr_temp1_emergency.dev_attr.attr,
&sensor_dev_attr_temp2_emergency.dev_attr.attr,
&sensor_dev_attr_pwm1.dev_attr.attr,
&sensor_dev_attr_pwm2.dev_attr.attr,
&sensor_dev_attr_fan1_input.dev_attr.attr,
&sensor_dev_attr_fan2_input.dev_attr.attr,
&sensor_dev_attr_fan1_fault.dev_attr.attr,
&sensor_dev_attr_fan2_fault.dev_attr.attr,
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_emergency_alarm.dev_attr.attr,
NULL
};
static const struct attribute_group max6639_group = {
.attrs = max6639_attributes,
};
/*
* returns respective index in rpm_ranges table
* 1 by default on invalid range
*/
static int rpm_range_to_reg(int range)
{
int i;
for (i = 0; i < ARRAY_SIZE(rpm_ranges); i++) {
if (rpm_ranges[i] == range)
return i;
}
return 1; /* default: 4000 RPM */
}
static int max6639_init_client(struct i2c_client *client)
{
struct max6639_data *data = i2c_get_clientdata(client);
struct max6639_platform_data *max6639_info =
client->dev.platform_data;
int i = 0;
int rpm_range = 1; /* default: 4000 RPM */
int err = 0;
/* Reset chip to default values, see below for GCONFIG setup */
err = i2c_smbus_write_byte_data(client, MAX6639_REG_GCONFIG,
MAX6639_GCONFIG_POR);
if (err)
goto exit;
/* Fans pulse per revolution is 2 by default */
if (max6639_info && max6639_info->ppr > 0 &&
max6639_info->ppr < 5)
data->ppr = max6639_info->ppr;
else
data->ppr = 2;
data->ppr -= 1;
err = i2c_smbus_write_byte_data(client,
MAX6639_REG_FAN_PPR(i),
data->ppr << 5);
if (err)
goto exit;
if (max6639_info)
rpm_range = rpm_range_to_reg(max6639_info->rpm_range);
data->rpm_range = rpm_range;
for (i = 0; i < 2; i++) {
/* Fans config PWM, RPM */
err = i2c_smbus_write_byte_data(client,
MAX6639_REG_FAN_CONFIG1(i),
MAX6639_FAN_CONFIG1_PWM | rpm_range);
if (err)
goto exit;
/* Fans PWM polarity high by default */
if (max6639_info && max6639_info->pwm_polarity == 0)
err = i2c_smbus_write_byte_data(client,
MAX6639_REG_FAN_CONFIG2a(i), 0x00);
else
err = i2c_smbus_write_byte_data(client,
MAX6639_REG_FAN_CONFIG2a(i), 0x02);
if (err)
goto exit;
/*
* /THERM full speed enable,
* PWM frequency 25kHz, see also GCONFIG below
*/
err = i2c_smbus_write_byte_data(client,
MAX6639_REG_FAN_CONFIG3(i),
MAX6639_FAN_CONFIG3_THERM_FULL_SPEED | 0x03);
if (err)
goto exit;
/* Max. temp. 80C/90C/100C */
data->temp_therm[i] = 80;
data->temp_alert[i] = 90;
data->temp_ot[i] = 100;
err = i2c_smbus_write_byte_data(client,
MAX6639_REG_THERM_LIMIT(i),
data->temp_therm[i]);
if (err)
goto exit;
err = i2c_smbus_write_byte_data(client,
MAX6639_REG_ALERT_LIMIT(i),
data->temp_alert[i]);
if (err)
goto exit;
err = i2c_smbus_write_byte_data(client,
MAX6639_REG_OT_LIMIT(i), data->temp_ot[i]);
if (err)
goto exit;
/* PWM 120/120 (i.e. 100%) */
data->pwm[i] = 120;
err = i2c_smbus_write_byte_data(client,
MAX6639_REG_TARGTDUTY(i), data->pwm[i]);
if (err)
goto exit;
}
/* Start monitoring */
err = i2c_smbus_write_byte_data(client, MAX6639_REG_GCONFIG,
MAX6639_GCONFIG_DISABLE_TIMEOUT | MAX6639_GCONFIG_CH2_LOCAL |
MAX6639_GCONFIG_PWM_FREQ_HI);
exit:
return err;
}
/* Return 0 if detection is successful, -ENODEV otherwise */
static int max6639_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
int dev_id, manu_id;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
/* Actual detection via device and manufacturer ID */
dev_id = i2c_smbus_read_byte_data(client, MAX6639_REG_DEVID);
manu_id = i2c_smbus_read_byte_data(client, MAX6639_REG_MANUID);
if (dev_id != 0x58 || manu_id != 0x4D)
return -ENODEV;
strlcpy(info->type, "max6639", I2C_NAME_SIZE);
return 0;
}
static int max6639_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct max6639_data *data;
int err;
data = kzalloc(sizeof(struct max6639_data), GFP_KERNEL);
if (!data) {
err = -ENOMEM;
goto exit;
}
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
/* Initialize the max6639 chip */
err = max6639_init_client(client);
if (err < 0)
goto error_free;
/* Register sysfs hooks */
err = sysfs_create_group(&client->dev.kobj, &max6639_group);
if (err)
goto error_free;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
goto error_remove;
}
dev_info(&client->dev, "temperature sensor and fan control found\n");
return 0;
error_remove:
sysfs_remove_group(&client->dev.kobj, &max6639_group);
error_free:
kfree(data);
exit:
return err;
}
static int max6639_remove(struct i2c_client *client)
{
struct max6639_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &max6639_group);
kfree(data);
return 0;
}
static int max6639_suspend(struct i2c_client *client, pm_message_t mesg)
{
int data = i2c_smbus_read_byte_data(client, MAX6639_REG_GCONFIG);
if (data < 0)
return data;
return i2c_smbus_write_byte_data(client,
MAX6639_REG_GCONFIG, data | MAX6639_GCONFIG_STANDBY);
}
static int max6639_resume(struct i2c_client *client)
{
int data = i2c_smbus_read_byte_data(client, MAX6639_REG_GCONFIG);
if (data < 0)
return data;
return i2c_smbus_write_byte_data(client,
MAX6639_REG_GCONFIG, data & ~MAX6639_GCONFIG_STANDBY);
}
static const struct i2c_device_id max6639_id[] = {
{"max6639", 0},
{ }
};
MODULE_DEVICE_TABLE(i2c, max6639_id);
static struct i2c_driver max6639_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "max6639",
},
.probe = max6639_probe,
.remove = max6639_remove,
.suspend = max6639_suspend,
.resume = max6639_resume,
.id_table = max6639_id,
.detect = max6639_detect,
.address_list = normal_i2c,
};
static int __init max6639_init(void)
{
return i2c_add_driver(&max6639_driver);
}
static void __exit max6639_exit(void)
{
i2c_del_driver(&max6639_driver);
}
MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
MODULE_DESCRIPTION("max6639 driver");
MODULE_LICENSE("GPL");
module_init(max6639_init);
module_exit(max6639_exit);

158
drivers/hwmon/max8688.c Normal file
View file

@ -0,0 +1,158 @@
/*
* Hardware monitoring driver for Maxim MAX8688
*
* 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/i2c.h>
#include "pmbus.h"
#define MAX8688_MFG_STATUS 0xd8
#define MAX8688_STATUS_OC_FAULT (1 << 4)
#define MAX8688_STATUS_OV_FAULT (1 << 5)
#define MAX8688_STATUS_OV_WARNING (1 << 8)
#define MAX8688_STATUS_UV_FAULT (1 << 9)
#define MAX8688_STATUS_UV_WARNING (1 << 10)
#define MAX8688_STATUS_UC_FAULT (1 << 11)
#define MAX8688_STATUS_OC_WARNING (1 << 12)
#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)
{
int ret = 0;
int mfg_status;
if (page)
return -EINVAL;
switch (reg) {
case PMBUS_STATUS_VOUT:
mfg_status = pmbus_read_word_data(client, 0,
MAX8688_MFG_STATUS);
if (mfg_status < 0)
return mfg_status;
if (mfg_status & MAX8688_STATUS_UV_WARNING)
ret |= PB_VOLTAGE_UV_WARNING;
if (mfg_status & MAX8688_STATUS_UV_FAULT)
ret |= PB_VOLTAGE_UV_FAULT;
if (mfg_status & MAX8688_STATUS_OV_WARNING)
ret |= PB_VOLTAGE_OV_WARNING;
if (mfg_status & MAX8688_STATUS_OV_FAULT)
ret |= PB_VOLTAGE_OV_FAULT;
break;
case PMBUS_STATUS_IOUT:
mfg_status = pmbus_read_word_data(client, 0,
MAX8688_MFG_STATUS);
if (mfg_status < 0)
return mfg_status;
if (mfg_status & MAX8688_STATUS_UC_FAULT)
ret |= PB_IOUT_UC_FAULT;
if (mfg_status & MAX8688_STATUS_OC_WARNING)
ret |= PB_IOUT_OC_WARNING;
if (mfg_status & MAX8688_STATUS_OC_FAULT)
ret |= PB_IOUT_OC_FAULT;
break;
case PMBUS_STATUS_TEMPERATURE:
mfg_status = pmbus_read_word_data(client, 0,
MAX8688_MFG_STATUS);
if (mfg_status < 0)
return mfg_status;
if (mfg_status & MAX8688_STATUS_OT_WARNING)
ret |= PB_TEMP_OT_WARNING;
if (mfg_status & MAX8688_STATUS_OT_FAULT)
ret |= PB_TEMP_OT_FAULT;
break;
default:
ret = -ENODATA;
break;
}
return ret;
}
static struct pmbus_driver_info max8688_info = {
.pages = 1,
.direct[PSC_VOLTAGE_IN] = true,
.direct[PSC_VOLTAGE_OUT] = true,
.direct[PSC_TEMPERATURE] = true,
.direct[PSC_CURRENT_OUT] = true,
.m[PSC_VOLTAGE_IN] = 19995,
.b[PSC_VOLTAGE_IN] = 0,
.R[PSC_VOLTAGE_IN] = -1,
.m[PSC_VOLTAGE_OUT] = 19995,
.b[PSC_VOLTAGE_OUT] = 0,
.R[PSC_VOLTAGE_OUT] = -1,
.m[PSC_CURRENT_OUT] = 23109,
.b[PSC_CURRENT_OUT] = 0,
.R[PSC_CURRENT_OUT] = -2,
.m[PSC_TEMPERATURE] = -7612,
.b[PSC_TEMPERATURE] = 335,
.R[PSC_TEMPERATURE] = -3,
.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,
};
static int max8688_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
return pmbus_do_probe(client, id, &max8688_info);
}
static int max8688_remove(struct i2c_client *client)
{
return pmbus_do_remove(client);
}
static const struct i2c_device_id max8688_id[] = {
{"max8688", 0},
{ }
};
MODULE_DEVICE_TABLE(i2c, max8688_id);
/* This is the driver that will be inserted */
static struct i2c_driver max8688_driver = {
.driver = {
.name = "max8688",
},
.probe = max8688_probe,
.remove = max8688_remove,
.id_table = max8688_id,
};
static int __init max8688_init(void)
{
return i2c_add_driver(&max8688_driver);
}
static void __exit max8688_exit(void)
{
i2c_del_driver(&max8688_driver);
}
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("PMBus driver for Maxim MAX8688");
MODULE_LICENSE("GPL");
module_init(max8688_init);
module_exit(max8688_exit);

203
drivers/hwmon/pmbus.c Normal file
View file

@ -0,0 +1,203 @@
/*
* Hardware monitoring driver for PMBus devices
*
* Copyright (c) 2010, 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/mutex.h>
#include <linux/i2c.h>
#include "pmbus.h"
/*
* Find sensor groups and status registers on each page.
*/
static void pmbus_find_sensor_groups(struct i2c_client *client,
struct pmbus_driver_info *info)
{
int page;
/* Sensors detected on page 0 only */
if (pmbus_check_word_register(client, 0, PMBUS_READ_VIN))
info->func[0] |= PMBUS_HAVE_VIN;
if (pmbus_check_word_register(client, 0, PMBUS_READ_VCAP))
info->func[0] |= PMBUS_HAVE_VCAP;
if (pmbus_check_word_register(client, 0, PMBUS_READ_IIN))
info->func[0] |= PMBUS_HAVE_IIN;
if (pmbus_check_word_register(client, 0, PMBUS_READ_PIN))
info->func[0] |= PMBUS_HAVE_PIN;
if (info->func[0]
&& pmbus_check_byte_register(client, 0, PMBUS_STATUS_INPUT))
info->func[0] |= PMBUS_HAVE_STATUS_INPUT;
if (pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_1)) {
info->func[0] |= PMBUS_HAVE_FAN12;
if (pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_12))
info->func[0] |= PMBUS_HAVE_STATUS_FAN12;
}
if (pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_3)) {
info->func[0] |= PMBUS_HAVE_FAN34;
if (pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_34))
info->func[0] |= PMBUS_HAVE_STATUS_FAN34;
}
if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_1)) {
info->func[0] |= PMBUS_HAVE_TEMP;
if (pmbus_check_byte_register(client, 0,
PMBUS_STATUS_TEMPERATURE))
info->func[0] |= PMBUS_HAVE_STATUS_TEMP;
}
/* Sensors detected on all pages */
for (page = 0; page < info->pages; page++) {
if (pmbus_check_word_register(client, page, PMBUS_READ_VOUT)) {
info->func[page] |= PMBUS_HAVE_VOUT;
if (pmbus_check_byte_register(client, page,
PMBUS_STATUS_VOUT))
info->func[page] |= PMBUS_HAVE_STATUS_VOUT;
}
if (pmbus_check_word_register(client, page, PMBUS_READ_IOUT)) {
info->func[page] |= PMBUS_HAVE_IOUT;
if (pmbus_check_byte_register(client, 0,
PMBUS_STATUS_IOUT))
info->func[page] |= PMBUS_HAVE_STATUS_IOUT;
}
if (pmbus_check_word_register(client, page, PMBUS_READ_POUT))
info->func[page] |= PMBUS_HAVE_POUT;
}
}
/*
* Identify chip parameters.
*/
static int pmbus_identify(struct i2c_client *client,
struct pmbus_driver_info *info)
{
if (!info->pages) {
/*
* Check if the PAGE command is supported. If it is,
* keep setting the page number until it fails or until the
* maximum number of pages has been reached. Assume that
* this is the number of pages supported by the chip.
*/
if (pmbus_check_byte_register(client, 0, PMBUS_PAGE)) {
int page;
for (page = 1; page < PMBUS_PAGES; page++) {
if (pmbus_set_page(client, page) < 0)
break;
}
pmbus_set_page(client, 0);
info->pages = page;
} else {
info->pages = 1;
}
}
/*
* We should check if the COEFFICIENTS register is supported.
* If it is, and the chip is configured for direct mode, we can read
* the coefficients from the chip, one set per group of sensor
* registers.
*
* To do this, we will need access to a chip which actually supports the
* COEFFICIENTS command, since the command is too complex to implement
* without testing it.
*/
/* Try to find sensor groups */
pmbus_find_sensor_groups(client, info);
return 0;
}
static int pmbus_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pmbus_driver_info *info;
int ret;
info = kzalloc(sizeof(struct pmbus_driver_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->pages = id->driver_data;
info->identify = pmbus_identify;
ret = pmbus_do_probe(client, id, info);
if (ret < 0)
goto out;
return 0;
out:
kfree(info);
return ret;
}
static int pmbus_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;
}
/*
* Use driver_data to set the number of pages supported by the chip.
*/
static const struct i2c_device_id pmbus_id[] = {
{"bmr450", 1},
{"bmr451", 1},
{"bmr453", 1},
{"bmr454", 1},
{"ltc2978", 8},
{"pmbus", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, pmbus_id);
/* This is the driver that will be inserted */
static struct i2c_driver pmbus_driver = {
.driver = {
.name = "pmbus",
},
.probe = pmbus_probe,
.remove = pmbus_remove,
.id_table = pmbus_id,
};
static int __init pmbus_init(void)
{
return i2c_add_driver(&pmbus_driver);
}
static void __exit pmbus_exit(void)
{
i2c_del_driver(&pmbus_driver);
}
MODULE_AUTHOR("Guenter Roeck");
MODULE_DESCRIPTION("Generic PMBus driver");
MODULE_LICENSE("GPL");
module_init(pmbus_init);
module_exit(pmbus_exit);

313
drivers/hwmon/pmbus.h Normal file
View file

@ -0,0 +1,313 @@
/*
* pmbus.h - Common defines and structures for PMBus devices
*
* Copyright (c) 2010, 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.
*/
#ifndef PMBUS_H
#define PMBUS_H
/*
* Registers
*/
#define PMBUS_PAGE 0x00
#define PMBUS_OPERATION 0x01
#define PMBUS_ON_OFF_CONFIG 0x02
#define PMBUS_CLEAR_FAULTS 0x03
#define PMBUS_PHASE 0x04
#define PMBUS_CAPABILITY 0x19
#define PMBUS_QUERY 0x1A
#define PMBUS_VOUT_MODE 0x20
#define PMBUS_VOUT_COMMAND 0x21
#define PMBUS_VOUT_TRIM 0x22
#define PMBUS_VOUT_CAL_OFFSET 0x23
#define PMBUS_VOUT_MAX 0x24
#define PMBUS_VOUT_MARGIN_HIGH 0x25
#define PMBUS_VOUT_MARGIN_LOW 0x26
#define PMBUS_VOUT_TRANSITION_RATE 0x27
#define PMBUS_VOUT_DROOP 0x28
#define PMBUS_VOUT_SCALE_LOOP 0x29
#define PMBUS_VOUT_SCALE_MONITOR 0x2A
#define PMBUS_COEFFICIENTS 0x30
#define PMBUS_POUT_MAX 0x31
#define PMBUS_FAN_CONFIG_12 0x3A
#define PMBUS_FAN_COMMAND_1 0x3B
#define PMBUS_FAN_COMMAND_2 0x3C
#define PMBUS_FAN_CONFIG_34 0x3D
#define PMBUS_FAN_COMMAND_3 0x3E
#define PMBUS_FAN_COMMAND_4 0x3F
#define PMBUS_VOUT_OV_FAULT_LIMIT 0x40
#define PMBUS_VOUT_OV_FAULT_RESPONSE 0x41
#define PMBUS_VOUT_OV_WARN_LIMIT 0x42
#define PMBUS_VOUT_UV_WARN_LIMIT 0x43
#define PMBUS_VOUT_UV_FAULT_LIMIT 0x44
#define PMBUS_VOUT_UV_FAULT_RESPONSE 0x45
#define PMBUS_IOUT_OC_FAULT_LIMIT 0x46
#define PMBUS_IOUT_OC_FAULT_RESPONSE 0x47
#define PMBUS_IOUT_OC_LV_FAULT_LIMIT 0x48
#define PMBUS_IOUT_OC_LV_FAULT_RESPONSE 0x49
#define PMBUS_IOUT_OC_WARN_LIMIT 0x4A
#define PMBUS_IOUT_UC_FAULT_LIMIT 0x4B
#define PMBUS_IOUT_UC_FAULT_RESPONSE 0x4C
#define PMBUS_OT_FAULT_LIMIT 0x4F
#define PMBUS_OT_FAULT_RESPONSE 0x50
#define PMBUS_OT_WARN_LIMIT 0x51
#define PMBUS_UT_WARN_LIMIT 0x52
#define PMBUS_UT_FAULT_LIMIT 0x53
#define PMBUS_UT_FAULT_RESPONSE 0x54
#define PMBUS_VIN_OV_FAULT_LIMIT 0x55
#define PMBUS_VIN_OV_FAULT_RESPONSE 0x56
#define PMBUS_VIN_OV_WARN_LIMIT 0x57
#define PMBUS_VIN_UV_WARN_LIMIT 0x58
#define PMBUS_VIN_UV_FAULT_LIMIT 0x59
#define PMBUS_IIN_OC_FAULT_LIMIT 0x5B
#define PMBUS_IIN_OC_WARN_LIMIT 0x5D
#define PMBUS_POUT_OP_FAULT_LIMIT 0x68
#define PMBUS_POUT_OP_WARN_LIMIT 0x6A
#define PMBUS_PIN_OP_WARN_LIMIT 0x6B
#define PMBUS_STATUS_BYTE 0x78
#define PMBUS_STATUS_WORD 0x79
#define PMBUS_STATUS_VOUT 0x7A
#define PMBUS_STATUS_IOUT 0x7B
#define PMBUS_STATUS_INPUT 0x7C
#define PMBUS_STATUS_TEMPERATURE 0x7D
#define PMBUS_STATUS_CML 0x7E
#define PMBUS_STATUS_OTHER 0x7F
#define PMBUS_STATUS_MFR_SPECIFIC 0x80
#define PMBUS_STATUS_FAN_12 0x81
#define PMBUS_STATUS_FAN_34 0x82
#define PMBUS_READ_VIN 0x88
#define PMBUS_READ_IIN 0x89
#define PMBUS_READ_VCAP 0x8A
#define PMBUS_READ_VOUT 0x8B
#define PMBUS_READ_IOUT 0x8C
#define PMBUS_READ_TEMPERATURE_1 0x8D
#define PMBUS_READ_TEMPERATURE_2 0x8E
#define PMBUS_READ_TEMPERATURE_3 0x8F
#define PMBUS_READ_FAN_SPEED_1 0x90
#define PMBUS_READ_FAN_SPEED_2 0x91
#define PMBUS_READ_FAN_SPEED_3 0x92
#define PMBUS_READ_FAN_SPEED_4 0x93
#define PMBUS_READ_DUTY_CYCLE 0x94
#define PMBUS_READ_FREQUENCY 0x95
#define PMBUS_READ_POUT 0x96
#define PMBUS_READ_PIN 0x97
#define PMBUS_REVISION 0x98
#define PMBUS_MFR_ID 0x99
#define PMBUS_MFR_MODEL 0x9A
#define PMBUS_MFR_REVISION 0x9B
#define PMBUS_MFR_LOCATION 0x9C
#define PMBUS_MFR_DATE 0x9D
#define PMBUS_MFR_SERIAL 0x9E
/*
* CAPABILITY
*/
#define PB_CAPABILITY_SMBALERT (1<<4)
#define PB_CAPABILITY_ERROR_CHECK (1<<7)
/*
* VOUT_MODE
*/
#define PB_VOUT_MODE_MODE_MASK 0xe0
#define PB_VOUT_MODE_PARAM_MASK 0x1f
#define PB_VOUT_MODE_LINEAR 0x00
#define PB_VOUT_MODE_VID 0x20
#define PB_VOUT_MODE_DIRECT 0x40
/*
* Fan configuration
*/
#define PB_FAN_2_PULSE_MASK ((1 << 0) | (1 << 1))
#define PB_FAN_2_RPM (1 << 2)
#define PB_FAN_2_INSTALLED (1 << 3)
#define PB_FAN_1_PULSE_MASK ((1 << 4) | (1 << 5))
#define PB_FAN_1_RPM (1 << 6)
#define PB_FAN_1_INSTALLED (1 << 7)
/*
* STATUS_BYTE, STATUS_WORD (lower)
*/
#define PB_STATUS_NONE_ABOVE (1<<0)
#define PB_STATUS_CML (1<<1)
#define PB_STATUS_TEMPERATURE (1<<2)
#define PB_STATUS_VIN_UV (1<<3)
#define PB_STATUS_IOUT_OC (1<<4)
#define PB_STATUS_VOUT_OV (1<<5)
#define PB_STATUS_OFF (1<<6)
#define PB_STATUS_BUSY (1<<7)
/*
* STATUS_WORD (upper)
*/
#define PB_STATUS_UNKNOWN (1<<8)
#define PB_STATUS_OTHER (1<<9)
#define PB_STATUS_FANS (1<<10)
#define PB_STATUS_POWER_GOOD_N (1<<11)
#define PB_STATUS_WORD_MFR (1<<12)
#define PB_STATUS_INPUT (1<<13)
#define PB_STATUS_IOUT_POUT (1<<14)
#define PB_STATUS_VOUT (1<<15)
/*
* STATUS_IOUT
*/
#define PB_POUT_OP_WARNING (1<<0)
#define PB_POUT_OP_FAULT (1<<1)
#define PB_POWER_LIMITING (1<<2)
#define PB_CURRENT_SHARE_FAULT (1<<3)
#define PB_IOUT_UC_FAULT (1<<4)
#define PB_IOUT_OC_WARNING (1<<5)
#define PB_IOUT_OC_LV_FAULT (1<<6)
#define PB_IOUT_OC_FAULT (1<<7)
/*
* STATUS_VOUT, STATUS_INPUT
*/
#define PB_VOLTAGE_UV_FAULT (1<<4)
#define PB_VOLTAGE_UV_WARNING (1<<5)
#define PB_VOLTAGE_OV_WARNING (1<<6)
#define PB_VOLTAGE_OV_FAULT (1<<7)
/*
* STATUS_INPUT
*/
#define PB_PIN_OP_WARNING (1<<0)
#define PB_IIN_OC_WARNING (1<<1)
#define PB_IIN_OC_FAULT (1<<2)
/*
* STATUS_TEMPERATURE
*/
#define PB_TEMP_UT_FAULT (1<<4)
#define PB_TEMP_UT_WARNING (1<<5)
#define PB_TEMP_OT_WARNING (1<<6)
#define PB_TEMP_OT_FAULT (1<<7)
/*
* STATUS_FAN
*/
#define PB_FAN_AIRFLOW_WARNING (1<<0)
#define PB_FAN_AIRFLOW_FAULT (1<<1)
#define PB_FAN_FAN2_SPEED_OVERRIDE (1<<2)
#define PB_FAN_FAN1_SPEED_OVERRIDE (1<<3)
#define PB_FAN_FAN2_WARNING (1<<4)
#define PB_FAN_FAN1_WARNING (1<<5)
#define PB_FAN_FAN2_FAULT (1<<6)
#define PB_FAN_FAN1_FAULT (1<<7)
/*
* CML_FAULT_STATUS
*/
#define PB_CML_FAULT_OTHER_MEM_LOGIC (1<<0)
#define PB_CML_FAULT_OTHER_COMM (1<<1)
#define PB_CML_FAULT_PROCESSOR (1<<3)
#define PB_CML_FAULT_MEMORY (1<<4)
#define PB_CML_FAULT_PACKET_ERROR (1<<5)
#define PB_CML_FAULT_INVALID_DATA (1<<6)
#define PB_CML_FAULT_INVALID_COMMAND (1<<7)
enum pmbus_sensor_classes {
PSC_VOLTAGE_IN = 0,
PSC_VOLTAGE_OUT,
PSC_CURRENT_IN,
PSC_CURRENT_OUT,
PSC_POWER,
PSC_TEMPERATURE,
PSC_FAN,
PSC_NUM_CLASSES /* Number of power sensor classes */
};
#define PMBUS_PAGES 32 /* Per PMBus specification */
/* Functionality bit mask */
#define PMBUS_HAVE_VIN (1 << 0)
#define PMBUS_HAVE_VCAP (1 << 1)
#define PMBUS_HAVE_VOUT (1 << 2)
#define PMBUS_HAVE_IIN (1 << 3)
#define PMBUS_HAVE_IOUT (1 << 4)
#define PMBUS_HAVE_PIN (1 << 5)
#define PMBUS_HAVE_POUT (1 << 6)
#define PMBUS_HAVE_FAN12 (1 << 7)
#define PMBUS_HAVE_FAN34 (1 << 8)
#define PMBUS_HAVE_TEMP (1 << 9)
#define PMBUS_HAVE_TEMP2 (1 << 10)
#define PMBUS_HAVE_TEMP3 (1 << 11)
#define PMBUS_HAVE_STATUS_VOUT (1 << 12)
#define PMBUS_HAVE_STATUS_IOUT (1 << 13)
#define PMBUS_HAVE_STATUS_INPUT (1 << 14)
#define PMBUS_HAVE_STATUS_TEMP (1 << 15)
#define PMBUS_HAVE_STATUS_FAN12 (1 << 16)
#define PMBUS_HAVE_STATUS_FAN34 (1 << 17)
struct pmbus_driver_info {
int pages; /* Total number of pages */
bool direct[PSC_NUM_CLASSES];
/* true if device uses direct data format
for the given sensor class */
/*
* Support one set of coefficients for each sensor type
* Used for chips providing data in direct mode.
*/
int m[PSC_NUM_CLASSES]; /* mantissa for direct data format */
int b[PSC_NUM_CLASSES]; /* offset */
int R[PSC_NUM_CLASSES]; /* exponent */
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.
*/
int (*get_status)(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
* chips, and the chip functionality is not pre-determined.
*/
int (*identify)(struct i2c_client *client,
struct pmbus_driver_info *info);
};
/* Function declarations */
int pmbus_set_page(struct i2c_client *client, u8 page);
int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg);
void pmbus_clear_faults(struct i2c_client *client);
bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg);
bool pmbus_check_word_register(struct i2c_client *client, int page, int reg);
int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
struct pmbus_driver_info *info);
int pmbus_do_remove(struct i2c_client *client);
const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client
*client);
#endif /* PMBUS_H */

1658
drivers/hwmon/pmbus_core.c Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,14 @@
#ifndef _LINUX_MAX6639_H
#define _LINUX_MAX6639_H
#include <linux/types.h>
/* platform data for the MAX6639 temperature sensor and fan control */
struct max6639_platform_data {
bool pwm_polarity; /* Polarity low (0) or high (1, default) */
int ppr; /* Pulses per rotation 1..4 (default == 2) */
int rpm_range; /* 2000, 4000 (default), 8000 or 16000 */
};
#endif /* _LINUX_MAX6639_H */

45
include/linux/i2c/pmbus.h Normal file
View file

@ -0,0 +1,45 @@
/*
* Hardware monitoring driver for PMBus devices
*
* Copyright (c) 2010, 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.
*/
#ifndef _PMBUS_H_
#define _PMBUS_H_
/* flags */
/*
* PMBUS_SKIP_STATUS_CHECK
*
* During register detection, skip checking the status register for
* communication or command errors.
*
* Some PMBus chips respond with valid data when trying to read an unsupported
* register. For such chips, checking the status register is mandatory when
* trying to determine if a chip register exists or not.
* Other PMBus chips don't support the STATUS_CML register, or report
* communication errors for no explicable reason. For such chips, checking
* the status register must be disabled.
*/
#define PMBUS_SKIP_STATUS_CHECK (1 << 0)
struct pmbus_platform_data {
u32 flags; /* Device specific flags */
};
#endif /* _PMBUS_H_ */