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:
commit
19520fc1ee
|
@ -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.
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
-----------------------
|
||||
|
|
|
@ -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)
|
|
@ -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
|
|
@ -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.
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 @@ exit_free:
|
|||
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;
|
||||
|
|
|
@ -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);
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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, <c4151_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, <c4151_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, <c4151_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(<c4151_driver);
|
||||
}
|
||||
|
||||
static void __exit ltc4151_exit(void)
|
||||
{
|
||||
i2c_del_driver(<c4151_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Per Dalen <per.dalen@appeartv.com>");
|
||||
MODULE_DESCRIPTION("LTC4151 driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(ltc4151_init);
|
||||
module_exit(ltc4151_exit);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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 */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
|
@ -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_ */
|
Loading…
Reference in New Issue