You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
* 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (26 commits) hwmon: (w83627ehf) Better fix for negative temperature values hwmon: (w83627ehf) Uninline is_word_sized hwmon: (lm75) Document why clones are not detected hwmon: (w83627ehf) Move fan pins check to a separate function hwmon: (w83627ehf) Skip reading unused voltage registers hwmon: (lm75) Add support for Analog Devices ADT75 hwmon: (pmbus_core) Simplify sign extensions hwmon: (pmbus) Add support for Lineage Power DC-DC converters hwmon: (pmbus/ltc2978) Add support for LTC3880 to LTC2978 driver hwmon: (pmbus/ltc2978) Explicit driver for LTC2978 hwmon: (pmbus) Add support for TEMP2 peak attributes hwmon: AD7314 driver (ported from IIO) hwmon: (pmbus) Add support for Intersil power management chips hwmon: (pmbus) Always call _pmbus_read_byte in core driver hwmon: (pmbus) Replace EINVAL return codes with more appropriate errors hwmon: (pmbus) Provide more documentation hwmon/f71882fg: Make the decision wether to register fan attr. per fan hwmon/f71882fg: Add a f71882fg_create_fan_sysfs_files helper function hwmon/f71882fg: Make all fan/pwm attr tables 2 dimensional hwmon: (exynos4_tmu) Remove IRQF_DISABLED ...
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
Kernel driver ad7314
|
||||
====================
|
||||
|
||||
Supported chips:
|
||||
* Analog Devices AD7314
|
||||
Prefix: 'ad7314'
|
||||
Datasheet: Publicly available at Analog Devices website.
|
||||
* Analog Devices ADT7301
|
||||
Prefix: 'adt7301'
|
||||
Datasheet: Publicly available at Analog Devices website.
|
||||
* Analog Devices ADT7302
|
||||
Prefix: 'adt7302'
|
||||
Datasheet: Publicly available at Analog Devices website.
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
Driver supports the above parts. The ad7314 has a 10 bit
|
||||
sensor with 1lsb = 0.25 degrees centigrade. The adt7301 and
|
||||
adt7302 have 14 bit sensors with 1lsb = 0.03125 degrees centigrade.
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
Currently power down mode is not supported.
|
||||
+26
-14
@@ -6,6 +6,10 @@ Supported chips:
|
||||
Prefix: 'adm1275'
|
||||
Addresses scanned: -
|
||||
Datasheet: www.analog.com/static/imported-files/data_sheets/ADM1275.pdf
|
||||
* Analog Devices ADM1276
|
||||
Prefix: 'adm1276'
|
||||
Addresses scanned: -
|
||||
Datasheet: www.analog.com/static/imported-files/data_sheets/ADM1276.pdf
|
||||
|
||||
Author: Guenter Roeck <guenter.roeck@ericsson.com>
|
||||
|
||||
@@ -13,13 +17,13 @@ Author: Guenter Roeck <guenter.roeck@ericsson.com>
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver supports hardware montoring for Analog Devices ADM1275 Hot-Swap
|
||||
Controller and Digital Power Monitor.
|
||||
This driver supports hardware montoring for Analog Devices ADM1275 and ADM1276
|
||||
Hot-Swap Controller and Digital Power Monitor.
|
||||
|
||||
The ADM1275 is a hot-swap controller that allows a circuit board to be removed
|
||||
from or inserted into a live backplane. It also features current and voltage
|
||||
readback via an integrated 12-bit analog-to-digital converter (ADC), accessed
|
||||
using a PMBus. interface.
|
||||
ADM1275 and ADM1276 are hot-swap controllers that allow a circuit board to be
|
||||
removed from or inserted into a live backplane. They also feature current and
|
||||
voltage readback via an integrated 12-bit analog-to-digital converter (ADC),
|
||||
accessed using a PMBus interface.
|
||||
|
||||
The driver is a client driver to the core PMBus driver. Please see
|
||||
Documentation/hwmon/pmbus for details on PMBus client drivers.
|
||||
@@ -48,17 +52,25 @@ attributes are write-only, all other attributes are read-only.
|
||||
|
||||
in1_label "vin1" or "vout1" depending on chip variant and
|
||||
configuration.
|
||||
in1_input Measured voltage. From READ_VOUT register.
|
||||
in1_min Minumum Voltage. From VOUT_UV_WARN_LIMIT register.
|
||||
in1_max Maximum voltage. From VOUT_OV_WARN_LIMIT register.
|
||||
in1_min_alarm Voltage low alarm. From VOLTAGE_UV_WARNING status.
|
||||
in1_max_alarm Voltage high alarm. From VOLTAGE_OV_WARNING status.
|
||||
in1_input Measured voltage.
|
||||
in1_min Minumum Voltage.
|
||||
in1_max Maximum voltage.
|
||||
in1_min_alarm Voltage low alarm.
|
||||
in1_max_alarm Voltage high alarm.
|
||||
in1_highest Historical maximum voltage.
|
||||
in1_reset_history Write any value to reset history.
|
||||
|
||||
curr1_label "iout1"
|
||||
curr1_input Measured current. From READ_IOUT register.
|
||||
curr1_max Maximum current. From IOUT_OC_WARN_LIMIT register.
|
||||
curr1_max_alarm Current high alarm. From IOUT_OC_WARN_LIMIT register.
|
||||
curr1_input Measured current.
|
||||
curr1_max Maximum current.
|
||||
curr1_max_alarm Current high alarm.
|
||||
curr1_lcrit Critical minimum current. Depending on the chip
|
||||
configuration, either curr1_lcrit or curr1_crit is
|
||||
supported, but not both.
|
||||
curr1_lcrit_alarm Critical current low alarm.
|
||||
curr1_crit Critical maximum current. Depending on the chip
|
||||
configuration, either curr1_lcrit or curr1_crit is
|
||||
supported, but not both.
|
||||
curr1_crit_alarm Critical current high alarm.
|
||||
curr1_highest Historical maximum current.
|
||||
curr1_reset_history Write any value to reset history.
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
Kernel driver exynos4_tmu
|
||||
=================
|
||||
|
||||
Supported chips:
|
||||
* ARM SAMSUNG EXYNOS4 series of SoC
|
||||
Prefix: 'exynos4-tmu'
|
||||
Datasheet: Not publicly available
|
||||
|
||||
Authors: Donggeun Kim <dg77.kim@samsung.com>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver allows to read temperature inside SAMSUNG EXYNOS4 series of SoC.
|
||||
|
||||
The chip only exposes the measured 8-bit temperature code value
|
||||
through a register.
|
||||
Temperature can be taken from the temperature code.
|
||||
There are three equations converting from temperature to temperature code.
|
||||
|
||||
The three equations are:
|
||||
1. Two point trimming
|
||||
Tc = (T - 25) * (TI2 - TI1) / (85 - 25) + TI1
|
||||
|
||||
2. One point trimming
|
||||
Tc = T + TI1 - 25
|
||||
|
||||
3. No trimming
|
||||
Tc = T + 50
|
||||
|
||||
Tc: Temperature code, T: Temperature,
|
||||
TI1: Trimming info for 25 degree Celsius (stored at TRIMINFO register)
|
||||
Temperature code measured at 25 degree Celsius which is unchanged
|
||||
TI2: Trimming info for 85 degree Celsius (stored at TRIMINFO register)
|
||||
Temperature code measured at 85 degree Celsius which is unchanged
|
||||
|
||||
TMU(Thermal Management Unit) in EXYNOS4 generates interrupt
|
||||
when temperature exceeds pre-defined levels.
|
||||
The maximum number of configurable threshold is four.
|
||||
The threshold levels are defined as follows:
|
||||
Level_0: current temperature > trigger_level_0 + threshold
|
||||
Level_1: current temperature > trigger_level_1 + threshold
|
||||
Level_2: current temperature > trigger_level_2 + threshold
|
||||
Level_3: current temperature > trigger_level_3 + threshold
|
||||
|
||||
The threshold and each trigger_level are set
|
||||
through the corresponding registers.
|
||||
|
||||
When an interrupt occurs, this driver notify user space of
|
||||
one of four threshold levels for the interrupt
|
||||
through kobject_uevent_env and sysfs_notify functions.
|
||||
Although an interrupt condition for level_0 can be set,
|
||||
it is not notified to user space through sysfs_notify function.
|
||||
|
||||
Sysfs Interface
|
||||
---------------
|
||||
name name of the temperature sensor
|
||||
RO
|
||||
|
||||
temp1_input temperature
|
||||
RO
|
||||
|
||||
temp1_max temperature for level_1 interrupt
|
||||
RO
|
||||
|
||||
temp1_crit temperature for level_2 interrupt
|
||||
RO
|
||||
|
||||
temp1_emergency temperature for level_3 interrupt
|
||||
RO
|
||||
|
||||
temp1_max_alarm alarm for level_1 interrupt
|
||||
RO
|
||||
|
||||
temp1_crit_alarm
|
||||
alarm for level_2 interrupt
|
||||
RO
|
||||
|
||||
temp1_emergency_alarm
|
||||
alarm for level_3 interrupt
|
||||
RO
|
||||
+38
-23
@@ -12,26 +12,46 @@ Supported chips:
|
||||
Addresses scanned: I2C 0x48 - 0x4f
|
||||
Datasheet: Publicly available at the National Semiconductor website
|
||||
http://www.national.com/
|
||||
* Dallas Semiconductor DS75
|
||||
Prefix: 'lm75'
|
||||
Addresses scanned: I2C 0x48 - 0x4f
|
||||
Datasheet: Publicly available at the Dallas Semiconductor website
|
||||
http://www.maxim-ic.com/
|
||||
* Dallas Semiconductor DS1775
|
||||
Prefix: 'lm75'
|
||||
Addresses scanned: I2C 0x48 - 0x4f
|
||||
* Dallas Semiconductor DS75, DS1775
|
||||
Prefixes: 'ds75', 'ds1775'
|
||||
Addresses scanned: none
|
||||
Datasheet: Publicly available at the Dallas Semiconductor website
|
||||
http://www.maxim-ic.com/
|
||||
* Maxim MAX6625, MAX6626
|
||||
Prefix: 'lm75'
|
||||
Addresses scanned: I2C 0x48 - 0x4b
|
||||
Prefixes: 'max6625', 'max6626'
|
||||
Addresses scanned: none
|
||||
Datasheet: Publicly available at the Maxim website
|
||||
http://www.maxim-ic.com/
|
||||
* Microchip (TelCom) TCN75
|
||||
Prefix: 'lm75'
|
||||
Addresses scanned: I2C 0x48 - 0x4f
|
||||
Addresses scanned: none
|
||||
Datasheet: Publicly available at the Microchip website
|
||||
http://www.microchip.com/
|
||||
* Microchip MCP9800, MCP9801, MCP9802, MCP9803
|
||||
Prefix: 'mcp980x'
|
||||
Addresses scanned: none
|
||||
Datasheet: Publicly available at the Microchip website
|
||||
http://www.microchip.com/
|
||||
* Analog Devices ADT75
|
||||
Prefix: 'adt75'
|
||||
Addresses scanned: none
|
||||
Datasheet: Publicly available at the Analog Devices website
|
||||
http://www.analog.com/adt75
|
||||
* ST Microelectronics STDS75
|
||||
Prefix: 'stds75'
|
||||
Addresses scanned: none
|
||||
Datasheet: Publicly available at the ST website
|
||||
http://www.st.com/internet/analog/product/121769.jsp
|
||||
* Texas Instruments TMP100, TMP101, TMP105, TMP75, TMP175, TMP275
|
||||
Prefixes: 'tmp100', 'tmp101', 'tmp105', 'tmp175', 'tmp75', 'tmp275'
|
||||
Addresses scanned: none
|
||||
Datasheet: Publicly available at the Texas Instruments website
|
||||
http://www.ti.com/product/tmp100
|
||||
http://www.ti.com/product/tmp101
|
||||
http://www.ti.com/product/tmp105
|
||||
http://www.ti.com/product/tmp75
|
||||
http://www.ti.com/product/tmp175
|
||||
http://www.ti.com/product/tmp275
|
||||
|
||||
Author: Frodo Looijaard <frodol@dds.nl>
|
||||
|
||||
@@ -50,21 +70,16 @@ range of -55 to +125 degrees.
|
||||
The LM75 only updates its values each 1.5 seconds; reading it more often
|
||||
will do no harm, but will return 'old' values.
|
||||
|
||||
The LM75 is usually used in combination with LM78-like chips, to measure
|
||||
the temperature of the processor(s).
|
||||
|
||||
The DS75, DS1775, MAX6625, and MAX6626 are supported as well.
|
||||
They are not distinguished from an LM75. While most of these chips
|
||||
have three additional bits of accuracy (12 vs. 9 for the LM75),
|
||||
the additional bits are not supported. Not only that, but these chips will
|
||||
not be detected if not in 9-bit precision mode (use the force parameter if
|
||||
needed).
|
||||
|
||||
The TCN75 is supported as well, and is not distinguished from an LM75.
|
||||
The original LM75 was typically used in combination with LM78-like chips
|
||||
on PC motherboards, to measure the temperature of the processor(s). Clones
|
||||
are now used in various embedded designs.
|
||||
|
||||
The LM75 is essentially an industry standard; there may be other
|
||||
LM75 clones not listed here, with or without various enhancements,
|
||||
that are supported.
|
||||
that are supported. The clones are not detected by the driver, unless
|
||||
they reproduce the exact register tricks of the original LM75, and must
|
||||
therefore be instantiated explicitly. The specific enhancements (such as
|
||||
higher resolution) are not currently supported by the driver.
|
||||
|
||||
The LM77 is not supported, contrary to what we pretended for a long time.
|
||||
Both chips are simply not compatible, value encoding differs.
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
Kernel driver ltc2978
|
||||
=====================
|
||||
|
||||
Supported chips:
|
||||
* Linear Technology LTC2978
|
||||
Prefix: 'ltc2978'
|
||||
Addresses scanned: -
|
||||
Datasheet: http://cds.linear.com/docs/Datasheet/2978fa.pdf
|
||||
* Linear Technology LTC3880
|
||||
Prefix: 'ltc3880'
|
||||
Addresses scanned: -
|
||||
Datasheet: http://cds.linear.com/docs/Datasheet/3880f.pdf
|
||||
|
||||
Author: Guenter Roeck <guenter.roeck@ericsson.com>
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The LTC2978 is an octal power supply monitor, supervisor, sequencer and
|
||||
margin controller. The LTC3880 is a dual, PolyPhase DC/DC synchronous
|
||||
step-down switching regulator controller.
|
||||
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
This driver does not probe for PMBus devices. You will have to instantiate
|
||||
devices explicitly.
|
||||
|
||||
Example: the following commands will load the driver for an LTC2978 at address
|
||||
0x60 on I2C bus #1:
|
||||
|
||||
# modprobe ltc2978
|
||||
# echo ltc2978 0x60 > /sys/bus/i2c/devices/i2c-1/new_device
|
||||
|
||||
|
||||
Sysfs attributes
|
||||
----------------
|
||||
|
||||
in1_label "vin"
|
||||
in1_input Measured input voltage.
|
||||
in1_min Minimum input voltage.
|
||||
in1_max Maximum input voltage.
|
||||
in1_lcrit Critical minimum input voltage.
|
||||
in1_crit Critical maximum input voltage.
|
||||
in1_min_alarm Input voltage low alarm.
|
||||
in1_max_alarm Input voltage high alarm.
|
||||
in1_lcrit_alarm Input voltage critical low alarm.
|
||||
in1_crit_alarm Input voltage critical high alarm.
|
||||
in1_lowest Lowest input voltage. LTC2978 only.
|
||||
in1_highest Highest input voltage.
|
||||
in1_reset_history Reset history. Writing into this attribute will reset
|
||||
history for all attributes.
|
||||
|
||||
in[2-9]_label "vout[1-8]". Channels 3 to 9 on LTC2978 only.
|
||||
in[2-9]_input Measured output voltage.
|
||||
in[2-9]_min Minimum output voltage.
|
||||
in[2-9]_max Maximum output voltage.
|
||||
in[2-9]_lcrit Critical minimum output voltage.
|
||||
in[2-9]_crit Critical maximum output voltage.
|
||||
in[2-9]_min_alarm Output voltage low alarm.
|
||||
in[2-9]_max_alarm Output voltage high alarm.
|
||||
in[2-9]_lcrit_alarm Output voltage critical low alarm.
|
||||
in[2-9]_crit_alarm Output voltage critical high alarm.
|
||||
in[2-9]_lowest Lowest output voltage. LTC2978 only.
|
||||
in[2-9]_highest Lowest output voltage.
|
||||
in[2-9]_reset_history Reset history. Writing into this attribute will reset
|
||||
history for all attributes.
|
||||
|
||||
temp[1-3]_input Measured temperature.
|
||||
On LTC2978, only one temperature measurement is
|
||||
supported and reflects the internal temperature.
|
||||
On LTC3880, temp1 and temp2 report external
|
||||
temperatures, and temp3 reports the internal
|
||||
temperature.
|
||||
temp[1-3]_min Mimimum temperature.
|
||||
temp[1-3]_max Maximum temperature.
|
||||
temp[1-3]_lcrit Critical low temperature.
|
||||
temp[1-3]_crit Critical high temperature.
|
||||
temp[1-3]_min_alarm Chip temperature low alarm.
|
||||
temp[1-3]_max_alarm Chip temperature high alarm.
|
||||
temp[1-3]_lcrit_alarm Chip temperature critical low alarm.
|
||||
temp[1-3]_crit_alarm Chip temperature critical high alarm.
|
||||
temp[1-3]_lowest Lowest measured temperature. LTC2978 only.
|
||||
temp[1-3]_highest Highest measured temperature.
|
||||
temp[1-3]_reset_history Reset history. Writing into this attribute will reset
|
||||
history for all attributes.
|
||||
|
||||
power[1-2]_label "pout[1-2]". LTC3880 only.
|
||||
power[1-2]_input Measured power.
|
||||
|
||||
curr1_label "iin". LTC3880 only.
|
||||
curr1_input Measured input current.
|
||||
curr1_max Maximum input current.
|
||||
curr1_max_alarm Input current high alarm.
|
||||
|
||||
curr[2-3]_label "iout[1-2]". LTC3880 only.
|
||||
curr[2-3]_input Measured input current.
|
||||
curr[2-3]_max Maximum input current.
|
||||
curr[2-3]_crit Critical input current.
|
||||
curr[2-3]_max_alarm Input current high alarm.
|
||||
curr[2-3]_crit_alarm Input current critical high alarm.
|
||||
@@ -8,11 +8,6 @@ Supported chips:
|
||||
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
|
||||
* ON Semiconductor ADP4000, NCP4200, NCP4208
|
||||
Prefixes: 'adp4000', 'ncp4200', 'ncp4208'
|
||||
Addresses scanned: -
|
||||
@@ -20,6 +15,14 @@ Supported chips:
|
||||
http://www.onsemi.com/pub_link/Collateral/ADP4000-D.PDF
|
||||
http://www.onsemi.com/pub_link/Collateral/NCP4200-D.PDF
|
||||
http://www.onsemi.com/pub_link/Collateral/JUNE%202009-%20REV.%200.PDF
|
||||
* Lineage Power
|
||||
Prefixes: 'pdt003', 'pdt006', 'pdt012', 'udt020'
|
||||
Addresses scanned: -
|
||||
Datasheets:
|
||||
http://www.lineagepower.com/oem/pdf/PDT003A0X.pdf
|
||||
http://www.lineagepower.com/oem/pdf/PDT006A0X.pdf
|
||||
http://www.lineagepower.com/oem/pdf/PDT012A0X.pdf
|
||||
http://www.lineagepower.com/oem/pdf/UDT020A0X.pdf
|
||||
* Generic PMBus devices
|
||||
Prefix: 'pmbus'
|
||||
Addresses scanned: -
|
||||
|
||||
@@ -0,0 +1,283 @@
|
||||
PMBus core driver and internal API
|
||||
==================================
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
[from pmbus.org] The Power Management Bus (PMBus) is an open standard
|
||||
power-management protocol with a fully defined command language that facilitates
|
||||
communication with power converters and other devices in a power system. The
|
||||
protocol is implemented over the industry-standard SMBus serial interface and
|
||||
enables programming, control, and real-time monitoring of compliant power
|
||||
conversion products. This flexible and highly versatile standard allows for
|
||||
communication between devices based on both analog and digital technologies, and
|
||||
provides true interoperability which will reduce design complexity and shorten
|
||||
time to market for power system designers. Pioneered by leading power supply and
|
||||
semiconductor companies, this open power system standard is maintained and
|
||||
promoted by the PMBus Implementers Forum (PMBus-IF), comprising 30+ adopters
|
||||
with the objective to provide support to, and facilitate adoption among, users.
|
||||
|
||||
Unfortunately, while PMBus commands are standardized, there are no mandatory
|
||||
commands, and manufacturers can add as many non-standard commands as they like.
|
||||
Also, different PMBUs devices act differently if non-supported commands are
|
||||
executed. Some devices return an error, some devices return 0xff or 0xffff and
|
||||
set a status error flag, and some devices may simply hang up.
|
||||
|
||||
Despite all those difficulties, a generic PMBus device driver is still useful
|
||||
and supported since kernel version 2.6.39. However, it was necessary to support
|
||||
device specific extensions in addition to the core PMBus driver, since it is
|
||||
simply unknown what new device specific functionality PMBus device developers
|
||||
come up with next.
|
||||
|
||||
To make device specific extensions as scalable as possible, and to avoid having
|
||||
to modify the core PMBus driver repeatedly for new devices, the PMBus driver was
|
||||
split into core, generic, and device specific code. The core code (in
|
||||
pmbus_core.c) provides generic functionality. The generic code (in pmbus.c)
|
||||
provides support for generic PMBus devices. Device specific code is responsible
|
||||
for device specific initialization and, if needed, maps device specific
|
||||
functionality into generic functionality. This is to some degree comparable
|
||||
to PCI code, where generic code is augmented as needed with quirks for all kinds
|
||||
of devices.
|
||||
|
||||
PMBus device capabilities auto-detection
|
||||
========================================
|
||||
|
||||
For generic PMBus devices, code in pmbus.c attempts to auto-detect all supported
|
||||
PMBus commands. Auto-detection is somewhat limited, since there are simply too
|
||||
many variables to consider. For example, it is almost impossible to autodetect
|
||||
which PMBus commands are paged and which commands are replicated across all
|
||||
pages (see the PMBus specification for details on multi-page PMBus devices).
|
||||
|
||||
For this reason, it often makes sense to provide a device specific driver if not
|
||||
all commands can be auto-detected. The data structures in this driver can be
|
||||
used to inform the core driver about functionality supported by individual
|
||||
chips.
|
||||
|
||||
Some commands are always auto-detected. This applies to all limit commands
|
||||
(lcrit, min, max, and crit attributes) as well as associated alarm attributes.
|
||||
Limits and alarm attributes are auto-detected because there are simply too many
|
||||
possible combinations to provide a manual configuration interface.
|
||||
|
||||
PMBus internal API
|
||||
==================
|
||||
|
||||
The API between core and device specific PMBus code is defined in
|
||||
drivers/hwmon/pmbus/pmbus.h. In addition to the internal API, pmbus.h defines
|
||||
standard PMBus commands and virtual PMBus commands.
|
||||
|
||||
Standard PMBus commands
|
||||
-----------------------
|
||||
|
||||
Standard PMBus commands (commands values 0x00 to 0xff) are defined in the PMBUs
|
||||
specification.
|
||||
|
||||
Virtual PMBus commands
|
||||
----------------------
|
||||
|
||||
Virtual PMBus commands are provided to enable support for non-standard
|
||||
functionality which has been implemented by several chip vendors and is thus
|
||||
desirable to support.
|
||||
|
||||
Virtual PMBus commands start with command value 0x100 and can thus easily be
|
||||
distinguished from standard PMBus commands (which can not have values larger
|
||||
than 0xff). Support for virtual PMBus commands is device specific and thus has
|
||||
to be implemented in device specific code.
|
||||
|
||||
Virtual commands are named PMBUS_VIRT_xxx and start with PMBUS_VIRT_BASE. All
|
||||
virtual commands are word sized.
|
||||
|
||||
There are currently two types of virtual commands.
|
||||
|
||||
- READ commands are read-only; writes are either ignored or return an error.
|
||||
- RESET commands are read/write. Reading reset registers returns zero
|
||||
(used for detection), writing any value causes the associated history to be
|
||||
reset.
|
||||
|
||||
Virtual commands have to be handled in device specific driver code. Chip driver
|
||||
code returns non-negative values if a virtual command is supported, or a
|
||||
negative error code if not. The chip driver may return -ENODATA or any other
|
||||
Linux error code in this case, though an error code other than -ENODATA is
|
||||
handled more efficiently and thus preferred. Either case, the calling PMBus
|
||||
core code will abort if the chip driver returns an error code when reading
|
||||
or writing virtual registers (in other words, the PMBus core code will never
|
||||
send a virtual command to a chip).
|
||||
|
||||
PMBus driver information
|
||||
------------------------
|
||||
|
||||
PMBus driver information, defined in struct pmbus_driver_info, is the main means
|
||||
for device specific drivers to pass information to the core PMBus driver.
|
||||
Specifically, it provides the following information.
|
||||
|
||||
- For devices supporting its data in Direct Data Format, it provides coefficients
|
||||
for converting register values into normalized data. This data is usually
|
||||
provided by chip manufacturers in device datasheets.
|
||||
- Supported chip functionality can be provided to the core driver. This may be
|
||||
necessary for chips which react badly if non-supported commands are executed,
|
||||
and/or to speed up device detection and initialization.
|
||||
- Several function entry points are provided to support overriding and/or
|
||||
augmenting generic command execution. This functionality can be used to map
|
||||
non-standard PMBus commands to standard commands, or to augment standard
|
||||
command return values with device specific information.
|
||||
|
||||
API functions
|
||||
-------------
|
||||
|
||||
Functions provided by chip driver
|
||||
---------------------------------
|
||||
|
||||
All functions return the command return value (read) or zero (write) if
|
||||
successful. A return value of -ENODATA indicates that there is no manufacturer
|
||||
specific command, but that a standard PMBus command may exist. Any other
|
||||
negative return value indicates that the commands does not exist for this
|
||||
chip, and that no attempt should be made to read or write the standard
|
||||
command.
|
||||
|
||||
As mentioned above, an exception to this rule applies to virtual commands,
|
||||
which _must_ be handled in driver specific code. See "Virtual PMBus Commands"
|
||||
above for more details.
|
||||
|
||||
Command execution in the core PMBus driver code is as follows.
|
||||
|
||||
if (chip_access_function) {
|
||||
status = chip_access_function();
|
||||
if (status != -ENODATA)
|
||||
return status;
|
||||
}
|
||||
if (command >= PMBUS_VIRT_BASE) /* For word commands/registers only */
|
||||
return -EINVAL;
|
||||
return generic_access();
|
||||
|
||||
Chip drivers may provide pointers to the following functions in struct
|
||||
pmbus_driver_info. All functions are optional.
|
||||
|
||||
int (*read_byte_data)(struct i2c_client *client, int page, int reg);
|
||||
|
||||
Read byte from page <page>, register <reg>.
|
||||
<page> may be -1, which means "current page".
|
||||
|
||||
int (*read_word_data)(struct i2c_client *client, int page, int reg);
|
||||
|
||||
Read word from page <page>, register <reg>.
|
||||
|
||||
int (*write_word_data)(struct i2c_client *client, int page, int reg,
|
||||
u16 word);
|
||||
|
||||
Write word to page <page>, register <reg>.
|
||||
|
||||
int (*write_byte)(struct i2c_client *client, int page, u8 value);
|
||||
|
||||
Write byte to page <page>, register <reg>.
|
||||
<page> may be -1, which means "current page".
|
||||
|
||||
int (*identify)(struct i2c_client *client, struct pmbus_driver_info *info);
|
||||
|
||||
Determine supported PMBus functionality. This function is only necessary
|
||||
if a chip driver supports multiple chips, and the chip functionality is not
|
||||
pre-determined. It is currently only used by the generic pmbus driver
|
||||
(pmbus.c).
|
||||
|
||||
Functions exported by core driver
|
||||
---------------------------------
|
||||
|
||||
Chip drivers are expected to use the following functions to read or write
|
||||
PMBus registers. Chip drivers may also use direct I2C commands. If direct I2C
|
||||
commands are used, the chip driver code must not directly modify the current
|
||||
page, since the selected page is cached in the core driver and the core driver
|
||||
will assume that it is selected. Using pmbus_set_page() to select a new page
|
||||
is mandatory.
|
||||
|
||||
int pmbus_set_page(struct i2c_client *client, u8 page);
|
||||
|
||||
Set PMBus page register to <page> for subsequent commands.
|
||||
|
||||
int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg);
|
||||
|
||||
Read word data from <page>, <reg>. Similar to i2c_smbus_read_word_data(), but
|
||||
selects page first.
|
||||
|
||||
int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg,
|
||||
u16 word);
|
||||
|
||||
Write word data to <page>, <reg>. Similar to i2c_smbus_write_word_data(), but
|
||||
selects page first.
|
||||
|
||||
int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg);
|
||||
|
||||
Read byte data from <page>, <reg>. Similar to i2c_smbus_read_byte_data(), but
|
||||
selects page first. <page> may be -1, which means "current page".
|
||||
|
||||
int pmbus_write_byte(struct i2c_client *client, int page, u8 value);
|
||||
|
||||
Write byte data to <page>, <reg>. Similar to i2c_smbus_write_byte(), but
|
||||
selects page first. <page> may be -1, which means "current page".
|
||||
|
||||
void pmbus_clear_faults(struct i2c_client *client);
|
||||
|
||||
Execute PMBus "Clear Fault" command on all chip pages.
|
||||
This function calls the device specific write_byte function if defined.
|
||||
Therefore, it must _not_ be called from that function.
|
||||
|
||||
bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg);
|
||||
|
||||
Check if byte register exists. Return true if the register exists, false
|
||||
otherwise.
|
||||
This function calls the device specific write_byte function if defined to
|
||||
obtain the chip status. Therefore, it must _not_ be called from that function.
|
||||
|
||||
bool pmbus_check_word_register(struct i2c_client *client, int page, int reg);
|
||||
|
||||
Check if word register exists. Return true if the register exists, false
|
||||
otherwise.
|
||||
This function calls the device specific write_byte function if defined to
|
||||
obtain the chip status. Therefore, it must _not_ be called from that function.
|
||||
|
||||
int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
|
||||
struct pmbus_driver_info *info);
|
||||
|
||||
Execute probe function. Similar to standard probe function for other drivers,
|
||||
with the pointer to struct pmbus_driver_info as additional argument. Calls
|
||||
identify function if supported. Must only be called from device probe
|
||||
function.
|
||||
|
||||
void pmbus_do_remove(struct i2c_client *client);
|
||||
|
||||
Execute driver remove function. Similar to standard driver remove function.
|
||||
|
||||
const struct pmbus_driver_info
|
||||
*pmbus_get_driver_info(struct i2c_client *client);
|
||||
|
||||
Return pointer to struct pmbus_driver_info as passed to pmbus_do_probe().
|
||||
|
||||
|
||||
PMBus driver platform data
|
||||
==========================
|
||||
|
||||
PMBus platform data is defined in include/linux/i2c/pmbus.h. Platform data
|
||||
currently only provides a flag field with a single bit used.
|
||||
|
||||
#define PMBUS_SKIP_STATUS_CHECK (1 << 0)
|
||||
|
||||
struct pmbus_platform_data {
|
||||
u32 flags; /* Device specific flags */
|
||||
};
|
||||
|
||||
|
||||
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.
|
||||
|
||||
Some i2c controllers do not support single-byte commands (write commands with
|
||||
no data, i2c_smbus_write_byte()). With such controllers, clearing the status
|
||||
register is impossible, and the PMBUS_SKIP_STATUS_CHECK flag must be set.
|
||||
@@ -0,0 +1,125 @@
|
||||
Kernel driver zl6100
|
||||
====================
|
||||
|
||||
Supported chips:
|
||||
* Intersil / Zilker Labs ZL2004
|
||||
Prefix: 'zl2004'
|
||||
Addresses scanned: -
|
||||
Datasheet: http://www.intersil.com/data/fn/fn6847.pdf
|
||||
* Intersil / Zilker Labs ZL2006
|
||||
Prefix: 'zl2006'
|
||||
Addresses scanned: -
|
||||
Datasheet: http://www.intersil.com/data/fn/fn6850.pdf
|
||||
* Intersil / Zilker Labs ZL2008
|
||||
Prefix: 'zl2008'
|
||||
Addresses scanned: -
|
||||
Datasheet: http://www.intersil.com/data/fn/fn6859.pdf
|
||||
* Intersil / Zilker Labs ZL2105
|
||||
Prefix: 'zl2105'
|
||||
Addresses scanned: -
|
||||
Datasheet: http://www.intersil.com/data/fn/fn6851.pdf
|
||||
* Intersil / Zilker Labs ZL2106
|
||||
Prefix: 'zl2106'
|
||||
Addresses scanned: -
|
||||
Datasheet: http://www.intersil.com/data/fn/fn6852.pdf
|
||||
* Intersil / Zilker Labs ZL6100
|
||||
Prefix: 'zl6100'
|
||||
Addresses scanned: -
|
||||
Datasheet: http://www.intersil.com/data/fn/fn6876.pdf
|
||||
* Intersil / Zilker Labs ZL6105
|
||||
Prefix: 'zl6105'
|
||||
Addresses scanned: -
|
||||
Datasheet: http://www.intersil.com/data/fn/fn6906.pdf
|
||||
|
||||
Author: Guenter Roeck <guenter.roeck@ericsson.com>
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver supports hardware montoring for Intersil / Zilker Labs ZL6100 and
|
||||
compatible digital DC-DC controllers.
|
||||
|
||||
The driver is a client driver to the core PMBus driver. Please see
|
||||
Documentation/hwmon/pmbus and Documentation.hwmon/pmbus-core for details
|
||||
on PMBus client drivers.
|
||||
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
This driver does not auto-detect devices. You will have to instantiate the
|
||||
devices explicitly. Please see Documentation/i2c/instantiating-devices for
|
||||
details.
|
||||
|
||||
WARNING: Do not access chip registers using the i2cdump command, and do not use
|
||||
any of the i2ctools commands on a command register used to save and restore
|
||||
configuration data (0x11, 0x12, 0x15, 0x16, and 0xf4). The chips supported by
|
||||
this driver interpret any access to those command registers (including read
|
||||
commands) as request to execute the command in question. Unless write accesses
|
||||
to those registers are protected, this may result in power loss, board resets,
|
||||
and/or Flash corruption. Worst case, your board may turn into a brick.
|
||||
|
||||
|
||||
Platform data support
|
||||
---------------------
|
||||
|
||||
The driver supports standard PMBus driver platform data.
|
||||
|
||||
|
||||
Module parameters
|
||||
-----------------
|
||||
|
||||
delay
|
||||
-----
|
||||
|
||||
Some Intersil/Zilker Labs DC-DC controllers require a minimum interval between
|
||||
I2C bus accesses. According to Intersil, the minimum interval is 2 ms, though
|
||||
1 ms appears to be sufficient and has not caused any problems in testing.
|
||||
The problem is known to affect ZL6100, ZL2105, and ZL2008. It is known not to
|
||||
affect ZL2004 and ZL6105. The driver automatically sets the interval to 1 ms
|
||||
except for ZL2004 and ZL6105. To enable manual override, the driver provides a
|
||||
writeable module parameter, 'delay', which can be used to set the interval to
|
||||
a value between 0 and 65,535 microseconds.
|
||||
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
The following attributes are supported. Limits are read-write; all other
|
||||
attributes are read-only.
|
||||
|
||||
in1_label "vin"
|
||||
in1_input Measured input voltage.
|
||||
in1_min Minimum input voltage.
|
||||
in1_max Maximum input voltage.
|
||||
in1_lcrit Critical minumum input voltage.
|
||||
in1_crit Critical maximum input voltage.
|
||||
in1_min_alarm Input voltage low alarm.
|
||||
in1_max_alarm Input voltage high alarm.
|
||||
in1_lcrit_alarm Input voltage critical low alarm.
|
||||
in1_crit_alarm Input voltage critical high alarm.
|
||||
|
||||
in2_label "vout1"
|
||||
in2_input Measured output voltage.
|
||||
in2_lcrit Critical minumum output Voltage.
|
||||
in2_crit Critical maximum output voltage.
|
||||
in2_lcrit_alarm Critical output voltage critical low alarm.
|
||||
in2_crit_alarm Critical output voltage critical high alarm.
|
||||
|
||||
curr1_label "iout1"
|
||||
curr1_input Measured output current.
|
||||
curr1_lcrit Critical minimum output current.
|
||||
curr1_crit Critical maximum output current.
|
||||
curr1_lcrit_alarm Output current critical low alarm.
|
||||
curr1_crit_alarm Output current critical high alarm.
|
||||
|
||||
temp[12]_input Measured temperature.
|
||||
temp[12]_min Minimum temperature.
|
||||
temp[12]_max Maximum temperature.
|
||||
temp[12]_lcrit Critical low temperature.
|
||||
temp[12]_crit Critical high temperature.
|
||||
temp[12]_min_alarm Chip temperature low alarm.
|
||||
temp[12]_max_alarm Chip temperature high alarm.
|
||||
temp[12]_lcrit_alarm Chip temperature critical low alarm.
|
||||
temp[12]_crit_alarm Chip temperature critical high alarm.
|
||||
@@ -68,6 +68,16 @@ config SENSORS_ABITUGURU3
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called abituguru3.
|
||||
|
||||
config SENSORS_AD7314
|
||||
tristate "Analog Devices AD7314 and compatibles"
|
||||
depends on SPI && EXPERIMENTAL
|
||||
help
|
||||
If you say yes here you get support for the Analog Devices
|
||||
AD7314, ADT7301 and ADT7302 temperature sensors.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called ad7314.
|
||||
|
||||
config SENSORS_AD7414
|
||||
tristate "Analog Devices AD7414"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
@@ -303,6 +313,16 @@ config SENSORS_DS1621
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called ds1621.
|
||||
|
||||
config SENSORS_EXYNOS4_TMU
|
||||
tristate "Temperature sensor on Samsung EXYNOS4"
|
||||
depends on EXYNOS4_DEV_TMU
|
||||
help
|
||||
If you say yes here you get support for TMU (Thermal Managment
|
||||
Unit) on SAMSUNG EXYNOS4 series of SoC.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called exynos4-tmu.
|
||||
|
||||
config SENSORS_I5K_AMB
|
||||
tristate "FB-DIMM AMB temperature sensor on Intel 5000 series chipsets"
|
||||
depends on PCI && EXPERIMENTAL
|
||||
@@ -531,6 +551,7 @@ config SENSORS_LM75
|
||||
If you say yes here you get support for one common type of
|
||||
temperature sensor chip, with models including:
|
||||
|
||||
- Analog Devices ADT75
|
||||
- Dallas Semiconductor DS75 and DS1775
|
||||
- Maxim MAX6625 and MAX6626
|
||||
- Microchip MCP980x
|
||||
|
||||
@@ -21,6 +21,7 @@ obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
|
||||
|
||||
obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
|
||||
obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o
|
||||
obj-$(CONFIG_SENSORS_AD7314) += ad7314.o
|
||||
obj-$(CONFIG_SENSORS_AD7414) += ad7414.o
|
||||
obj-$(CONFIG_SENSORS_AD7418) += ad7418.o
|
||||
obj-$(CONFIG_SENSORS_ADCXX) += adcxx.o
|
||||
@@ -47,6 +48,7 @@ obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
|
||||
obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o
|
||||
obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o
|
||||
obj-$(CONFIG_SENSORS_EMC6W201) += emc6w201.o
|
||||
obj-$(CONFIG_SENSORS_EXYNOS4_TMU) += exynos4_tmu.o
|
||||
obj-$(CONFIG_SENSORS_F71805F) += f71805f.o
|
||||
obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o
|
||||
obj-$(CONFIG_SENSORS_F75375S) += f75375s.o
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* AD7314 digital temperature sensor driver for AD7314, ADT7301 and ADT7302
|
||||
*
|
||||
* Copyright 2010 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*
|
||||
* Conversion to hwmon from IIO done by Jonathan Cameron <jic23@cam.ac.uk>
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
|
||||
/*
|
||||
* AD7314 power mode
|
||||
*/
|
||||
#define AD7314_PD 0x2000
|
||||
|
||||
/*
|
||||
* AD7314 temperature masks
|
||||
*/
|
||||
#define AD7314_TEMP_SIGN 0x200
|
||||
#define AD7314_TEMP_MASK 0x7FE0
|
||||
#define AD7314_TEMP_OFFSET 5
|
||||
|
||||
/*
|
||||
* ADT7301 and ADT7302 temperature masks
|
||||
*/
|
||||
#define ADT7301_TEMP_SIGN 0x2000
|
||||
#define ADT7301_TEMP_MASK 0x3FFF
|
||||
|
||||
enum ad7314_variant {
|
||||
adt7301,
|
||||
adt7302,
|
||||
ad7314,
|
||||
};
|
||||
|
||||
struct ad7314_data {
|
||||
struct spi_device *spi_dev;
|
||||
struct device *hwmon_dev;
|
||||
u16 rx ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int ad7314_spi_read(struct ad7314_data *chip, s16 *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = spi_read(chip->spi_dev, (u8 *)&chip->rx, sizeof(chip->rx));
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->spi_dev->dev, "SPI read error\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
*data = be16_to_cpu(chip->rx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ad7314_show_temperature(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ad7314_data *chip = dev_get_drvdata(dev);
|
||||
s16 data;
|
||||
int ret;
|
||||
|
||||
ret = ad7314_spi_read(chip, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
switch (spi_get_device_id(chip->spi_dev)->driver_data) {
|
||||
case ad7314:
|
||||
data = (data & AD7314_TEMP_MASK) >> AD7314_TEMP_OFFSET;
|
||||
data = (data << 6) >> 6;
|
||||
|
||||
return sprintf(buf, "%d\n", 250 * data);
|
||||
case adt7301:
|
||||
case adt7302:
|
||||
/*
|
||||
* Documented as a 13 bit twos complement register
|
||||
* with a sign bit - which is a 14 bit 2's complement
|
||||
* register. 1lsb - 31.25 milli degrees centigrade
|
||||
*/
|
||||
data &= ADT7301_TEMP_MASK;
|
||||
data = (data << 2) >> 2;
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
DIV_ROUND_CLOSEST(data * 3125, 100));
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
|
||||
ad7314_show_temperature, NULL, 0);
|
||||
|
||||
static struct attribute *ad7314_attributes[] = {
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group ad7314_group = {
|
||||
.attrs = ad7314_attributes,
|
||||
};
|
||||
|
||||
static int __devinit ad7314_probe(struct spi_device *spi_dev)
|
||||
{
|
||||
int ret;
|
||||
struct ad7314_data *chip;
|
||||
|
||||
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
||||
if (chip == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
dev_set_drvdata(&spi_dev->dev, chip);
|
||||
|
||||
ret = sysfs_create_group(&spi_dev->dev.kobj, &ad7314_group);
|
||||
if (ret < 0)
|
||||
goto error_free_chip;
|
||||
chip->hwmon_dev = hwmon_device_register(&spi_dev->dev);
|
||||
if (IS_ERR(chip->hwmon_dev)) {
|
||||
ret = PTR_ERR(chip->hwmon_dev);
|
||||
goto error_remove_group;
|
||||
}
|
||||
|
||||
return 0;
|
||||
error_remove_group:
|
||||
sysfs_remove_group(&spi_dev->dev.kobj, &ad7314_group);
|
||||
error_free_chip:
|
||||
kfree(chip);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit ad7314_remove(struct spi_device *spi_dev)
|
||||
{
|
||||
struct ad7314_data *chip = dev_get_drvdata(&spi_dev->dev);
|
||||
|
||||
hwmon_device_unregister(chip->hwmon_dev);
|
||||
sysfs_remove_group(&spi_dev->dev.kobj, &ad7314_group);
|
||||
kfree(chip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7314_id[] = {
|
||||
{ "adt7301", adt7301 },
|
||||
{ "adt7302", adt7302 },
|
||||
{ "ad7314", ad7314 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7314_id);
|
||||
|
||||
static struct spi_driver ad7314_driver = {
|
||||
.driver = {
|
||||
.name = "ad7314",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7314_probe,
|
||||
.remove = __devexit_p(ad7314_remove),
|
||||
.id_table = ad7314_id,
|
||||
};
|
||||
|
||||
static __init int ad7314_init(void)
|
||||
{
|
||||
return spi_register_driver(&ad7314_driver);
|
||||
}
|
||||
module_init(ad7314_init);
|
||||
|
||||
static __exit void ad7314_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&ad7314_driver);
|
||||
}
|
||||
module_exit(ad7314_exit);
|
||||
|
||||
MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7314, ADT7301 and ADT7302 digital"
|
||||
" temperature sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
File diff suppressed because it is too large
Load Diff
+121
-110
@@ -605,7 +605,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
|
||||
standard models */
|
||||
static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = {
|
||||
static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[3][7] = { {
|
||||
SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
|
||||
show_pwm_auto_point_channel,
|
||||
store_pwm_auto_point_channel, 0, 0),
|
||||
@@ -627,7 +627,7 @@ static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = {
|
||||
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),
|
||||
@@ -649,7 +649,7 @@ static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = {
|
||||
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),
|
||||
@@ -671,12 +671,12 @@ static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = {
|
||||
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 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[] = {
|
||||
static struct sensor_device_attribute_2 f71869_auto_pwm_attr[3][8] = { {
|
||||
SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
|
||||
show_pwm_auto_point_channel,
|
||||
store_pwm_auto_point_channel, 0, 0),
|
||||
@@ -701,7 +701,7 @@ static struct sensor_device_attribute_2 f71869_auto_pwm_attr[] = {
|
||||
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),
|
||||
@@ -726,7 +726,7 @@ static struct sensor_device_attribute_2 f71869_auto_pwm_attr[] = {
|
||||
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),
|
||||
@@ -751,7 +751,7 @@ static struct sensor_device_attribute_2 f71869_auto_pwm_attr[] = {
|
||||
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] = { {
|
||||
@@ -928,7 +928,7 @@ static struct sensor_device_attribute_2 f8000_fan_attr[] = {
|
||||
/* PWM attr for the f8000, zones mapped to temp instead of to pwm!
|
||||
Also the register block at offset A0 maps to TEMP1 (so our temp2, as the
|
||||
F8000 starts counting temps at 0), B0 maps the TEMP2 and C0 maps to TEMP0 */
|
||||
static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = {
|
||||
static struct sensor_device_attribute_2 f8000_auto_pwm_attr[3][14] = { {
|
||||
SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
|
||||
show_pwm_auto_point_channel,
|
||||
store_pwm_auto_point_channel, 0, 0),
|
||||
@@ -969,7 +969,7 @@ static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = {
|
||||
show_pwm_auto_point_temp_hyst, NULL, 2, 2),
|
||||
SENSOR_ATTR_2(temp1_auto_point4_temp_hyst, S_IRUGO,
|
||||
show_pwm_auto_point_temp_hyst, NULL, 3, 2),
|
||||
|
||||
}, {
|
||||
SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR,
|
||||
show_pwm_auto_point_channel,
|
||||
store_pwm_auto_point_channel, 0, 1),
|
||||
@@ -1010,7 +1010,7 @@ static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = {
|
||||
show_pwm_auto_point_temp_hyst, NULL, 2, 0),
|
||||
SENSOR_ATTR_2(temp2_auto_point4_temp_hyst, S_IRUGO,
|
||||
show_pwm_auto_point_temp_hyst, NULL, 3, 0),
|
||||
|
||||
}, {
|
||||
SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
|
||||
show_pwm_auto_point_channel,
|
||||
store_pwm_auto_point_channel, 0, 2),
|
||||
@@ -1051,7 +1051,7 @@ static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = {
|
||||
show_pwm_auto_point_temp_hyst, NULL, 2, 1),
|
||||
SENSOR_ATTR_2(temp3_auto_point4_temp_hyst, S_IRUGO,
|
||||
show_pwm_auto_point_temp_hyst, NULL, 3, 1),
|
||||
};
|
||||
} };
|
||||
|
||||
/* Super I/O functions */
|
||||
static inline int superio_inb(int base, int reg)
|
||||
@@ -2154,6 +2154,104 @@ static void f71882fg_remove_sysfs_files(struct platform_device *pdev,
|
||||
device_remove_file(&pdev->dev, &attr[i].dev_attr);
|
||||
}
|
||||
|
||||
static int __devinit f71882fg_create_fan_sysfs_files(
|
||||
struct platform_device *pdev, int idx)
|
||||
{
|
||||
struct f71882fg_data *data = platform_get_drvdata(pdev);
|
||||
int err;
|
||||
|
||||
/* Sanity check the pwm setting */
|
||||
err = 0;
|
||||
switch (data->type) {
|
||||
case f71858fg:
|
||||
if (((data->pwm_enable >> (idx * 2)) & 3) == 3)
|
||||
err = 1;
|
||||
break;
|
||||
case f71862fg:
|
||||
if (((data->pwm_enable >> (idx * 2)) & 1) != 1)
|
||||
err = 1;
|
||||
break;
|
||||
case f8000:
|
||||
if (idx == 2)
|
||||
err = data->pwm_enable & 0x20;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"Invalid (reserved) pwm settings: 0x%02x, "
|
||||
"skipping fan %d\n",
|
||||
(data->pwm_enable >> (idx * 2)) & 3, idx + 1);
|
||||
return 0; /* This is a non fatal condition */
|
||||
}
|
||||
|
||||
err = f71882fg_create_sysfs_files(pdev, &fxxxx_fan_attr[idx][0],
|
||||
ARRAY_SIZE(fxxxx_fan_attr[0]));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (f71882fg_fan_has_beep[data->type]) {
|
||||
err = f71882fg_create_sysfs_files(pdev,
|
||||
&fxxxx_fan_beep_attr[idx],
|
||||
1);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "Fan: %d is in %s mode\n", idx + 1,
|
||||
(data->pwm_enable & (1 << (2 * idx))) ? "duty-cycle" : "RPM");
|
||||
|
||||
/* Check for unsupported auto pwm settings */
|
||||
switch (data->type) {
|
||||
case f71808e:
|
||||
case f71808a:
|
||||
case f71869:
|
||||
case f71869a:
|
||||
case f71889fg:
|
||||
case f71889ed:
|
||||
case f71889a:
|
||||
data->pwm_auto_point_mapping[idx] =
|
||||
f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(idx));
|
||||
if ((data->pwm_auto_point_mapping[idx] & 0x80) ||
|
||||
(data->pwm_auto_point_mapping[idx] & 3) == 0) {
|
||||
dev_warn(&pdev->dev,
|
||||
"Auto pwm controlled by raw digital "
|
||||
"data, disabling pwm auto_point "
|
||||
"sysfs attributes for fan %d\n", idx + 1);
|
||||
return 0; /* This is a non fatal condition */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (data->type) {
|
||||
case f71862fg:
|
||||
err = f71882fg_create_sysfs_files(pdev,
|
||||
&f71862fg_auto_pwm_attr[idx][0],
|
||||
ARRAY_SIZE(f71862fg_auto_pwm_attr[0]));
|
||||
break;
|
||||
case f71808e:
|
||||
case f71869:
|
||||
err = f71882fg_create_sysfs_files(pdev,
|
||||
&f71869_auto_pwm_attr[idx][0],
|
||||
ARRAY_SIZE(f71869_auto_pwm_attr[0]));
|
||||
break;
|
||||
case f8000:
|
||||
err = f71882fg_create_sysfs_files(pdev,
|
||||
&f8000_auto_pwm_attr[idx][0],
|
||||
ARRAY_SIZE(f8000_auto_pwm_attr[0]));
|
||||
break;
|
||||
default:
|
||||
err = f71882fg_create_sysfs_files(pdev,
|
||||
&fxxxx_auto_pwm_attr[idx][0],
|
||||
ARRAY_SIZE(fxxxx_auto_pwm_attr[0]));
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devinit f71882fg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct f71882fg_data *data;
|
||||
@@ -2272,117 +2370,29 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
|
||||
data->pwm_enable =
|
||||
f71882fg_read8(data, F71882FG_REG_PWM_ENABLE);
|
||||
|
||||
/* Sanity check the pwm settings */
|
||||
switch (data->type) {
|
||||
case f71858fg:
|
||||
err = 0;
|
||||
for (i = 0; i < nr_fans; i++)
|
||||
if (((data->pwm_enable >> (i * 2)) & 3) == 3)
|
||||
err = 1;
|
||||
break;
|
||||
case f71862fg:
|
||||
err = (data->pwm_enable & 0x15) != 0x15;
|
||||
break;
|
||||
case f8000:
|
||||
err = data->pwm_enable & 0x20;
|
||||
break;
|
||||
default:
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"Invalid (reserved) pwm settings: 0x%02x\n",
|
||||
(unsigned int)data->pwm_enable);
|
||||
err = -ENODEV;
|
||||
goto exit_unregister_sysfs;
|
||||
}
|
||||
|
||||
err = f71882fg_create_sysfs_files(pdev, &fxxxx_fan_attr[0][0],
|
||||
ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans);
|
||||
if (err)
|
||||
goto exit_unregister_sysfs;
|
||||
|
||||
if (f71882fg_fan_has_beep[data->type]) {
|
||||
err = f71882fg_create_sysfs_files(pdev,
|
||||
fxxxx_fan_beep_attr, nr_fans);
|
||||
for (i = 0; i < nr_fans; i++) {
|
||||
err = f71882fg_create_fan_sysfs_files(pdev, i);
|
||||
if (err)
|
||||
goto exit_unregister_sysfs;
|
||||
}
|
||||
|
||||
switch (data->type) {
|
||||
case f71808e:
|
||||
case f71808a:
|
||||
case f71869:
|
||||
case f71869a:
|
||||
case f71889fg:
|
||||
case f71889ed:
|
||||
case f71889a:
|
||||
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;
|
||||
}
|
||||
|
||||
/* Some types have 1 extra fan with limited functionality */
|
||||
switch (data->type) {
|
||||
case f71808a:
|
||||
err = f71882fg_create_sysfs_files(pdev,
|
||||
&fxxxx_auto_pwm_attr[0][0],
|
||||
ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
|
||||
if (err)
|
||||
goto exit_unregister_sysfs;
|
||||
err = f71882fg_create_sysfs_files(pdev,
|
||||
f71808a_fan3_attr,
|
||||
ARRAY_SIZE(f71808a_fan3_attr));
|
||||
break;
|
||||
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,
|
||||
ARRAY_SIZE(f8000_fan_attr));
|
||||
if (err)
|
||||
goto exit_unregister_sysfs;
|
||||
err = f71882fg_create_sysfs_files(pdev,
|
||||
f8000_auto_pwm_attr,
|
||||
ARRAY_SIZE(f8000_auto_pwm_attr));
|
||||
break;
|
||||
default:
|
||||
err = f71882fg_create_sysfs_files(pdev,
|
||||
&fxxxx_auto_pwm_attr[0][0],
|
||||
ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
|
||||
break;
|
||||
}
|
||||
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)) ?
|
||||
"duty-cycle" : "RPM");
|
||||
}
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&pdev->dev);
|
||||
@@ -2476,22 +2486,23 @@ static int f71882fg_remove(struct platform_device *pdev)
|
||||
break;
|
||||
case f71862fg:
|
||||
f71882fg_remove_sysfs_files(pdev,
|
||||
f71862fg_auto_pwm_attr,
|
||||
ARRAY_SIZE(f71862fg_auto_pwm_attr));
|
||||
&f71862fg_auto_pwm_attr[0][0],
|
||||
ARRAY_SIZE(f71862fg_auto_pwm_attr[0]) *
|
||||
nr_fans);
|
||||
break;
|
||||
case f71808e:
|
||||
case f71869:
|
||||
f71882fg_remove_sysfs_files(pdev,
|
||||
f71869_auto_pwm_attr,
|
||||
ARRAY_SIZE(f71869_auto_pwm_attr));
|
||||
&f71869_auto_pwm_attr[0][0],
|
||||
ARRAY_SIZE(f71869_auto_pwm_attr[0]) * nr_fans);
|
||||
break;
|
||||
case f8000:
|
||||
f71882fg_remove_sysfs_files(pdev,
|
||||
f8000_fan_attr,
|
||||
ARRAY_SIZE(f8000_fan_attr));
|
||||
f71882fg_remove_sysfs_files(pdev,
|
||||
f8000_auto_pwm_attr,
|
||||
ARRAY_SIZE(f8000_auto_pwm_attr));
|
||||
&f8000_auto_pwm_attr[0][0],
|
||||
ARRAY_SIZE(f8000_auto_pwm_attr[0]) * nr_fans);
|
||||
break;
|
||||
default:
|
||||
f71882fg_remove_sysfs_files(pdev,
|
||||
|
||||
+26
-13
@@ -35,6 +35,7 @@
|
||||
*/
|
||||
|
||||
enum lm75_type { /* keep sorted in alphabetical order */
|
||||
adt75,
|
||||
ds1775,
|
||||
ds75,
|
||||
lm75,
|
||||
@@ -213,6 +214,7 @@ static int lm75_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id lm75_ids[] = {
|
||||
{ "adt75", adt75, },
|
||||
{ "ds1775", ds1775, },
|
||||
{ "ds75", ds75, },
|
||||
{ "lm75", lm75, },
|
||||
@@ -247,19 +249,30 @@ static int lm75_detect(struct i2c_client *new_client,
|
||||
I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
/* Now, we do the remaining detection. There is no identification-
|
||||
dedicated register so we have to rely on several tricks:
|
||||
unused bits, registers cycling over 8-address boundaries,
|
||||
addresses 0x04-0x07 returning the last read value.
|
||||
The cycling+unused addresses combination is not tested,
|
||||
since it would significantly slow the detection down and would
|
||||
hardly add any value.
|
||||
|
||||
The National Semiconductor LM75A is different than earlier
|
||||
LM75s. It has an ID byte of 0xaX (where X is the chip
|
||||
revision, with 1 being the only revision in existence) in
|
||||
register 7, and unused registers return 0xff rather than the
|
||||
last read value. */
|
||||
/*
|
||||
* Now, we do the remaining detection. There is no identification-
|
||||
* dedicated register so we have to rely on several tricks:
|
||||
* unused bits, registers cycling over 8-address boundaries,
|
||||
* addresses 0x04-0x07 returning the last read value.
|
||||
* The cycling+unused addresses combination is not tested,
|
||||
* since it would significantly slow the detection down and would
|
||||
* hardly add any value.
|
||||
*
|
||||
* The National Semiconductor LM75A is different than earlier
|
||||
* LM75s. It has an ID byte of 0xaX (where X is the chip
|
||||
* revision, with 1 being the only revision in existence) in
|
||||
* register 7, and unused registers return 0xff rather than the
|
||||
* last read value.
|
||||
*
|
||||
* Note that this function only detects the original National
|
||||
* Semiconductor LM75 and the LM75A. Clones from other vendors
|
||||
* aren't detected, on purpose, because they are typically never
|
||||
* found on PC hardware. They are found on embedded designs where
|
||||
* they can be instantiated explicitly so detection is not needed.
|
||||
* The absence of identification registers on all these clones
|
||||
* would make their exhaustive detection very difficult and weak,
|
||||
* and odds are that the driver would bind to unsupported devices.
|
||||
*/
|
||||
|
||||
/* Unused bits */
|
||||
conf = i2c_smbus_read_byte_data(new_client, 1);
|
||||
|
||||
@@ -20,17 +20,18 @@ config SENSORS_PMBUS
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for generic
|
||||
PMBus devices, including but not limited to ADP4000, BMR450, BMR451,
|
||||
BMR453, BMR454, LTC2978, NCP4200, and NCP4208.
|
||||
BMR453, BMR454, NCP4200, and NCP4208.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called pmbus.
|
||||
|
||||
config SENSORS_ADM1275
|
||||
tristate "Analog Devices ADM1275"
|
||||
tristate "Analog Devices ADM1275 and compatibles"
|
||||
default n
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for Analog
|
||||
Devices ADM1275 Hot-Swap Controller and Digital Power Monitor.
|
||||
Devices ADM1275 and ADM1276 Hot-Swap Controller and Digital Power
|
||||
Monitor.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called adm1275.
|
||||
@@ -45,6 +46,16 @@ config SENSORS_LM25066
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called lm25066.
|
||||
|
||||
config SENSORS_LTC2978
|
||||
tristate "Linear Technologies LTC2978 and LTC3880"
|
||||
default n
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for Linear
|
||||
Technology LTC2978 and LTC3880.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called ltc2978.
|
||||
|
||||
config SENSORS_MAX16064
|
||||
tristate "Maxim MAX16064"
|
||||
default n
|
||||
@@ -97,4 +108,15 @@ config SENSORS_UCD9200
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called ucd9200.
|
||||
|
||||
config SENSORS_ZL6100
|
||||
tristate "Intersil ZL6100 and compatibles"
|
||||
default n
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for Intersil
|
||||
ZL2004, ZL2006, ZL2008, ZL2105, ZL2106, ZL6100, and ZL6105 Digital
|
||||
DC/DC Controllers.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called zl6100.
|
||||
|
||||
endif # PMBUS
|
||||
|
||||
@@ -6,8 +6,10 @@ obj-$(CONFIG_PMBUS) += pmbus_core.o
|
||||
obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o
|
||||
obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
|
||||
obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
|
||||
obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
|
||||
obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
|
||||
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
|
||||
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
|
||||
obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
|
||||
obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
|
||||
obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o
|
||||
|
||||
+141
-18
@@ -23,6 +23,8 @@
|
||||
#include <linux/i2c.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
enum chips { adm1275, adm1276 };
|
||||
|
||||
#define ADM1275_PEAK_IOUT 0xd0
|
||||
#define ADM1275_PEAK_VIN 0xd1
|
||||
#define ADM1275_PEAK_VOUT 0xd2
|
||||
@@ -31,14 +33,47 @@
|
||||
#define ADM1275_VIN_VOUT_SELECT (1 << 6)
|
||||
#define ADM1275_VRANGE (1 << 5)
|
||||
|
||||
#define ADM1275_IOUT_WARN2_LIMIT 0xd7
|
||||
#define ADM1275_DEVICE_CONFIG 0xd8
|
||||
|
||||
#define ADM1275_IOUT_WARN2_SELECT (1 << 4)
|
||||
|
||||
#define ADM1276_PEAK_PIN 0xda
|
||||
|
||||
#define ADM1275_MFR_STATUS_IOUT_WARN2 (1 << 0)
|
||||
|
||||
struct adm1275_data {
|
||||
int id;
|
||||
bool have_oc_fault;
|
||||
struct pmbus_driver_info info;
|
||||
};
|
||||
|
||||
#define to_adm1275_data(x) container_of(x, struct adm1275_data, info)
|
||||
|
||||
static int adm1275_read_word_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
int ret;
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
const struct adm1275_data *data = to_adm1275_data(info);
|
||||
int ret = 0;
|
||||
|
||||
if (page)
|
||||
return -EINVAL;
|
||||
return -ENXIO;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_IOUT_UC_FAULT_LIMIT:
|
||||
if (data->have_oc_fault) {
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
}
|
||||
ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT);
|
||||
break;
|
||||
case PMBUS_IOUT_OC_FAULT_LIMIT:
|
||||
if (!data->have_oc_fault) {
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
}
|
||||
ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_IOUT_MAX:
|
||||
ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_IOUT);
|
||||
break;
|
||||
@@ -48,10 +83,20 @@ static int adm1275_read_word_data(struct i2c_client *client, int page, int reg)
|
||||
case PMBUS_VIRT_READ_VIN_MAX:
|
||||
ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VIN);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_PIN_MAX:
|
||||
if (data->id != adm1276) {
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
}
|
||||
ret = pmbus_read_word_data(client, 0, ADM1276_PEAK_PIN);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_IOUT_HISTORY:
|
||||
case PMBUS_VIRT_RESET_VOUT_HISTORY:
|
||||
case PMBUS_VIRT_RESET_VIN_HISTORY:
|
||||
ret = 0;
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_PIN_HISTORY:
|
||||
if (data->id != adm1276)
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
@@ -66,9 +111,14 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg,
|
||||
int ret;
|
||||
|
||||
if (page)
|
||||
return -EINVAL;
|
||||
return -ENXIO;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_IOUT_UC_FAULT_LIMIT:
|
||||
case PMBUS_IOUT_OC_FAULT_LIMIT:
|
||||
ret = pmbus_write_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT,
|
||||
word);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_IOUT_HISTORY:
|
||||
ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_IOUT, 0);
|
||||
break;
|
||||
@@ -78,6 +128,41 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg,
|
||||
case PMBUS_VIRT_RESET_VIN_HISTORY:
|
||||
ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VIN, 0);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_PIN_HISTORY:
|
||||
ret = pmbus_write_word_data(client, 0, ADM1276_PEAK_PIN, 0);
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
const struct adm1275_data *data = to_adm1275_data(info);
|
||||
int mfr_status, ret;
|
||||
|
||||
if (page > 0)
|
||||
return -ENXIO;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_STATUS_IOUT:
|
||||
ret = pmbus_read_byte_data(client, page, PMBUS_STATUS_IOUT);
|
||||
if (ret < 0)
|
||||
break;
|
||||
mfr_status = pmbus_read_byte_data(client, page,
|
||||
PMBUS_STATUS_MFR_SPECIFIC);
|
||||
if (mfr_status < 0) {
|
||||
ret = mfr_status;
|
||||
break;
|
||||
}
|
||||
if (mfr_status & ADM1275_MFR_STATUS_IOUT_WARN2) {
|
||||
ret |= data->have_oc_fault ?
|
||||
PB_IOUT_OC_FAULT : PB_IOUT_UC_FAULT;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
@@ -88,16 +173,17 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg,
|
||||
static int adm1275_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int config;
|
||||
int config, device_config;
|
||||
int ret;
|
||||
struct pmbus_driver_info *info;
|
||||
struct adm1275_data *data;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_BYTE_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
info = kzalloc(sizeof(struct pmbus_driver_info), GFP_KERNEL);
|
||||
if (!info)
|
||||
data = kzalloc(sizeof(struct adm1275_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
config = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG);
|
||||
@@ -106,6 +192,15 @@ static int adm1275_probe(struct i2c_client *client,
|
||||
goto err_mem;
|
||||
}
|
||||
|
||||
device_config = i2c_smbus_read_byte_data(client, ADM1275_DEVICE_CONFIG);
|
||||
if (device_config < 0) {
|
||||
ret = device_config;
|
||||
goto err_mem;
|
||||
}
|
||||
|
||||
data->id = id->driver_data;
|
||||
info = &data->info;
|
||||
|
||||
info->pages = 1;
|
||||
info->format[PSC_VOLTAGE_IN] = direct;
|
||||
info->format[PSC_VOLTAGE_OUT] = direct;
|
||||
@@ -116,6 +211,7 @@ static int adm1275_probe(struct i2c_client *client,
|
||||
info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
|
||||
|
||||
info->read_word_data = adm1275_read_word_data;
|
||||
info->read_byte_data = adm1275_read_byte_data;
|
||||
info->write_word_data = adm1275_write_word_data;
|
||||
|
||||
if (config & ADM1275_VRANGE) {
|
||||
@@ -134,10 +230,36 @@ static int adm1275_probe(struct i2c_client *client,
|
||||
info->R[PSC_VOLTAGE_OUT] = -1;
|
||||
}
|
||||
|
||||
if (config & ADM1275_VIN_VOUT_SELECT)
|
||||
info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
|
||||
else
|
||||
info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT;
|
||||
if (device_config & ADM1275_IOUT_WARN2_SELECT)
|
||||
data->have_oc_fault = true;
|
||||
|
||||
switch (id->driver_data) {
|
||||
case adm1275:
|
||||
if (config & ADM1275_VIN_VOUT_SELECT)
|
||||
info->func[0] |=
|
||||
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
|
||||
else
|
||||
info->func[0] |=
|
||||
PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT;
|
||||
break;
|
||||
case adm1276:
|
||||
info->format[PSC_POWER] = direct;
|
||||
info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_PIN
|
||||
| PMBUS_HAVE_STATUS_INPUT;
|
||||
if (config & ADM1275_VIN_VOUT_SELECT)
|
||||
info->func[0] |=
|
||||
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
|
||||
if (config & ADM1275_VRANGE) {
|
||||
info->m[PSC_POWER] = 6043;
|
||||
info->b[PSC_POWER] = 0;
|
||||
info->R[PSC_POWER] = -2;
|
||||
} else {
|
||||
info->m[PSC_POWER] = 2115;
|
||||
info->b[PSC_POWER] = 0;
|
||||
info->R[PSC_POWER] = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ret = pmbus_do_probe(client, id, info);
|
||||
if (ret)
|
||||
@@ -145,22 +267,23 @@ static int adm1275_probe(struct i2c_client *client,
|
||||
return 0;
|
||||
|
||||
err_mem:
|
||||
kfree(info);
|
||||
kfree(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adm1275_remove(struct i2c_client *client)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
int ret;
|
||||
const struct adm1275_data *data = to_adm1275_data(info);
|
||||
|
||||
ret = pmbus_do_remove(client);
|
||||
kfree(info);
|
||||
return ret;
|
||||
pmbus_do_remove(client);
|
||||
kfree(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adm1275_id[] = {
|
||||
{"adm1275", 0},
|
||||
{ "adm1275", adm1275 },
|
||||
{ "adm1276", adm1276 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adm1275_id);
|
||||
@@ -185,7 +308,7 @@ static void __exit adm1275_exit(void)
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275");
|
||||
MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275 and compatibles");
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(adm1275_init);
|
||||
module_exit(adm1275_exit);
|
||||
|
||||
@@ -57,7 +57,7 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg)
|
||||
int ret;
|
||||
|
||||
if (page > 1)
|
||||
return -EINVAL;
|
||||
return -ENXIO;
|
||||
|
||||
/* Map READ_VAUX into READ_VOUT register on page 1 */
|
||||
if (page == 1) {
|
||||
@@ -85,7 +85,7 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg)
|
||||
break;
|
||||
default:
|
||||
/* No other valid registers on page 1 */
|
||||
ret = -EINVAL;
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
}
|
||||
goto done;
|
||||
@@ -138,7 +138,7 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg,
|
||||
int ret;
|
||||
|
||||
if (page > 1)
|
||||
return -EINVAL;
|
||||
return -ENXIO;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_IIN_OC_WARN_LIMIT:
|
||||
@@ -164,10 +164,10 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg,
|
||||
static int lm25066_write_byte(struct i2c_client *client, int page, u8 value)
|
||||
{
|
||||
if (page > 1)
|
||||
return -EINVAL;
|
||||
return -ENXIO;
|
||||
|
||||
if (page == 0)
|
||||
return pmbus_write_byte(client, 0, value);
|
||||
if (page <= 0)
|
||||
return pmbus_write_byte(client, page, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -309,11 +309,10 @@ static int lm25066_remove(struct i2c_client *client)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
const struct lm25066_data *data = to_lm25066_data(info);
|
||||
int ret;
|
||||
|
||||
ret = pmbus_do_remove(client);
|
||||
pmbus_do_remove(client);
|
||||
kfree(data);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id lm25066_id[] = {
|
||||
|
||||
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* Hardware monitoring driver for LTC2978 and LTC3880
|
||||
*
|
||||
* Copyright (c) 2011 Ericsson AB.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
enum chips { ltc2978, ltc3880 };
|
||||
|
||||
/* LTC2978 and LTC3880 */
|
||||
#define LTC2978_MFR_VOUT_PEAK 0xdd
|
||||
#define LTC2978_MFR_VIN_PEAK 0xde
|
||||
#define LTC2978_MFR_TEMPERATURE_PEAK 0xdf
|
||||
#define LTC2978_MFR_SPECIAL_ID 0xe7
|
||||
|
||||
/* LTC2978 only */
|
||||
#define LTC2978_MFR_VOUT_MIN 0xfb
|
||||
#define LTC2978_MFR_VIN_MIN 0xfc
|
||||
#define LTC2978_MFR_TEMPERATURE_MIN 0xfd
|
||||
|
||||
/* LTC3880 only */
|
||||
#define LTC3880_MFR_IOUT_PEAK 0xd7
|
||||
#define LTC3880_MFR_CLEAR_PEAKS 0xe3
|
||||
#define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4
|
||||
|
||||
#define LTC2978_ID_REV1 0x0121
|
||||
#define LTC2978_ID_REV2 0x0122
|
||||
#define LTC3880_ID 0x4000
|
||||
#define LTC3880_ID_MASK 0xff00
|
||||
|
||||
/*
|
||||
* LTC2978 clears peak data whenever the CLEAR_FAULTS command is executed, which
|
||||
* happens pretty much each time chip data is updated. Raw peak data therefore
|
||||
* does not provide much value. To be able to provide useful peak data, keep an
|
||||
* internal cache of measured peak data, which is only cleared if an explicit
|
||||
* "clear peak" command is executed for the sensor in question.
|
||||
*/
|
||||
struct ltc2978_data {
|
||||
enum chips id;
|
||||
int vin_min, vin_max;
|
||||
int temp_min, temp_max;
|
||||
int vout_min[8], vout_max[8];
|
||||
int iout_max[2];
|
||||
int temp2_max[2];
|
||||
struct pmbus_driver_info info;
|
||||
};
|
||||
|
||||
#define to_ltc2978_data(x) container_of(x, struct ltc2978_data, info)
|
||||
|
||||
static inline int lin11_to_val(int data)
|
||||
{
|
||||
s16 e = ((s16)data) >> 11;
|
||||
s32 m = (((s16)(data << 5)) >> 5);
|
||||
|
||||
/*
|
||||
* mantissa is 10 bit + sign, exponent adds up to 15 bit.
|
||||
* Add 6 bit to exponent for maximum accuracy (10 + 15 + 6 = 31).
|
||||
*/
|
||||
e += 6;
|
||||
return (e < 0 ? m >> -e : m << e);
|
||||
}
|
||||
|
||||
static int ltc2978_read_word_data_common(struct i2c_client *client, int page,
|
||||
int reg)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct ltc2978_data *data = to_ltc2978_data(info);
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_READ_VIN_MAX:
|
||||
ret = pmbus_read_word_data(client, page, LTC2978_MFR_VIN_PEAK);
|
||||
if (ret >= 0) {
|
||||
if (lin11_to_val(ret) > lin11_to_val(data->vin_max))
|
||||
data->vin_max = ret;
|
||||
ret = data->vin_max;
|
||||
}
|
||||
break;
|
||||
case PMBUS_VIRT_READ_VOUT_MAX:
|
||||
ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_PEAK);
|
||||
if (ret >= 0) {
|
||||
/*
|
||||
* VOUT is 16 bit unsigned with fixed exponent,
|
||||
* so we can compare it directly
|
||||
*/
|
||||
if (ret > data->vout_max[page])
|
||||
data->vout_max[page] = ret;
|
||||
ret = data->vout_max[page];
|
||||
}
|
||||
break;
|
||||
case PMBUS_VIRT_READ_TEMP_MAX:
|
||||
ret = pmbus_read_word_data(client, page,
|
||||
LTC2978_MFR_TEMPERATURE_PEAK);
|
||||
if (ret >= 0) {
|
||||
if (lin11_to_val(ret) > lin11_to_val(data->temp_max))
|
||||
data->temp_max = ret;
|
||||
ret = data->temp_max;
|
||||
}
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_VOUT_HISTORY:
|
||||
case PMBUS_VIRT_RESET_VIN_HISTORY:
|
||||
case PMBUS_VIRT_RESET_TEMP_HISTORY:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct ltc2978_data *data = to_ltc2978_data(info);
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_READ_VIN_MIN:
|
||||
ret = pmbus_read_word_data(client, page, LTC2978_MFR_VIN_MIN);
|
||||
if (ret >= 0) {
|
||||
if (lin11_to_val(ret) < lin11_to_val(data->vin_min))
|
||||
data->vin_min = ret;
|
||||
ret = data->vin_min;
|
||||
}
|
||||
break;
|
||||
case PMBUS_VIRT_READ_VOUT_MIN:
|
||||
ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_MIN);
|
||||
if (ret >= 0) {
|
||||
/*
|
||||
* VOUT_MIN is known to not be supported on some lots
|
||||
* of LTC2978 revision 1, and will return the maximum
|
||||
* possible voltage if read. If VOUT_MAX is valid and
|
||||
* lower than the reading of VOUT_MIN, use it instead.
|
||||
*/
|
||||
if (data->vout_max[page] && ret > data->vout_max[page])
|
||||
ret = data->vout_max[page];
|
||||
if (ret < data->vout_min[page])
|
||||
data->vout_min[page] = ret;
|
||||
ret = data->vout_min[page];
|
||||
}
|
||||
break;
|
||||
case PMBUS_VIRT_READ_TEMP_MIN:
|
||||
ret = pmbus_read_word_data(client, page,
|
||||
LTC2978_MFR_TEMPERATURE_MIN);
|
||||
if (ret >= 0) {
|
||||
if (lin11_to_val(ret)
|
||||
< lin11_to_val(data->temp_min))
|
||||
data->temp_min = ret;
|
||||
ret = data->temp_min;
|
||||
}
|
||||
break;
|
||||
case PMBUS_VIRT_READ_IOUT_MAX:
|
||||
case PMBUS_VIRT_RESET_IOUT_HISTORY:
|
||||
case PMBUS_VIRT_READ_TEMP2_MAX:
|
||||
case PMBUS_VIRT_RESET_TEMP2_HISTORY:
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
default:
|
||||
ret = ltc2978_read_word_data_common(client, page, reg);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc3880_read_word_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct ltc2978_data *data = to_ltc2978_data(info);
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_READ_IOUT_MAX:
|
||||
ret = pmbus_read_word_data(client, page, LTC3880_MFR_IOUT_PEAK);
|
||||
if (ret >= 0) {
|
||||
if (lin11_to_val(ret)
|
||||
> lin11_to_val(data->iout_max[page]))
|
||||
data->iout_max[page] = ret;
|
||||
ret = data->iout_max[page];
|
||||
}
|
||||
break;
|
||||
case PMBUS_VIRT_READ_TEMP2_MAX:
|
||||
ret = pmbus_read_word_data(client, page,
|
||||
LTC3880_MFR_TEMPERATURE2_PEAK);
|
||||
if (ret >= 0) {
|
||||
if (lin11_to_val(ret)
|
||||
> lin11_to_val(data->temp2_max[page]))
|
||||
data->temp2_max[page] = ret;
|
||||
ret = data->temp2_max[page];
|
||||
}
|
||||
break;
|
||||
case PMBUS_VIRT_READ_VIN_MIN:
|
||||
case PMBUS_VIRT_READ_VOUT_MIN:
|
||||
case PMBUS_VIRT_READ_TEMP_MIN:
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_IOUT_HISTORY:
|
||||
case PMBUS_VIRT_RESET_TEMP2_HISTORY:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = ltc2978_read_word_data_common(client, page, reg);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc2978_clear_peaks(struct i2c_client *client, int page,
|
||||
enum chips id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (id == ltc2978)
|
||||
ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS);
|
||||
else
|
||||
ret = pmbus_write_byte(client, 0, LTC3880_MFR_CLEAR_PEAKS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc2978_write_word_data(struct i2c_client *client, int page,
|
||||
int reg, u16 word)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct ltc2978_data *data = to_ltc2978_data(info);
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_RESET_IOUT_HISTORY:
|
||||
data->iout_max[page] = 0x7fff;
|
||||
ret = ltc2978_clear_peaks(client, page, data->id);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_TEMP2_HISTORY:
|
||||
data->temp2_max[page] = 0x7fff;
|
||||
ret = ltc2978_clear_peaks(client, page, data->id);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_VOUT_HISTORY:
|
||||
data->vout_min[page] = 0xffff;
|
||||
data->vout_max[page] = 0;
|
||||
ret = ltc2978_clear_peaks(client, page, data->id);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_VIN_HISTORY:
|
||||
data->vin_min = 0x7bff;
|
||||
data->vin_max = 0;
|
||||
ret = ltc2978_clear_peaks(client, page, data->id);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_TEMP_HISTORY:
|
||||
data->temp_min = 0x7bff;
|
||||
data->temp_max = 0x7fff;
|
||||
ret = ltc2978_clear_peaks(client, page, data->id);
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ltc2978_id[] = {
|
||||
{"ltc2978", ltc2978},
|
||||
{"ltc3880", ltc3880},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ltc2978_id);
|
||||
|
||||
static int ltc2978_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int chip_id, ret, i;
|
||||
struct ltc2978_data *data;
|
||||
struct pmbus_driver_info *info;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_WORD_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
data = kzalloc(sizeof(struct ltc2978_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
chip_id = i2c_smbus_read_word_data(client, LTC2978_MFR_SPECIAL_ID);
|
||||
if (chip_id < 0) {
|
||||
ret = chip_id;
|
||||
goto err_mem;
|
||||
}
|
||||
|
||||
if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2) {
|
||||
data->id = ltc2978;
|
||||
} else if ((chip_id & LTC3880_ID_MASK) == LTC3880_ID) {
|
||||
data->id = ltc3880;
|
||||
} else {
|
||||
dev_err(&client->dev, "Unsupported chip ID 0x%x\n", chip_id);
|
||||
ret = -ENODEV;
|
||||
goto err_mem;
|
||||
}
|
||||
if (data->id != id->driver_data)
|
||||
dev_warn(&client->dev,
|
||||
"Device mismatch: Configured %s, detected %s\n",
|
||||
id->name,
|
||||
ltc2978_id[data->id].name);
|
||||
|
||||
info = &data->info;
|
||||
info->write_word_data = ltc2978_write_word_data;
|
||||
|
||||
data->vout_min[0] = 0xffff;
|
||||
data->vin_min = 0x7bff;
|
||||
data->temp_min = 0x7bff;
|
||||
data->temp_max = 0x7fff;
|
||||
|
||||
switch (id->driver_data) {
|
||||
case ltc2978:
|
||||
info->read_word_data = ltc2978_read_word_data;
|
||||
info->pages = 8;
|
||||
info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
|
||||
| PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
|
||||
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
|
||||
for (i = 1; i < 8; i++) {
|
||||
info->func[i] = PMBUS_HAVE_VOUT
|
||||
| PMBUS_HAVE_STATUS_VOUT;
|
||||
data->vout_min[i] = 0xffff;
|
||||
}
|
||||
break;
|
||||
case ltc3880:
|
||||
info->read_word_data = ltc3880_read_word_data;
|
||||
info->pages = 2;
|
||||
info->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_POUT | PMBUS_HAVE_TEMP
|
||||
| PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
|
||||
info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
|
||||
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
|
||||
| PMBUS_HAVE_POUT
|
||||
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
|
||||
data->vout_min[1] = 0xffff;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODEV;
|
||||
goto err_mem;
|
||||
}
|
||||
|
||||
ret = pmbus_do_probe(client, id, info);
|
||||
if (ret)
|
||||
goto err_mem;
|
||||
return 0;
|
||||
|
||||
err_mem:
|
||||
kfree(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc2978_remove(struct i2c_client *client)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
const struct ltc2978_data *data = to_ltc2978_data(info);
|
||||
|
||||
pmbus_do_remove(client);
|
||||
kfree(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver ltc2978_driver = {
|
||||
.driver = {
|
||||
.name = "ltc2978",
|
||||
},
|
||||
.probe = ltc2978_probe,
|
||||
.remove = ltc2978_remove,
|
||||
.id_table = ltc2978_id,
|
||||
};
|
||||
|
||||
static int __init ltc2978_init(void)
|
||||
{
|
||||
return i2c_add_driver(<c2978_driver);
|
||||
}
|
||||
|
||||
static void __exit ltc2978_exit(void)
|
||||
{
|
||||
i2c_del_driver(<c2978_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for LTC2978 and LTC3880");
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(ltc2978_init);
|
||||
module_exit(ltc2978_exit);
|
||||
@@ -105,7 +105,8 @@ static int max16064_probe(struct i2c_client *client,
|
||||
|
||||
static int max16064_remove(struct i2c_client *client)
|
||||
{
|
||||
return pmbus_do_remove(client);
|
||||
pmbus_do_remove(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max16064_id[] = {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user