hwmon updates for v5.14
New drivers: - Delta DPS920AB - Flex PIM4006, PIM4328 and PIM4820 - MPS MP2888 - Sensirion SHT4X Added chip support to existing drivers: - Flex BMR310, BMR456, BMR457, BMR458, BMR480, BMR490, BMR491, and BMR492 - TI TMP1075 - Renesas ZLS1003, ZLS4009 and ZL8802 Other; - Dropped explicit ACPI support for MAX31722 and LM70; the APIC IDs in those drivers do not exist. - Support set_trips() callback into thermal subsystem - Minor fixes and improvements in various drivers -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEiHPvMQj9QTOCiqgVyx8mb86fmYEFAmDZyoMACgkQyx8mb86f mYGMow/8D8bfef6RhmfLCviVzrdUHscHcrNzKzL2S1afdt1lH2UaBO4IKsT8eac5 jgi2AH9xEhak75GZufdL1zX2lXbIOxD24Ud0ax0epFvm82LY6Jn0CiXx1pA/RGF1 AY0WgQ0UrYV0IfjjpyyHiCrTkhDYfOU9hUmBv9j4+T4pTne1Jv/ERHQfomdIaYkm PSg8/urhv7Rga8TUOw7GZY0Bch+JZiq2erQ4dNEG2ni3ZLAKtM5UyQoCNMOhhkT4 3S2b+3gBh32z9r+OixaCvSxOVKOEWRGcAnwsg6xdQDUzP7KVzByznDs3RJHlTBFm Mv08shTDvJ7GXtXwPa+9astKjZNGoteYYBExSXX+xNKRE6O5jog/PZihZHIT1W72 t81Vfir1C4yS9A8Lp4J8fiTXUEEUHu65NltWynHQZMG+EhfVyypz0WGd94nok4/X wQiZ/c3ZsHK0qBJHcCk3lmAzjm0DoJHexmqkMkU5dKSb+clawNtEfucCPZUgwGZZ sxv2vT05G2dSs548exy+AP/63m31y+MR2NZHHAu+9o4NzsugruDZ9g31PJb7U+9v aTXC9pWTucqPL3cgmuJt9weWaG2quogdxH8s+Hn7NcWWZxPMelfENsztbX940Bu6 86egpZ15YEvHMIO3AGxjQpfc22bleeTjxLWUPfSQ26fGoVKTUrY= =saPU -----END PGP SIGNATURE----- Merge tag 'hwmon-for-v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging Pull hwmon updates from Guenter Roeck: "New drivers: - Delta DPS920AB - Flex PIM4006, PIM4328 and PIM4820 - MPS MP2888 - Sensirion SHT4X Added chip support to existing drivers: - Flex BMR310, BMR456, BMR457, BMR458, BMR480, BMR490, BMR491, and BMR492 - TI TMP1075 - Renesas ZLS1003, ZLS4009 and ZL8802 Other: - Dropped explicit ACPI support for MAX31722 and LM70; the APIC IDs in those drivers do not exist. - Support set_trips() callback into thermal subsystem - Minor fixes and improvements in various drivers" * tag 'hwmon-for-v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (49 commits) hwmon: Support set_trips() of thermal device ops hwmon: (lm90) Prevent integer underflows of temperature calculations hwmon: (lm90) Disable interrupt on suspend hwmon: (lm90) Unmask hardware interrupt hwmon: (lm90) Use hwmon_notify_event() hwmon: (lm90) Don't override interrupt trigger type hwmon: (pmbus/dps920ab) Delete some dead code hwmon: (ntc_thermistor) Drop unused headers. MAINTAINERS: Add Delta DPS920AB PSU driver dt-bindings: trivial-devices: Add Delta DPS920AB hwmon: (pmbus) Add driver for Delta DPS-920AB PSU hwmon: (pmbus/pim4328) Add documentation for the pim4328 PMBus driver hwmon: (pmbus/pim4328) Add PMBus driver for PIM4006, PIM4328 and PIM4820 hwmon: (pmbus) Allow phase function even if it's not on page hwmon: (pmbus) Add support for reading direct mode coefficients hwmon: (pmbus) Add new pmbus flag NO_WRITE_PROTECT docs: hwmon: adm1177.rst: avoid using ReSt :doc:`foo` markup hwmon: (pmbus_core) Check adapter PEC support hwmon: (ina3221) use CVRF only for single-shot conversion hwmon: (max31790) Detect and report zero fan speed ...
This commit is contained in:
commit
2a5c61843e
|
@ -30,6 +30,7 @@ properties:
|
||||||
- st,stds75
|
- st,stds75
|
||||||
- st,stlm75
|
- st,stlm75
|
||||||
- microchip,tcn75
|
- microchip,tcn75
|
||||||
|
- ti,tmp1075
|
||||||
- ti,tmp100
|
- ti,tmp100
|
||||||
- ti,tmp101
|
- ti,tmp101
|
||||||
- ti,tmp105
|
- ti,tmp105
|
||||||
|
|
|
@ -73,6 +73,8 @@ properties:
|
||||||
- dallas,ds4510
|
- dallas,ds4510
|
||||||
# Digital Thermometer and Thermostat
|
# Digital Thermometer and Thermostat
|
||||||
- dallas,ds75
|
- dallas,ds75
|
||||||
|
# Delta Electronics DPS920AB 920W 54V Power Supply
|
||||||
|
- delta,dps920ab
|
||||||
# 1/4 Brick DC/DC Regulated Power Module
|
# 1/4 Brick DC/DC Regulated Power Module
|
||||||
- delta,q54sj108a2
|
- delta,q54sj108a2
|
||||||
# Devantech SRF02 ultrasonic ranger in I2C mode
|
# Devantech SRF02 ultrasonic ranger in I2C mode
|
||||||
|
@ -103,6 +105,8 @@ properties:
|
||||||
- fsl,mpl3115
|
- fsl,mpl3115
|
||||||
# MPR121: Proximity Capacitive Touch Sensor Controller
|
# MPR121: Proximity Capacitive Touch Sensor Controller
|
||||||
- fsl,mpr121
|
- fsl,mpr121
|
||||||
|
# Monolithic Power Systems Inc. multi-phase controller mp2888
|
||||||
|
- mps,mp2888
|
||||||
# Monolithic Power Systems Inc. multi-phase controller mp2975
|
# Monolithic Power Systems Inc. multi-phase controller mp2975
|
||||||
- mps,mp2975
|
- mps,mp2975
|
||||||
# G751: Digital Temperature Sensor and Thermal Watchdog with Two-Wire Interface
|
# G751: Digital Temperature Sensor and Thermal Watchdog with Two-Wire Interface
|
||||||
|
|
|
@ -20,7 +20,8 @@ Usage Notes
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
This driver does not auto-detect devices. You will have to instantiate the
|
This driver does not auto-detect devices. You will have to instantiate the
|
||||||
devices explicitly. Please see :doc:`/i2c/instantiating-devices` for details.
|
devices explicitly. Please see Documentation/i2c/instantiating-devices.rst
|
||||||
|
for details.
|
||||||
|
|
||||||
|
|
||||||
Sysfs entries
|
Sysfs entries
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
.. SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
Kernel driver dps920ab
|
||||||
|
========================
|
||||||
|
|
||||||
|
Supported chips:
|
||||||
|
|
||||||
|
* Delta DPS920AB
|
||||||
|
|
||||||
|
Prefix: 'dps920ab'
|
||||||
|
|
||||||
|
Addresses scanned: -
|
||||||
|
|
||||||
|
Authors:
|
||||||
|
Robert Marko <robert.marko@sartura.hr>
|
||||||
|
|
||||||
|
|
||||||
|
Description
|
||||||
|
-----------
|
||||||
|
|
||||||
|
This driver implements support for Delta DPS920AB 920W 54V DC single output
|
||||||
|
power supply with PMBus support.
|
||||||
|
|
||||||
|
The driver is a client driver to the core PMBus driver.
|
||||||
|
Please see Documentation/hwmon/pmbus.rst 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.rst for
|
||||||
|
details.
|
||||||
|
|
||||||
|
|
||||||
|
Sysfs entries
|
||||||
|
-------------
|
||||||
|
|
||||||
|
======================= ======================================================
|
||||||
|
curr1_label "iin"
|
||||||
|
curr1_input Measured input current
|
||||||
|
curr1_alarm Input current high alarm
|
||||||
|
|
||||||
|
curr2_label "iout1"
|
||||||
|
curr2_input Measured output current
|
||||||
|
curr2_max Maximum output current
|
||||||
|
curr2_rated_max Maximum rated output current
|
||||||
|
|
||||||
|
in1_label "vin"
|
||||||
|
in1_input Measured input voltage
|
||||||
|
in1_alarm Input voltage alarm
|
||||||
|
|
||||||
|
in2_label "vout1"
|
||||||
|
in2_input Measured output voltage
|
||||||
|
in2_rated_min Minimum rated output voltage
|
||||||
|
in2_rated_max Maximum rated output voltage
|
||||||
|
in2_alarm Output voltage alarm
|
||||||
|
|
||||||
|
power1_label "pin"
|
||||||
|
power1_input Measured input power
|
||||||
|
power1_alarm Input power high alarm
|
||||||
|
|
||||||
|
power2_label "pout1"
|
||||||
|
power2_input Measured output power
|
||||||
|
power2_rated_max Maximum rated output power
|
||||||
|
|
||||||
|
temp[1-3]_input Measured temperature
|
||||||
|
temp[1-3]_alarm Temperature alarm
|
||||||
|
|
||||||
|
fan1_alarm Fan 1 warning.
|
||||||
|
fan1_fault Fan 1 fault.
|
||||||
|
fan1_input Fan 1 speed in RPM.
|
||||||
|
======================= ======================================================
|
|
@ -53,6 +53,7 @@ Hardware Monitoring Kernel Drivers
|
||||||
da9055
|
da9055
|
||||||
dell-smm-hwmon
|
dell-smm-hwmon
|
||||||
dme1737
|
dme1737
|
||||||
|
dps920ab
|
||||||
drivetemp
|
drivetemp
|
||||||
ds1621
|
ds1621
|
||||||
ds620
|
ds620
|
||||||
|
@ -137,6 +138,7 @@ Hardware Monitoring Kernel Drivers
|
||||||
mcp3021
|
mcp3021
|
||||||
menf21bmc
|
menf21bmc
|
||||||
mlxreg-fan
|
mlxreg-fan
|
||||||
|
mp2888
|
||||||
mp2975
|
mp2975
|
||||||
nct6683
|
nct6683
|
||||||
nct6775
|
nct6775
|
||||||
|
@ -150,6 +152,7 @@ Hardware Monitoring Kernel Drivers
|
||||||
pc87360
|
pc87360
|
||||||
pc87427
|
pc87427
|
||||||
pcf8591
|
pcf8591
|
||||||
|
pim4328
|
||||||
pm6764tr
|
pm6764tr
|
||||||
pmbus
|
pmbus
|
||||||
powr1220
|
powr1220
|
||||||
|
@ -164,6 +167,7 @@ Hardware Monitoring Kernel Drivers
|
||||||
sht15
|
sht15
|
||||||
sht21
|
sht21
|
||||||
sht3x
|
sht3x
|
||||||
|
sht4x
|
||||||
shtc1
|
shtc1
|
||||||
sis5595
|
sis5595
|
||||||
sl28cpld
|
sl28cpld
|
||||||
|
|
|
@ -19,7 +19,7 @@ Authors:
|
||||||
Description
|
Description
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
The IR36021 is a dual‐loop digital multi‐phase buck controller designed for
|
The IR36021 is a dual-loop digital multi-phase buck controller designed for
|
||||||
point of load applications.
|
point of load applications.
|
||||||
|
|
||||||
Usage Notes
|
Usage Notes
|
||||||
|
|
|
@ -93,9 +93,9 @@ Supported chips:
|
||||||
|
|
||||||
https://www.st.com/resource/en/datasheet/stlm75.pdf
|
https://www.st.com/resource/en/datasheet/stlm75.pdf
|
||||||
|
|
||||||
* Texas Instruments TMP100, TMP101, TMP105, TMP112, TMP75, TMP75B, TMP75C, TMP175, TMP275
|
* Texas Instruments TMP100, TMP101, TMP105, TMP112, TMP75, TMP75B, TMP75C, TMP175, TMP275, TMP1075
|
||||||
|
|
||||||
Prefixes: 'tmp100', 'tmp101', 'tmp105', 'tmp112', 'tmp175', 'tmp75', 'tmp75b', 'tmp75c', 'tmp275'
|
Prefixes: 'tmp100', 'tmp101', 'tmp105', 'tmp112', 'tmp175', 'tmp75', 'tmp75b', 'tmp75c', 'tmp275', 'tmp1075'
|
||||||
|
|
||||||
Addresses scanned: none
|
Addresses scanned: none
|
||||||
|
|
||||||
|
@ -119,6 +119,8 @@ Supported chips:
|
||||||
|
|
||||||
https://www.ti.com/product/tmp275
|
https://www.ti.com/product/tmp275
|
||||||
|
|
||||||
|
https://www.ti.com/product/TMP1075
|
||||||
|
|
||||||
* NXP LM75B, PCT2075
|
* NXP LM75B, PCT2075
|
||||||
|
|
||||||
Prefix: 'lm75b', 'pct2075'
|
Prefix: 'lm75b', 'pct2075'
|
||||||
|
|
|
@ -19,7 +19,7 @@ This driver supports hardware monitoring for Linear Technology LTC2992 power mon
|
||||||
LTC2992 is a rail-to-rail system monitor that measures current,
|
LTC2992 is a rail-to-rail system monitor that measures current,
|
||||||
voltage, and power of two supplies.
|
voltage, and power of two supplies.
|
||||||
|
|
||||||
Two ADCs simultaneously measure each supply’s current. A third ADC monitors
|
Two ADCs simultaneously measure each supply's current. A third ADC monitors
|
||||||
the input voltages and four auxiliary external voltages.
|
the input voltages and four auxiliary external voltages.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ Sysfs entries
|
||||||
fan[1-12]_input RO fan tachometer speed in RPM
|
fan[1-12]_input RO fan tachometer speed in RPM
|
||||||
fan[1-12]_fault RO fan experienced fault
|
fan[1-12]_fault RO fan experienced fault
|
||||||
fan[1-6]_target RW desired fan speed in RPM
|
fan[1-6]_target RW desired fan speed in RPM
|
||||||
pwm[1-6]_enable RW regulator mode, 0=disabled, 1=manual mode, 2=rpm mode
|
pwm[1-6]_enable RW regulator mode, 0=disabled (duty cycle=0%), 1=manual mode, 2=rpm mode
|
||||||
pwm[1-6] RW fan target duty cycle (0-255)
|
pwm[1-6] RW read: current pwm duty cycle,
|
||||||
|
write: target pwm duty cycle (0-255)
|
||||||
================== === =======================================================
|
================== === =======================================================
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
.. SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
Kernel driver mp2888
|
||||||
|
====================
|
||||||
|
|
||||||
|
Supported chips:
|
||||||
|
|
||||||
|
* MPS MP12254
|
||||||
|
|
||||||
|
Prefix: 'mp2888'
|
||||||
|
|
||||||
|
Author:
|
||||||
|
|
||||||
|
Vadim Pasternak <vadimp@nvidia.com>
|
||||||
|
|
||||||
|
Description
|
||||||
|
-----------
|
||||||
|
|
||||||
|
This driver implements support for Monolithic Power Systems, Inc. (MPS)
|
||||||
|
vendor dual-loop, digital, multi-phase controller MP2888.
|
||||||
|
|
||||||
|
This device: supports:
|
||||||
|
|
||||||
|
- One power rail.
|
||||||
|
- Programmable Multi-Phase up to 10 Phases.
|
||||||
|
- PWM-VID Interface
|
||||||
|
- One pages 0 for telemetry.
|
||||||
|
- Programmable pins for PMBus Address.
|
||||||
|
- Built-In EEPROM to Store Custom Configurations.
|
||||||
|
|
||||||
|
Device complaint with:
|
||||||
|
|
||||||
|
- PMBus rev 1.3 interface.
|
||||||
|
|
||||||
|
Device supports direct format for reading output current, output voltage,
|
||||||
|
input and output power and temperature.
|
||||||
|
Device supports linear format for reading input voltage and input power.
|
||||||
|
|
||||||
|
The driver provides the next attributes for the current:
|
||||||
|
|
||||||
|
- for current out input and maximum alarm;
|
||||||
|
- for phase current: input and label.
|
||||||
|
|
||||||
|
The driver exports the following attributes via the 'sysfs' files, where:
|
||||||
|
|
||||||
|
- 'n' is number of configured phases (from 1 to 10);
|
||||||
|
- index 1 for "iout";
|
||||||
|
- indexes 2 ... 1 + n for phases.
|
||||||
|
|
||||||
|
**curr[1-{1+n}]_input**
|
||||||
|
|
||||||
|
**curr[1-{1+n}]_label**
|
||||||
|
|
||||||
|
**curr1_max**
|
||||||
|
|
||||||
|
**curr1_max_alarm**
|
||||||
|
|
||||||
|
The driver provides the next attributes for the voltage:
|
||||||
|
|
||||||
|
- for voltage in: input, low and high critical thresholds, low and high
|
||||||
|
critical alarms;
|
||||||
|
- for voltage out: input and high alarm;
|
||||||
|
|
||||||
|
The driver exports the following attributes via the 'sysfs' files, where
|
||||||
|
|
||||||
|
**in1_crit**
|
||||||
|
|
||||||
|
**in1_crit_alarm**
|
||||||
|
|
||||||
|
**in1_input**
|
||||||
|
|
||||||
|
**in1_label**
|
||||||
|
|
||||||
|
**in1_min**
|
||||||
|
|
||||||
|
**in1_min_alarm**
|
||||||
|
|
||||||
|
**in2_alarm**
|
||||||
|
|
||||||
|
**in2_input**
|
||||||
|
|
||||||
|
**in2_label**
|
||||||
|
|
||||||
|
The driver provides the next attributes for the power:
|
||||||
|
|
||||||
|
- for power in alarm and input.
|
||||||
|
- for power out: cap, cap alarm an input.
|
||||||
|
|
||||||
|
The driver exports the following attributes via the 'sysfs' files, where
|
||||||
|
- indexes 1 for "pin";
|
||||||
|
- indexes 2 for "pout";
|
||||||
|
|
||||||
|
**power1_alarm**
|
||||||
|
|
||||||
|
**power1_input**
|
||||||
|
|
||||||
|
**power1_label**
|
||||||
|
|
||||||
|
**power2_input**
|
||||||
|
|
||||||
|
**power2_label**
|
||||||
|
|
||||||
|
**power2_max**
|
||||||
|
|
||||||
|
**power2_max_alarm**
|
||||||
|
|
||||||
|
The driver provides the next attributes for the temperature:
|
||||||
|
|
||||||
|
**temp1_input**
|
||||||
|
|
||||||
|
**temp1_max**
|
||||||
|
|
||||||
|
**temp1_max_alarm**
|
|
@ -0,0 +1,105 @@
|
||||||
|
.. SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
Kernel driver pim4328
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Supported chips:
|
||||||
|
|
||||||
|
* Flex PIM4328
|
||||||
|
|
||||||
|
Prefix: 'pim4328', 'bmr455'
|
||||||
|
|
||||||
|
Addresses scanned: -
|
||||||
|
|
||||||
|
Datasheet:
|
||||||
|
|
||||||
|
https://flexpowermodules.com/resources/fpm-techspec-pim4328
|
||||||
|
|
||||||
|
* Flex PIM4820
|
||||||
|
|
||||||
|
Prefixes: 'pim4820'
|
||||||
|
|
||||||
|
Addresses scanned: -
|
||||||
|
|
||||||
|
Datasheet: https://flexpowermodules.com/resources/fpm-techspec-pim4820
|
||||||
|
|
||||||
|
* Flex PIM4006, PIM4106, PIM4206, PIM4306, PIM4406
|
||||||
|
|
||||||
|
Prefixes: 'pim4006', 'pim4106', 'pim4206', 'pim4306', 'pim4406'
|
||||||
|
|
||||||
|
Addresses scanned: -
|
||||||
|
|
||||||
|
Datasheet: https://flexpowermodules.com/resources/fpm-techspec-pim4006
|
||||||
|
|
||||||
|
Author: Erik Rosen <erik.rosen@metormote.com>
|
||||||
|
|
||||||
|
|
||||||
|
Description
|
||||||
|
-----------
|
||||||
|
|
||||||
|
This driver supports hardware monitoring for Flex PIM4328 and
|
||||||
|
compatible digital power interface modules.
|
||||||
|
|
||||||
|
The driver is a client driver to the core PMBus driver. Please see
|
||||||
|
Documentation/hwmon/pmbus.rst 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.rst for
|
||||||
|
details.
|
||||||
|
|
||||||
|
|
||||||
|
Platform data support
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
The driver supports standard PMBus driver platform data.
|
||||||
|
|
||||||
|
|
||||||
|
Sysfs entries
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The following attributes are supported. All attributes are read-only.
|
||||||
|
|
||||||
|
======================= ========================================================
|
||||||
|
in1_label "vin"
|
||||||
|
in1_input Measured input voltage.
|
||||||
|
in1_alarm Input voltage alarm.
|
||||||
|
|
||||||
|
in2_label "vin.0"
|
||||||
|
in2_input Measured input voltage on input A.
|
||||||
|
|
||||||
|
PIM4328 and PIM4X06
|
||||||
|
|
||||||
|
in3_label "vin.1"
|
||||||
|
in3_input Measured input voltage on input B.
|
||||||
|
|
||||||
|
PIM4328 and PIM4X06
|
||||||
|
|
||||||
|
in4_label "vcap"
|
||||||
|
in4_input Measured voltage on holdup capacitor.
|
||||||
|
|
||||||
|
PIM4328
|
||||||
|
|
||||||
|
curr1_label "iin.0"
|
||||||
|
curr1_input Measured input current on input A.
|
||||||
|
|
||||||
|
PIM4X06
|
||||||
|
|
||||||
|
curr2_label "iin.1"
|
||||||
|
curr2_input Measured input current on input B.
|
||||||
|
|
||||||
|
PIM4X06
|
||||||
|
|
||||||
|
currX_label "iout1"
|
||||||
|
currX_input Measured output current.
|
||||||
|
currX_alarm Output current alarm.
|
||||||
|
|
||||||
|
X is 1 for PIM4820, 3 otherwise.
|
||||||
|
|
||||||
|
temp1_input Measured temperature.
|
||||||
|
temp1_alarm High temperature alarm.
|
||||||
|
======================= ========================================================
|
|
@ -20,7 +20,7 @@ Description:
|
||||||
------------
|
------------
|
||||||
|
|
||||||
This driver supports the STMicroelectronics PM6764TR chip. The PM6764TR is a high
|
This driver supports the STMicroelectronics PM6764TR chip. The PM6764TR is a high
|
||||||
performance digital controller designed to power Intel’s VR12.5 processors and memories.
|
performance digital controller designed to power Intel's VR12.5 processors and memories.
|
||||||
|
|
||||||
The device utilizes digital technology to implement all control and power management
|
The device utilizes digital technology to implement all control and power management
|
||||||
functions to provide maximum flexibility and performance. The NVM is embedded to store
|
functions to provide maximum flexibility and performance. The NVM is embedded to store
|
||||||
|
|
|
@ -289,12 +289,22 @@ PMBus driver platform data
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
PMBus platform data is defined in include/linux/pmbus.h. Platform data
|
PMBus platform data is defined in include/linux/pmbus.h. Platform data
|
||||||
currently only provides a flag field with a single bit used::
|
currently provides a flags field with four bits used::
|
||||||
|
|
||||||
#define PMBUS_SKIP_STATUS_CHECK (1 << 0)
|
#define PMBUS_SKIP_STATUS_CHECK BIT(0)
|
||||||
|
|
||||||
|
#define PMBUS_WRITE_PROTECTED BIT(1)
|
||||||
|
|
||||||
|
#define PMBUS_NO_CAPABILITY BIT(2)
|
||||||
|
|
||||||
|
#define PMBUS_READ_STATUS_AFTER_FAILED_CHECK BIT(3)
|
||||||
|
|
||||||
struct pmbus_platform_data {
|
struct pmbus_platform_data {
|
||||||
u32 flags; /* Device specific flags */
|
u32 flags; /* Device specific flags */
|
||||||
|
|
||||||
|
/* regulator support */
|
||||||
|
int num_regulators;
|
||||||
|
struct regulator_init_data *reg_init_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -302,8 +312,9 @@ Flags
|
||||||
-----
|
-----
|
||||||
|
|
||||||
PMBUS_SKIP_STATUS_CHECK
|
PMBUS_SKIP_STATUS_CHECK
|
||||||
During register detection, skip checking the status register for
|
|
||||||
communication or command errors.
|
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
|
Some PMBus chips respond with valid data when trying to read an unsupported
|
||||||
register. For such chips, checking the status register is mandatory when
|
register. For such chips, checking the status register is mandatory when
|
||||||
|
@ -315,3 +326,26 @@ status register must be disabled.
|
||||||
Some i2c controllers do not support single-byte commands (write commands with
|
Some i2c controllers do not support single-byte commands (write commands with
|
||||||
no data, i2c_smbus_write_byte()). With such controllers, clearing the status
|
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.
|
register is impossible, and the PMBUS_SKIP_STATUS_CHECK flag must be set.
|
||||||
|
|
||||||
|
PMBUS_WRITE_PROTECTED
|
||||||
|
|
||||||
|
Set if the chip is write protected and write protection is not determined
|
||||||
|
by the standard WRITE_PROTECT command.
|
||||||
|
|
||||||
|
PMBUS_NO_CAPABILITY
|
||||||
|
|
||||||
|
Some PMBus chips don't respond with valid data when reading the CAPABILITY
|
||||||
|
register. For such chips, this flag should be set so that the PMBus core
|
||||||
|
driver doesn't use CAPABILITY to determine it's behavior.
|
||||||
|
|
||||||
|
PMBUS_READ_STATUS_AFTER_FAILED_CHECK
|
||||||
|
|
||||||
|
Read the STATUS register after each failed register check.
|
||||||
|
|
||||||
|
Some PMBus chips end up in an undefined state when trying to read an
|
||||||
|
unsupported register. For such chips, it is necessary to reset the
|
||||||
|
chip pmbus controller to a known state after a failed register check.
|
||||||
|
This can be done by reading a known register. By setting this flag the
|
||||||
|
driver will try to read the STATUS register after each failed
|
||||||
|
register check. This read may fail, but it will put the chip into a
|
||||||
|
known state.
|
||||||
|
|
|
@ -3,15 +3,18 @@ Kernel driver pmbus
|
||||||
|
|
||||||
Supported chips:
|
Supported chips:
|
||||||
|
|
||||||
* Ericsson BMR453, BMR454
|
* Flex BMR310, BMR453, BMR454, BMR456, BMR457, BMR458, BMR480,
|
||||||
|
BMR490, BMR491, BMR492
|
||||||
|
|
||||||
Prefixes: 'bmr453', 'bmr454'
|
Prefixes: 'bmr310', 'bmr453', 'bmr454', 'bmr456', 'bmr457', 'bmr458', 'bmr480',
|
||||||
|
'bmr490', 'bmr491', 'bmr492'
|
||||||
|
|
||||||
Addresses scanned: -
|
Addresses scanned: -
|
||||||
|
|
||||||
Datasheet:
|
Datasheets:
|
||||||
|
|
||||||
|
https://flexpowermodules.com/products
|
||||||
|
|
||||||
http://archive.ericsson.net/service/internet/picov/get?DocNo=28701-EN/LZT146395
|
|
||||||
|
|
||||||
* ON Semiconductor ADP4000, NCP4200, NCP4208
|
* ON Semiconductor ADP4000, NCP4200, NCP4208
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
.. SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
Kernel driver sht4x
|
||||||
|
===================
|
||||||
|
|
||||||
|
Supported Chips:
|
||||||
|
|
||||||
|
* Sensirion SHT4X
|
||||||
|
|
||||||
|
Prefix: 'sht4x'
|
||||||
|
|
||||||
|
Addresses scanned: None
|
||||||
|
|
||||||
|
Datasheet:
|
||||||
|
|
||||||
|
English: https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Datasheets/Sensirion_Humidity_Sensors_SHT4x_Datasheet.pdf
|
||||||
|
|
||||||
|
Author: Navin Sankar Velliangiri <navin@linumiz.com>
|
||||||
|
|
||||||
|
|
||||||
|
Description
|
||||||
|
-----------
|
||||||
|
|
||||||
|
This driver implements support for the Sensirion SHT4x chip, a humidity
|
||||||
|
and temperature sensor. Temperature is measured in degree celsius, relative
|
||||||
|
humidity is expressed as a percentage. In sysfs interface, all values are
|
||||||
|
scaled by 1000, i.e. the value for 31.5 degrees celsius is 31500.
|
||||||
|
|
||||||
|
Usage Notes
|
||||||
|
-----------
|
||||||
|
|
||||||
|
The device communicates with the I2C protocol. Sensors can have the I2C
|
||||||
|
address 0x44. See Documentation/i2c/instantiating-devices.rst for methods
|
||||||
|
to instantiate the device.
|
||||||
|
|
||||||
|
Sysfs entries
|
||||||
|
-------------
|
||||||
|
|
||||||
|
=============== ============================================
|
||||||
|
temp1_input Measured temperature in millidegrees Celcius
|
||||||
|
humidity1_input Measured humidity in %H
|
||||||
|
update_interval The minimum interval for polling the sensor,
|
||||||
|
in milliseconds. Writable. Must be at least
|
||||||
|
2000.
|
||||||
|
============== =============================================
|
|
@ -3,87 +3,103 @@ Kernel driver zl6100
|
||||||
|
|
||||||
Supported chips:
|
Supported chips:
|
||||||
|
|
||||||
* Intersil / Zilker Labs ZL2004
|
* Renesas / Intersil / Zilker Labs ZL2004
|
||||||
|
|
||||||
Prefix: 'zl2004'
|
Prefix: 'zl2004'
|
||||||
|
|
||||||
Addresses scanned: -
|
Addresses scanned: -
|
||||||
|
|
||||||
Datasheet: http://www.intersil.com/data/fn/fn6847.pdf
|
Datasheet: https://www.renesas.com/us/en/document/dst/zl2004-datasheet.pdf
|
||||||
|
|
||||||
* Intersil / Zilker Labs ZL2005
|
* Renesas / Intersil / Zilker Labs ZL2005
|
||||||
|
|
||||||
Prefix: 'zl2005'
|
Prefix: 'zl2005'
|
||||||
|
|
||||||
Addresses scanned: -
|
Addresses scanned: -
|
||||||
|
|
||||||
Datasheet: http://www.intersil.com/data/fn/fn6848.pdf
|
Datasheet: https://www.renesas.com/us/en/document/dst/zl2005-datasheet.pdf
|
||||||
|
|
||||||
* Intersil / Zilker Labs ZL2006
|
* Renesas / Intersil / Zilker Labs ZL2006
|
||||||
|
|
||||||
Prefix: 'zl2006'
|
Prefix: 'zl2006'
|
||||||
|
|
||||||
Addresses scanned: -
|
Addresses scanned: -
|
||||||
|
|
||||||
Datasheet: http://www.intersil.com/data/fn/fn6850.pdf
|
Datasheet: https://www.renesas.com/us/en/document/dst/zl2006-datasheet.pdf
|
||||||
|
|
||||||
* Intersil / Zilker Labs ZL2008
|
* Renesas / Intersil / Zilker Labs ZL2008
|
||||||
|
|
||||||
Prefix: 'zl2008'
|
Prefix: 'zl2008'
|
||||||
|
|
||||||
Addresses scanned: -
|
Addresses scanned: -
|
||||||
|
|
||||||
Datasheet: http://www.intersil.com/data/fn/fn6859.pdf
|
Datasheet: https://www.renesas.com/us/en/document/dst/zl2008-datasheet.pdf
|
||||||
|
|
||||||
* Intersil / Zilker Labs ZL2105
|
* Renesas / Intersil / Zilker Labs ZL2105
|
||||||
|
|
||||||
Prefix: 'zl2105'
|
Prefix: 'zl2105'
|
||||||
|
|
||||||
Addresses scanned: -
|
Addresses scanned: -
|
||||||
|
|
||||||
Datasheet: http://www.intersil.com/data/fn/fn6851.pdf
|
Datasheet: https://www.renesas.com/us/en/document/dst/zl2105-datasheet.pdf
|
||||||
|
|
||||||
* Intersil / Zilker Labs ZL2106
|
* Renesas / Intersil / Zilker Labs ZL2106
|
||||||
|
|
||||||
Prefix: 'zl2106'
|
Prefix: 'zl2106'
|
||||||
|
|
||||||
Addresses scanned: -
|
Addresses scanned: -
|
||||||
|
|
||||||
Datasheet: http://www.intersil.com/data/fn/fn6852.pdf
|
Datasheet: https://www.renesas.com/us/en/document/dst/zl2106-datasheet.pdf
|
||||||
|
|
||||||
* Intersil / Zilker Labs ZL6100
|
* Renesas / Intersil / Zilker Labs ZL6100
|
||||||
|
|
||||||
Prefix: 'zl6100'
|
Prefix: 'zl6100'
|
||||||
|
|
||||||
Addresses scanned: -
|
Addresses scanned: -
|
||||||
|
|
||||||
Datasheet: http://www.intersil.com/data/fn/fn6876.pdf
|
Datasheet: https://www.renesas.com/us/en/document/dst/zl6100-datasheet.pdf
|
||||||
|
|
||||||
* Intersil / Zilker Labs ZL6105
|
* Renesas / Intersil / Zilker Labs ZL6105
|
||||||
|
|
||||||
Prefix: 'zl6105'
|
Prefix: 'zl6105'
|
||||||
|
|
||||||
Addresses scanned: -
|
Addresses scanned: -
|
||||||
|
|
||||||
Datasheet: http://www.intersil.com/data/fn/fn6906.pdf
|
Datasheet: https://www.renesas.com/us/en/document/dst/zl6105-datasheet.pdf
|
||||||
|
|
||||||
* Intersil / Zilker Labs ZL9101M
|
* Renesas / Intersil / Zilker Labs ZL8802
|
||||||
|
|
||||||
|
Prefix: 'zl8802'
|
||||||
|
|
||||||
|
Addresses scanned: -
|
||||||
|
|
||||||
|
Datasheet: https://www.renesas.com/us/en/document/dst/zl8802-datasheet
|
||||||
|
|
||||||
|
* Renesas / Intersil / Zilker Labs ZL9101M
|
||||||
|
|
||||||
Prefix: 'zl9101'
|
Prefix: 'zl9101'
|
||||||
|
|
||||||
Addresses scanned: -
|
Addresses scanned: -
|
||||||
|
|
||||||
Datasheet: http://www.intersil.com/data/fn/fn7669.pdf
|
Datasheet: https://www.renesas.com/us/en/document/dst/zl9101m-datasheet
|
||||||
|
|
||||||
* Intersil / Zilker Labs ZL9117M
|
* Renesas / Intersil / Zilker Labs ZL9117M
|
||||||
|
|
||||||
Prefix: 'zl9117'
|
Prefix: 'zl9117'
|
||||||
|
|
||||||
Addresses scanned: -
|
Addresses scanned: -
|
||||||
|
|
||||||
Datasheet: http://www.intersil.com/data/fn/fn7914.pdf
|
Datasheet: https://www.renesas.com/us/en/document/dst/zl9117m-datasheet
|
||||||
|
|
||||||
* Ericsson BMR450, BMR451
|
* Renesas / Intersil / Zilker Labs ZLS1003, ZLS4009
|
||||||
|
|
||||||
|
Prefix: 'zls1003', zls4009
|
||||||
|
|
||||||
|
Addresses scanned: -
|
||||||
|
|
||||||
|
Datasheet: Not published
|
||||||
|
|
||||||
|
* Flex BMR450, BMR451
|
||||||
|
|
||||||
Prefix: 'bmr450', 'bmr451'
|
Prefix: 'bmr450', 'bmr451'
|
||||||
|
|
||||||
|
@ -91,17 +107,39 @@ Supported chips:
|
||||||
|
|
||||||
Datasheet:
|
Datasheet:
|
||||||
|
|
||||||
http://archive.ericsson.net/service/internet/picov/get?DocNo=28701-EN/LZT146401
|
https://flexpowermodules.com/resources/fpm-techspec-bmr450-digital-pol-regulators-20a
|
||||||
|
|
||||||
* Ericsson BMR462, BMR463, BMR464
|
* Flex BMR462, BMR463, BMR464
|
||||||
|
|
||||||
Prefixes: 'bmr462', 'bmr463', 'bmr464'
|
Prefixes: 'bmr462', 'bmr463', 'bmr464'
|
||||||
|
|
||||||
Addresses scanned: -
|
Addresses scanned: -
|
||||||
|
|
||||||
Datasheet:
|
Datasheet: https://flexpowermodules.com/resources/fpm-techspec-bmr462
|
||||||
|
|
||||||
http://archive.ericsson.net/service/internet/picov/get?DocNo=28701-EN/LZT146256
|
* Flex BMR465, BMR467
|
||||||
|
|
||||||
|
Prefixes: 'bmr465', 'bmr467'
|
||||||
|
|
||||||
|
Addresses scanned: -
|
||||||
|
|
||||||
|
Datasheet: https://flexpowermodules.com/resources/fpm-techspec-bmr465-digital-pol
|
||||||
|
|
||||||
|
* Flex BMR466
|
||||||
|
|
||||||
|
Prefixes: 'bmr466'
|
||||||
|
|
||||||
|
Addresses scanned: -
|
||||||
|
|
||||||
|
Datasheet: https://flexpowermodules.com/resources/fpm-techspec-bmr466-8x12
|
||||||
|
|
||||||
|
* Flex BMR469
|
||||||
|
|
||||||
|
Prefixes: 'bmr469'
|
||||||
|
|
||||||
|
Addresses scanned: -
|
||||||
|
|
||||||
|
Datasheet: https://flexpowermodules.com/resources/fpm-techspec-bmr4696001
|
||||||
|
|
||||||
Author: Guenter Roeck <linux@roeck-us.net>
|
Author: Guenter Roeck <linux@roeck-us.net>
|
||||||
|
|
||||||
|
@ -109,8 +147,8 @@ Author: Guenter Roeck <linux@roeck-us.net>
|
||||||
Description
|
Description
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
This driver supports hardware monitoring for Intersil / Zilker Labs ZL6100 and
|
This driver supports hardware monitoring for Renesas / Intersil / Zilker Labs
|
||||||
compatible digital DC-DC controllers.
|
ZL6100 and compatible digital DC-DC controllers.
|
||||||
|
|
||||||
The driver is a client driver to the core PMBus driver. Please see
|
The driver is a client driver to the core PMBus driver. Please see
|
||||||
Documentation/hwmon/pmbus.rst and Documentation.hwmon/pmbus-core for details
|
Documentation/hwmon/pmbus.rst and Documentation.hwmon/pmbus-core for details
|
||||||
|
@ -147,12 +185,12 @@ Module parameters
|
||||||
delay
|
delay
|
||||||
-----
|
-----
|
||||||
|
|
||||||
Intersil/Zilker Labs DC-DC controllers require a minimum interval between I2C
|
Renesas/Intersil/Zilker Labs DC-DC controllers require a minimum interval
|
||||||
bus accesses. According to Intersil, the minimum interval is 2 ms, though 1 ms
|
between I2C bus accesses. According to Intersil, the minimum interval is 2 ms,
|
||||||
appears to be sufficient and has not caused any problems in testing. The problem
|
though 1 ms appears to be sufficient and has not caused any problems in testing.
|
||||||
is known to affect all currently supported chips. For manual override, the
|
The problem is known to affect all currently supported chips. For manual override,
|
||||||
driver provides a writeable module parameter, 'delay', which can be used to set
|
the driver provides a writeable module parameter, 'delay', which can be used
|
||||||
the interval to a value between 0 and 65,535 microseconds.
|
to set the interval to a value between 0 and 65,535 microseconds.
|
||||||
|
|
||||||
|
|
||||||
Sysfs entries
|
Sysfs entries
|
||||||
|
@ -182,24 +220,32 @@ in2_crit Critical maximum VMON/VDRV voltage.
|
||||||
in2_lcrit_alarm VMON/VDRV voltage critical low alarm.
|
in2_lcrit_alarm VMON/VDRV voltage critical low alarm.
|
||||||
in2_crit_alarm VMON/VDRV voltage critical high alarm.
|
in2_crit_alarm VMON/VDRV voltage critical high alarm.
|
||||||
|
|
||||||
vmon attributes are supported on ZL2004, ZL9101M,
|
vmon attributes are supported on ZL2004, ZL8802,
|
||||||
and ZL9117M only.
|
ZL9101M, ZL9117M and ZLS4009 only.
|
||||||
|
|
||||||
inX_label "vout1"
|
inX_label "vout[12]"
|
||||||
inX_input Measured output voltage.
|
inX_input Measured output voltage.
|
||||||
inX_lcrit Critical minimum output Voltage.
|
inX_lcrit Critical minimum output Voltage.
|
||||||
inX_crit Critical maximum output voltage.
|
inX_crit Critical maximum output voltage.
|
||||||
inX_lcrit_alarm Critical output voltage critical low alarm.
|
inX_lcrit_alarm Critical output voltage critical low alarm.
|
||||||
inX_crit_alarm Critical output voltage critical high alarm.
|
inX_crit_alarm Critical output voltage critical high alarm.
|
||||||
|
|
||||||
X is 3 for ZL2004, ZL9101M, and ZL9117M, 2 otherwise.
|
X is 3 for ZL2004, ZL9101M, and ZL9117M,
|
||||||
|
3, 4 for ZL8802 and 2 otherwise.
|
||||||
|
|
||||||
curr1_label "iout1"
|
curr1_label "iin"
|
||||||
curr1_input Measured output current.
|
curr1_input Measured input current.
|
||||||
curr1_lcrit Critical minimum output current.
|
|
||||||
curr1_crit Critical maximum output current.
|
iin attributes are supported on ZL8802 only
|
||||||
curr1_lcrit_alarm Output current critical low alarm.
|
|
||||||
curr1_crit_alarm Output current critical high alarm.
|
currY_label "iout[12]"
|
||||||
|
currY_input Measured output current.
|
||||||
|
currY_lcrit Critical minimum output current.
|
||||||
|
currY_crit Critical maximum output current.
|
||||||
|
currY_lcrit_alarm Output current critical low alarm.
|
||||||
|
currY_crit_alarm Output current critical high alarm.
|
||||||
|
|
||||||
|
Y is 2, 3 for ZL8802, 1 otherwise
|
||||||
|
|
||||||
temp[12]_input Measured temperature.
|
temp[12]_input Measured temperature.
|
||||||
temp[12]_min Minimum temperature.
|
temp[12]_min Minimum temperature.
|
||||||
|
|
|
@ -5189,6 +5189,13 @@ W: https://linuxtv.org
|
||||||
T: git git://linuxtv.org/media_tree.git
|
T: git git://linuxtv.org/media_tree.git
|
||||||
F: drivers/media/platform/sti/delta
|
F: drivers/media/platform/sti/delta
|
||||||
|
|
||||||
|
DELTA DPS920AB PSU DRIVER
|
||||||
|
M: Robert Marko <robert.marko@sartura.hr>
|
||||||
|
L: linux-hwmon@vger.kernel.org
|
||||||
|
S: Maintained
|
||||||
|
F: Documentation/hwmon/dps920ab.rst
|
||||||
|
F: drivers/hwmon/pmbus/dps920ab.c
|
||||||
|
|
||||||
DENALI NAND DRIVER
|
DENALI NAND DRIVER
|
||||||
L: linux-mtd@lists.infradead.org
|
L: linux-mtd@lists.infradead.org
|
||||||
S: Orphan
|
S: Orphan
|
||||||
|
|
|
@ -1583,6 +1583,17 @@ config SENSORS_SHT3x
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called sht3x.
|
will be called sht3x.
|
||||||
|
|
||||||
|
config SENSORS_SHT4x
|
||||||
|
tristate "Sensiron humidity and temperature sensors. SHT4x and compat."
|
||||||
|
depends on I2C
|
||||||
|
select CRC8
|
||||||
|
help
|
||||||
|
If you say yes here you get support for the Sensiron SHT40, SHT41 and
|
||||||
|
SHT45 humidity and temperature sensors.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module
|
||||||
|
will be called sht4x.
|
||||||
|
|
||||||
config SENSORS_SHTC1
|
config SENSORS_SHTC1
|
||||||
tristate "Sensiron humidity and temperature sensors. SHTC1 and compat."
|
tristate "Sensiron humidity and temperature sensors. SHTC1 and compat."
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
|
|
@ -171,6 +171,7 @@ obj-$(CONFIG_SENSORS_SL28CPLD) += sl28cpld-hwmon.o
|
||||||
obj-$(CONFIG_SENSORS_SHT15) += sht15.o
|
obj-$(CONFIG_SENSORS_SHT15) += sht15.o
|
||||||
obj-$(CONFIG_SENSORS_SHT21) += sht21.o
|
obj-$(CONFIG_SENSORS_SHT21) += sht21.o
|
||||||
obj-$(CONFIG_SENSORS_SHT3x) += sht3x.o
|
obj-$(CONFIG_SENSORS_SHT3x) += sht3x.o
|
||||||
|
obj-$(CONFIG_SENSORS_SHT4x) += sht4x.o
|
||||||
obj-$(CONFIG_SENSORS_SHTC1) += shtc1.o
|
obj-$(CONFIG_SENSORS_SHTC1) += shtc1.o
|
||||||
obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o
|
obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o
|
||||||
obj-$(CONFIG_SENSORS_SMM665) += smm665.o
|
obj-$(CONFIG_SENSORS_SMM665) += smm665.o
|
||||||
|
|
|
@ -924,10 +924,8 @@ static int pvt_request_regs(struct pvt_hwmon *pvt)
|
||||||
}
|
}
|
||||||
|
|
||||||
pvt->regs = devm_ioremap_resource(pvt->dev, res);
|
pvt->regs = devm_ioremap_resource(pvt->dev, res);
|
||||||
if (IS_ERR(pvt->regs)) {
|
if (IS_ERR(pvt->regs))
|
||||||
dev_err(pvt->dev, "Couldn't map PVT registers\n");
|
|
||||||
return PTR_ERR(pvt->regs);
|
return PTR_ERR(pvt->regs);
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,8 +153,44 @@ static int hwmon_thermal_get_temp(void *data, int *temp)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int hwmon_thermal_set_trips(void *data, int low, int high)
|
||||||
|
{
|
||||||
|
struct hwmon_thermal_data *tdata = data;
|
||||||
|
struct hwmon_device *hwdev = to_hwmon_device(tdata->dev);
|
||||||
|
const struct hwmon_chip_info *chip = hwdev->chip;
|
||||||
|
const struct hwmon_channel_info **info = chip->info;
|
||||||
|
unsigned int i;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!chip->ops->write)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; info[i] && info[i]->type != hwmon_temp; i++)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!info[i])
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (info[i]->config[tdata->index] & HWMON_T_MIN) {
|
||||||
|
err = chip->ops->write(tdata->dev, hwmon_temp,
|
||||||
|
hwmon_temp_min, tdata->index, low);
|
||||||
|
if (err && err != -EOPNOTSUPP)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info[i]->config[tdata->index] & HWMON_T_MAX) {
|
||||||
|
err = chip->ops->write(tdata->dev, hwmon_temp,
|
||||||
|
hwmon_temp_max, tdata->index, high);
|
||||||
|
if (err && err != -EOPNOTSUPP)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct thermal_zone_of_device_ops hwmon_thermal_ops = {
|
static const struct thermal_zone_of_device_ops hwmon_thermal_ops = {
|
||||||
.get_temp = hwmon_thermal_get_temp,
|
.get_temp = hwmon_thermal_get_temp,
|
||||||
|
.set_trips = hwmon_thermal_set_trips,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void hwmon_thermal_remove_sensor(void *data)
|
static void hwmon_thermal_remove_sensor(void *data)
|
||||||
|
|
|
@ -196,13 +196,11 @@ static inline u32 ina3221_reg_to_interval_us(u16 config)
|
||||||
u32 channels = hweight16(config & INA3221_CONFIG_CHs_EN_MASK);
|
u32 channels = hweight16(config & INA3221_CONFIG_CHs_EN_MASK);
|
||||||
u32 vbus_ct_idx = INA3221_CONFIG_VBUS_CT(config);
|
u32 vbus_ct_idx = INA3221_CONFIG_VBUS_CT(config);
|
||||||
u32 vsh_ct_idx = INA3221_CONFIG_VSH_CT(config);
|
u32 vsh_ct_idx = INA3221_CONFIG_VSH_CT(config);
|
||||||
u32 samples_idx = INA3221_CONFIG_AVG(config);
|
|
||||||
u32 samples = ina3221_avg_samples[samples_idx];
|
|
||||||
u32 vbus_ct = ina3221_conv_time[vbus_ct_idx];
|
u32 vbus_ct = ina3221_conv_time[vbus_ct_idx];
|
||||||
u32 vsh_ct = ina3221_conv_time[vsh_ct_idx];
|
u32 vsh_ct = ina3221_conv_time[vsh_ct_idx];
|
||||||
|
|
||||||
/* Calculate total conversion time */
|
/* Calculate total conversion time */
|
||||||
return channels * (vbus_ct + vsh_ct) * samples;
|
return channels * (vbus_ct + vsh_ct);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int ina3221_wait_for_data(struct ina3221_data *ina)
|
static inline int ina3221_wait_for_data(struct ina3221_data *ina)
|
||||||
|
@ -288,13 +286,14 @@ static int ina3221_read_in(struct device *dev, u32 attr, int channel, long *val)
|
||||||
return -ENODATA;
|
return -ENODATA;
|
||||||
|
|
||||||
/* Write CONFIG register to trigger a single-shot measurement */
|
/* Write CONFIG register to trigger a single-shot measurement */
|
||||||
if (ina->single_shot)
|
if (ina->single_shot) {
|
||||||
regmap_write(ina->regmap, INA3221_CONFIG,
|
regmap_write(ina->regmap, INA3221_CONFIG,
|
||||||
ina->reg_config);
|
ina->reg_config);
|
||||||
|
|
||||||
ret = ina3221_wait_for_data(ina);
|
ret = ina3221_wait_for_data(ina);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
ret = ina3221_read_value(ina, reg, ®val);
|
ret = ina3221_read_value(ina, reg, ®val);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -344,13 +343,14 @@ static int ina3221_read_curr(struct device *dev, u32 attr,
|
||||||
return -ENODATA;
|
return -ENODATA;
|
||||||
|
|
||||||
/* Write CONFIG register to trigger a single-shot measurement */
|
/* Write CONFIG register to trigger a single-shot measurement */
|
||||||
if (ina->single_shot)
|
if (ina->single_shot) {
|
||||||
regmap_write(ina->regmap, INA3221_CONFIG,
|
regmap_write(ina->regmap, INA3221_CONFIG,
|
||||||
ina->reg_config);
|
ina->reg_config);
|
||||||
|
|
||||||
ret = ina3221_wait_for_data(ina);
|
ret = ina3221_wait_for_data(ina);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
fallthrough;
|
fallthrough;
|
||||||
case hwmon_curr_crit:
|
case hwmon_curr_crit:
|
||||||
|
|
|
@ -22,10 +22,10 @@
|
||||||
#include <linux/hwmon.h>
|
#include <linux/hwmon.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/mod_devicetable.h>
|
#include <linux/mod_devicetable.h>
|
||||||
|
#include <linux/of.h>
|
||||||
#include <linux/property.h>
|
#include <linux/property.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/acpi.h>
|
|
||||||
|
|
||||||
#define DRVNAME "lm70"
|
#define DRVNAME "lm70"
|
||||||
|
|
||||||
|
@ -148,29 +148,6 @@ static const struct of_device_id lm70_of_ids[] = {
|
||||||
MODULE_DEVICE_TABLE(of, lm70_of_ids);
|
MODULE_DEVICE_TABLE(of, lm70_of_ids);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_ACPI
|
|
||||||
static const struct acpi_device_id lm70_acpi_ids[] = {
|
|
||||||
{
|
|
||||||
.id = "LM000070",
|
|
||||||
.driver_data = LM70_CHIP_LM70,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.id = "TMP00121",
|
|
||||||
.driver_data = LM70_CHIP_TMP121,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.id = "LM000071",
|
|
||||||
.driver_data = LM70_CHIP_LM71,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.id = "LM000074",
|
|
||||||
.driver_data = LM70_CHIP_LM74,
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(acpi, lm70_acpi_ids);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int lm70_probe(struct spi_device *spi)
|
static int lm70_probe(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct device *hwmon_dev;
|
struct device *hwmon_dev;
|
||||||
|
@ -184,7 +161,7 @@ static int lm70_probe(struct spi_device *spi)
|
||||||
|
|
||||||
|
|
||||||
/* signaling is SPI_MODE_0 */
|
/* signaling is SPI_MODE_0 */
|
||||||
if (spi->mode & (SPI_CPOL | SPI_CPHA))
|
if ((spi->mode & SPI_MODE_X_MASK) != SPI_MODE_0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* NOTE: we assume 8-bit words, and convert to 16 bits manually */
|
/* NOTE: we assume 8-bit words, and convert to 16 bits manually */
|
||||||
|
@ -217,7 +194,6 @@ static struct spi_driver lm70_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "lm70",
|
.name = "lm70",
|
||||||
.of_match_table = of_match_ptr(lm70_of_ids),
|
.of_match_table = of_match_ptr(lm70_of_ids),
|
||||||
.acpi_match_table = ACPI_PTR(lm70_acpi_ids),
|
|
||||||
},
|
},
|
||||||
.id_table = lm70_ids,
|
.id_table = lm70_ids,
|
||||||
.probe = lm70_probe,
|
.probe = lm70_probe,
|
||||||
|
|
|
@ -50,6 +50,7 @@ enum lm75_type { /* keep sorted in alphabetical order */
|
||||||
tmp75,
|
tmp75,
|
||||||
tmp75b,
|
tmp75b,
|
||||||
tmp75c,
|
tmp75c,
|
||||||
|
tmp1075,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -293,6 +294,13 @@ static const struct lm75_params device_params[] = {
|
||||||
.clr_mask = 1 << 5, /*not one-shot mode*/
|
.clr_mask = 1 << 5, /*not one-shot mode*/
|
||||||
.default_resolution = 12,
|
.default_resolution = 12,
|
||||||
.default_sample_time = MSEC_PER_SEC / 12,
|
.default_sample_time = MSEC_PER_SEC / 12,
|
||||||
|
},
|
||||||
|
[tmp1075] = { /* not one-shot mode, 27.5 ms sample rate */
|
||||||
|
.clr_mask = 1 << 5 | 1 << 6 | 1 << 7,
|
||||||
|
.default_resolution = 12,
|
||||||
|
.default_sample_time = 28,
|
||||||
|
.num_sample_times = 4,
|
||||||
|
.sample_times = (unsigned int []){ 28, 55, 110, 220 },
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -662,6 +670,7 @@ static const struct i2c_device_id lm75_ids[] = {
|
||||||
{ "tmp75", tmp75, },
|
{ "tmp75", tmp75, },
|
||||||
{ "tmp75b", tmp75b, },
|
{ "tmp75b", tmp75b, },
|
||||||
{ "tmp75c", tmp75c, },
|
{ "tmp75c", tmp75c, },
|
||||||
|
{ "tmp1075", tmp1075, },
|
||||||
{ /* LIST END */ }
|
{ /* LIST END */ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(i2c, lm75_ids);
|
MODULE_DEVICE_TABLE(i2c, lm75_ids);
|
||||||
|
@ -771,6 +780,10 @@ static const struct of_device_id __maybe_unused lm75_of_match[] = {
|
||||||
.compatible = "ti,tmp75c",
|
.compatible = "ti,tmp75c",
|
||||||
.data = (void *)tmp75c
|
.data = (void *)tmp75c
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.compatible = "ti,tmp1075",
|
||||||
|
.data = (void *)tmp1075
|
||||||
|
},
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, lm75_of_match);
|
MODULE_DEVICE_TABLE(of, lm75_of_match);
|
||||||
|
|
|
@ -465,6 +465,7 @@ enum lm90_temp11_reg_index {
|
||||||
|
|
||||||
struct lm90_data {
|
struct lm90_data {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
|
struct device *hwmon_dev;
|
||||||
u32 channel_config[4];
|
u32 channel_config[4];
|
||||||
struct hwmon_channel_info temp_info;
|
struct hwmon_channel_info temp_info;
|
||||||
const struct hwmon_channel_info *info[3];
|
const struct hwmon_channel_info *info[3];
|
||||||
|
@ -1028,8 +1029,11 @@ static int lm90_set_temp11(struct lm90_data *data, int index, long val)
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* +16 degrees offset for temp2 for the LM99 */
|
/* +16 degrees offset for temp2 for the LM99 */
|
||||||
if (data->kind == lm99 && index <= 2)
|
if (data->kind == lm99 && index <= 2) {
|
||||||
|
/* prevent integer underflow */
|
||||||
|
val = max(val, -128000l);
|
||||||
val -= 16000;
|
val -= 16000;
|
||||||
|
}
|
||||||
|
|
||||||
if (data->kind == adt7461 || data->kind == tmp451)
|
if (data->kind == adt7461 || data->kind == tmp451)
|
||||||
data->temp11[index] = temp_to_u16_adt7461(data, val);
|
data->temp11[index] = temp_to_u16_adt7461(data, val);
|
||||||
|
@ -1088,8 +1092,11 @@ static int lm90_set_temp8(struct lm90_data *data, int index, long val)
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* +16 degrees offset for temp2 for the LM99 */
|
/* +16 degrees offset for temp2 for the LM99 */
|
||||||
if (data->kind == lm99 && index == 3)
|
if (data->kind == lm99 && index == 3) {
|
||||||
|
/* prevent integer underflow */
|
||||||
|
val = max(val, -128000l);
|
||||||
val -= 16000;
|
val -= 16000;
|
||||||
|
}
|
||||||
|
|
||||||
if (data->kind == adt7461 || data->kind == tmp451)
|
if (data->kind == adt7461 || data->kind == tmp451)
|
||||||
data->temp8[index] = temp_to_u8_adt7461(data, val);
|
data->temp8[index] = temp_to_u8_adt7461(data, val);
|
||||||
|
@ -1136,6 +1143,9 @@ static int lm90_set_temphyst(struct lm90_data *data, long val)
|
||||||
else
|
else
|
||||||
temp = temp_from_s8(data->temp8[LOCAL_CRIT]);
|
temp = temp_from_s8(data->temp8[LOCAL_CRIT]);
|
||||||
|
|
||||||
|
/* prevent integer underflow */
|
||||||
|
val = max(val, -128000l);
|
||||||
|
|
||||||
data->temp_hyst = hyst_to_reg(temp - val);
|
data->temp_hyst = hyst_to_reg(temp - val);
|
||||||
err = i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST,
|
err = i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST,
|
||||||
data->temp_hyst);
|
data->temp_hyst);
|
||||||
|
@ -1703,6 +1713,13 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
|
||||||
if (data->kind == max6696)
|
if (data->kind == max6696)
|
||||||
config &= ~0x08;
|
config &= ~0x08;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interrupt is enabled by default on reset, but it may be disabled
|
||||||
|
* by bootloader, unmask it.
|
||||||
|
*/
|
||||||
|
if (client->irq)
|
||||||
|
config &= ~0x80;
|
||||||
|
|
||||||
config &= 0xBF; /* run */
|
config &= 0xBF; /* run */
|
||||||
lm90_update_confreg(data, config);
|
lm90_update_confreg(data, config);
|
||||||
|
|
||||||
|
@ -1731,23 +1748,42 @@ static bool lm90_is_tripped(struct i2c_client *client, u16 *status)
|
||||||
|
|
||||||
if ((st & (LM90_STATUS_LLOW | LM90_STATUS_LHIGH | LM90_STATUS_LTHRM)) ||
|
if ((st & (LM90_STATUS_LLOW | LM90_STATUS_LHIGH | LM90_STATUS_LTHRM)) ||
|
||||||
(st2 & MAX6696_STATUS2_LOT2))
|
(st2 & MAX6696_STATUS2_LOT2))
|
||||||
dev_warn(&client->dev,
|
dev_dbg(&client->dev,
|
||||||
"temp%d out of range, please check!\n", 1);
|
"temp%d out of range, please check!\n", 1);
|
||||||
if ((st & (LM90_STATUS_RLOW | LM90_STATUS_RHIGH | LM90_STATUS_RTHRM)) ||
|
if ((st & (LM90_STATUS_RLOW | LM90_STATUS_RHIGH | LM90_STATUS_RTHRM)) ||
|
||||||
(st2 & MAX6696_STATUS2_ROT2))
|
(st2 & MAX6696_STATUS2_ROT2))
|
||||||
dev_warn(&client->dev,
|
dev_dbg(&client->dev,
|
||||||
"temp%d out of range, please check!\n", 2);
|
"temp%d out of range, please check!\n", 2);
|
||||||
if (st & LM90_STATUS_ROPEN)
|
if (st & LM90_STATUS_ROPEN)
|
||||||
dev_warn(&client->dev,
|
dev_dbg(&client->dev,
|
||||||
"temp%d diode open, please check!\n", 2);
|
"temp%d diode open, please check!\n", 2);
|
||||||
if (st2 & (MAX6696_STATUS2_R2LOW | MAX6696_STATUS2_R2HIGH |
|
if (st2 & (MAX6696_STATUS2_R2LOW | MAX6696_STATUS2_R2HIGH |
|
||||||
MAX6696_STATUS2_R2THRM | MAX6696_STATUS2_R2OT2))
|
MAX6696_STATUS2_R2THRM | MAX6696_STATUS2_R2OT2))
|
||||||
dev_warn(&client->dev,
|
dev_dbg(&client->dev,
|
||||||
"temp%d out of range, please check!\n", 3);
|
"temp%d out of range, please check!\n", 3);
|
||||||
if (st2 & MAX6696_STATUS2_R2OPEN)
|
if (st2 & MAX6696_STATUS2_R2OPEN)
|
||||||
dev_warn(&client->dev,
|
dev_dbg(&client->dev,
|
||||||
"temp%d diode open, please check!\n", 3);
|
"temp%d diode open, please check!\n", 3);
|
||||||
|
|
||||||
|
if (st & LM90_STATUS_LLOW)
|
||||||
|
hwmon_notify_event(data->hwmon_dev, hwmon_temp,
|
||||||
|
hwmon_temp_min, 0);
|
||||||
|
if (st & LM90_STATUS_RLOW)
|
||||||
|
hwmon_notify_event(data->hwmon_dev, hwmon_temp,
|
||||||
|
hwmon_temp_min, 1);
|
||||||
|
if (st2 & MAX6696_STATUS2_R2LOW)
|
||||||
|
hwmon_notify_event(data->hwmon_dev, hwmon_temp,
|
||||||
|
hwmon_temp_min, 2);
|
||||||
|
if (st & LM90_STATUS_LHIGH)
|
||||||
|
hwmon_notify_event(data->hwmon_dev, hwmon_temp,
|
||||||
|
hwmon_temp_max, 0);
|
||||||
|
if (st & LM90_STATUS_RHIGH)
|
||||||
|
hwmon_notify_event(data->hwmon_dev, hwmon_temp,
|
||||||
|
hwmon_temp_max, 1);
|
||||||
|
if (st2 & MAX6696_STATUS2_R2HIGH)
|
||||||
|
hwmon_notify_event(data->hwmon_dev, hwmon_temp,
|
||||||
|
hwmon_temp_max, 2);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1904,12 +1940,13 @@ static int lm90_probe(struct i2c_client *client)
|
||||||
if (IS_ERR(hwmon_dev))
|
if (IS_ERR(hwmon_dev))
|
||||||
return PTR_ERR(hwmon_dev);
|
return PTR_ERR(hwmon_dev);
|
||||||
|
|
||||||
|
data->hwmon_dev = hwmon_dev;
|
||||||
|
|
||||||
if (client->irq) {
|
if (client->irq) {
|
||||||
dev_dbg(dev, "IRQ: %d\n", client->irq);
|
dev_dbg(dev, "IRQ: %d\n", client->irq);
|
||||||
err = devm_request_threaded_irq(dev, client->irq,
|
err = devm_request_threaded_irq(dev, client->irq,
|
||||||
NULL, lm90_irq_thread,
|
NULL, lm90_irq_thread,
|
||||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
IRQF_ONESHOT, "lm90", client);
|
||||||
"lm90", client);
|
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(dev, "cannot request IRQ %d\n", client->irq);
|
dev_err(dev, "cannot request IRQ %d\n", client->irq);
|
||||||
return err;
|
return err;
|
||||||
|
@ -1941,15 +1978,40 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type,
|
||||||
lm90_update_confreg(data, data->config | 0x80);
|
lm90_update_confreg(data, data->config | 0x80);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dev_info(&client->dev, "Everything OK\n");
|
dev_dbg(&client->dev, "Everything OK\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused lm90_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct lm90_data *data = dev_get_drvdata(dev);
|
||||||
|
struct i2c_client *client = data->client;
|
||||||
|
|
||||||
|
if (client->irq)
|
||||||
|
disable_irq(client->irq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused lm90_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct lm90_data *data = dev_get_drvdata(dev);
|
||||||
|
struct i2c_client *client = data->client;
|
||||||
|
|
||||||
|
if (client->irq)
|
||||||
|
enable_irq(client->irq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIMPLE_DEV_PM_OPS(lm90_pm_ops, lm90_suspend, lm90_resume);
|
||||||
|
|
||||||
static struct i2c_driver lm90_driver = {
|
static struct i2c_driver lm90_driver = {
|
||||||
.class = I2C_CLASS_HWMON,
|
.class = I2C_CLASS_HWMON,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "lm90",
|
.name = "lm90",
|
||||||
.of_match_table = of_match_ptr(lm90_of_match),
|
.of_match_table = of_match_ptr(lm90_of_match),
|
||||||
|
.pm = &lm90_pm_ops,
|
||||||
},
|
},
|
||||||
.probe_new = lm90_probe,
|
.probe_new = lm90_probe,
|
||||||
.alert = lm90_alert,
|
.alert = lm90_alert,
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
* Copyright (c) 2016, Intel Corporation.
|
* Copyright (c) 2016, Intel Corporation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/acpi.h>
|
|
||||||
#include <linux/hwmon.h>
|
#include <linux/hwmon.h>
|
||||||
#include <linux/hwmon-sysfs.h>
|
#include <linux/hwmon-sysfs.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
@ -133,20 +132,12 @@ static const struct spi_device_id max31722_spi_id[] = {
|
||||||
{"max31723", 0},
|
{"max31723", 0},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct acpi_device_id __maybe_unused max31722_acpi_id[] = {
|
|
||||||
{"MAX31722", 0},
|
|
||||||
{"MAX31723", 0},
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
MODULE_DEVICE_TABLE(spi, max31722_spi_id);
|
MODULE_DEVICE_TABLE(spi, max31722_spi_id);
|
||||||
|
|
||||||
static struct spi_driver max31722_driver = {
|
static struct spi_driver max31722_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "max31722",
|
.name = "max31722",
|
||||||
.pm = &max31722_pm_ops,
|
.pm = &max31722_pm_ops,
|
||||||
.acpi_match_table = ACPI_PTR(max31722_acpi_id),
|
|
||||||
},
|
},
|
||||||
.probe = max31722_probe,
|
.probe = max31722_probe,
|
||||||
.remove = max31722_remove,
|
.remove = max31722_remove,
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
/* Fan Config register bits */
|
/* Fan Config register bits */
|
||||||
#define MAX31790_FAN_CFG_RPM_MODE 0x80
|
#define MAX31790_FAN_CFG_RPM_MODE 0x80
|
||||||
|
#define MAX31790_FAN_CFG_CTRL_MON 0x10
|
||||||
#define MAX31790_FAN_CFG_TACH_INPUT_EN 0x08
|
#define MAX31790_FAN_CFG_TACH_INPUT_EN 0x08
|
||||||
#define MAX31790_FAN_CFG_TACH_INPUT 0x01
|
#define MAX31790_FAN_CFG_TACH_INPUT 0x01
|
||||||
|
|
||||||
|
@ -39,6 +40,8 @@
|
||||||
#define FAN_RPM_MIN 120
|
#define FAN_RPM_MIN 120
|
||||||
#define FAN_RPM_MAX 7864320
|
#define FAN_RPM_MAX 7864320
|
||||||
|
|
||||||
|
#define FAN_COUNT_REG_MAX 0xffe0
|
||||||
|
|
||||||
#define RPM_FROM_REG(reg, sr) (((reg) >> 4) ? \
|
#define RPM_FROM_REG(reg, sr) (((reg) >> 4) ? \
|
||||||
((60 * (sr) * 8192) / ((reg) >> 4)) : \
|
((60 * (sr) * 8192) / ((reg) >> 4)) : \
|
||||||
FAN_RPM_MAX)
|
FAN_RPM_MAX)
|
||||||
|
@ -79,7 +82,7 @@ static struct max31790_data *max31790_update_device(struct device *dev)
|
||||||
MAX31790_REG_FAN_FAULT_STATUS1);
|
MAX31790_REG_FAN_FAULT_STATUS1);
|
||||||
if (rv < 0)
|
if (rv < 0)
|
||||||
goto abort;
|
goto abort;
|
||||||
data->fault_status = rv & 0x3F;
|
data->fault_status |= rv & 0x3F;
|
||||||
|
|
||||||
rv = i2c_smbus_read_byte_data(client,
|
rv = i2c_smbus_read_byte_data(client,
|
||||||
MAX31790_REG_FAN_FAULT_STATUS2);
|
MAX31790_REG_FAN_FAULT_STATUS2);
|
||||||
|
@ -104,7 +107,7 @@ static struct max31790_data *max31790_update_device(struct device *dev)
|
||||||
data->tach[NR_CHANNEL + i] = rv;
|
data->tach[NR_CHANNEL + i] = rv;
|
||||||
} else {
|
} else {
|
||||||
rv = i2c_smbus_read_word_swapped(client,
|
rv = i2c_smbus_read_word_swapped(client,
|
||||||
MAX31790_REG_PWMOUT(i));
|
MAX31790_REG_PWM_DUTY_CYCLE(i));
|
||||||
if (rv < 0)
|
if (rv < 0)
|
||||||
goto abort;
|
goto abort;
|
||||||
data->pwm[i] = rv;
|
data->pwm[i] = rv;
|
||||||
|
@ -170,7 +173,10 @@ static int max31790_read_fan(struct device *dev, u32 attr, int channel,
|
||||||
|
|
||||||
switch (attr) {
|
switch (attr) {
|
||||||
case hwmon_fan_input:
|
case hwmon_fan_input:
|
||||||
sr = get_tach_period(data->fan_dynamics[channel]);
|
sr = get_tach_period(data->fan_dynamics[channel % NR_CHANNEL]);
|
||||||
|
if (data->tach[channel] == FAN_COUNT_REG_MAX)
|
||||||
|
rpm = 0;
|
||||||
|
else
|
||||||
rpm = RPM_FROM_REG(data->tach[channel], sr);
|
rpm = RPM_FROM_REG(data->tach[channel], sr);
|
||||||
*val = rpm;
|
*val = rpm;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -180,7 +186,21 @@ static int max31790_read_fan(struct device *dev, u32 attr, int channel,
|
||||||
*val = rpm;
|
*val = rpm;
|
||||||
return 0;
|
return 0;
|
||||||
case hwmon_fan_fault:
|
case hwmon_fan_fault:
|
||||||
|
mutex_lock(&data->update_lock);
|
||||||
*val = !!(data->fault_status & (1 << channel));
|
*val = !!(data->fault_status & (1 << channel));
|
||||||
|
data->fault_status &= ~(1 << channel);
|
||||||
|
/*
|
||||||
|
* If a fault bit is set, we need to write into one of the fan
|
||||||
|
* configuration registers to clear it. Note that this also
|
||||||
|
* clears the fault for the companion channel if enabled.
|
||||||
|
*/
|
||||||
|
if (*val) {
|
||||||
|
int reg = MAX31790_REG_TARGET_COUNT(channel % NR_CHANNEL);
|
||||||
|
|
||||||
|
i2c_smbus_write_byte_data(data->client, reg,
|
||||||
|
data->target_count[channel % NR_CHANNEL] >> 8);
|
||||||
|
}
|
||||||
|
mutex_unlock(&data->update_lock);
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
@ -271,12 +291,12 @@ static int max31790_read_pwm(struct device *dev, u32 attr, int channel,
|
||||||
*val = data->pwm[channel] >> 8;
|
*val = data->pwm[channel] >> 8;
|
||||||
return 0;
|
return 0;
|
||||||
case hwmon_pwm_enable:
|
case hwmon_pwm_enable:
|
||||||
if (fan_config & MAX31790_FAN_CFG_RPM_MODE)
|
if (fan_config & MAX31790_FAN_CFG_CTRL_MON)
|
||||||
*val = 2;
|
|
||||||
else if (fan_config & MAX31790_FAN_CFG_TACH_INPUT_EN)
|
|
||||||
*val = 1;
|
|
||||||
else
|
|
||||||
*val = 0;
|
*val = 0;
|
||||||
|
else if (fan_config & MAX31790_FAN_CFG_RPM_MODE)
|
||||||
|
*val = 2;
|
||||||
|
else
|
||||||
|
*val = 1;
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
@ -299,31 +319,41 @@ static int max31790_write_pwm(struct device *dev, u32 attr, int channel,
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
data->pwm[channel] = val << 8;
|
data->valid = false;
|
||||||
err = i2c_smbus_write_word_swapped(client,
|
err = i2c_smbus_write_word_swapped(client,
|
||||||
MAX31790_REG_PWMOUT(channel),
|
MAX31790_REG_PWMOUT(channel),
|
||||||
data->pwm[channel]);
|
val << 8);
|
||||||
break;
|
break;
|
||||||
case hwmon_pwm_enable:
|
case hwmon_pwm_enable:
|
||||||
fan_config = data->fan_config[channel];
|
fan_config = data->fan_config[channel];
|
||||||
if (val == 0) {
|
if (val == 0) {
|
||||||
fan_config &= ~(MAX31790_FAN_CFG_TACH_INPUT_EN |
|
fan_config |= MAX31790_FAN_CFG_CTRL_MON;
|
||||||
MAX31790_FAN_CFG_RPM_MODE);
|
/*
|
||||||
|
* Disable RPM mode; otherwise disabling fan speed
|
||||||
|
* monitoring is not possible.
|
||||||
|
*/
|
||||||
|
fan_config &= ~MAX31790_FAN_CFG_RPM_MODE;
|
||||||
} else if (val == 1) {
|
} else if (val == 1) {
|
||||||
fan_config = (fan_config |
|
fan_config &= ~(MAX31790_FAN_CFG_CTRL_MON | MAX31790_FAN_CFG_RPM_MODE);
|
||||||
MAX31790_FAN_CFG_TACH_INPUT_EN) &
|
|
||||||
~MAX31790_FAN_CFG_RPM_MODE;
|
|
||||||
} else if (val == 2) {
|
} else if (val == 2) {
|
||||||
fan_config |= MAX31790_FAN_CFG_TACH_INPUT_EN |
|
fan_config &= ~MAX31790_FAN_CFG_CTRL_MON;
|
||||||
MAX31790_FAN_CFG_RPM_MODE;
|
/*
|
||||||
|
* The chip sets MAX31790_FAN_CFG_TACH_INPUT_EN on its
|
||||||
|
* own if MAX31790_FAN_CFG_RPM_MODE is set.
|
||||||
|
* Do it here as well to reflect the actual register
|
||||||
|
* value in the cache.
|
||||||
|
*/
|
||||||
|
fan_config |= (MAX31790_FAN_CFG_RPM_MODE | MAX31790_FAN_CFG_TACH_INPUT_EN);
|
||||||
} else {
|
} else {
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
data->fan_config[channel] = fan_config;
|
if (fan_config != data->fan_config[channel]) {
|
||||||
err = i2c_smbus_write_byte_data(client,
|
err = i2c_smbus_write_byte_data(client, MAX31790_REG_FAN_CONFIG(channel),
|
||||||
MAX31790_REG_FAN_CONFIG(channel),
|
|
||||||
fan_config);
|
fan_config);
|
||||||
|
if (!err)
|
||||||
|
data->fan_config[channel] = fan_config;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
err = -EOPNOTSUPP;
|
err = -EOPNOTSUPP;
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pm_runtime.h>
|
|
||||||
#include <linux/math64.h>
|
#include <linux/math64.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
@ -17,9 +16,6 @@
|
||||||
|
|
||||||
#include <linux/platform_data/ntc_thermistor.h>
|
#include <linux/platform_data/ntc_thermistor.h>
|
||||||
|
|
||||||
#include <linux/iio/iio.h>
|
|
||||||
#include <linux/iio/machine.h>
|
|
||||||
#include <linux/iio/driver.h>
|
|
||||||
#include <linux/iio/consumer.h>
|
#include <linux/iio/consumer.h>
|
||||||
|
|
||||||
#include <linux/hwmon.h>
|
#include <linux/hwmon.h>
|
||||||
|
|
|
@ -19,9 +19,10 @@ config SENSORS_PMBUS
|
||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
If you say yes here you get hardware monitoring support for generic
|
If you say yes here you get hardware monitoring support for generic
|
||||||
PMBus devices, including but not limited to ADP4000, BMR453, BMR454,
|
PMBus devices, including but not limited to ADP4000, BMR310, BMR453,
|
||||||
MAX20796, MDT040, NCP4200, NCP4208, PDT003, PDT006, PDT012, TPS40400,
|
BMR454, BMR456, BMR457, BMR458, BMR480, BMR490, BMR491, BMR492,
|
||||||
TPS544B20, TPS544B25, TPS544C20, TPS544C25, and UDT020.
|
MAX20796, MDT040, NCP4200, NCP4208, PDT003, PDT006, PDT012,
|
||||||
|
TPS40400, TPS544B20, TPS544B25, TPS544C20, TPS544C25, and UDT020.
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module will
|
This driver can also be built as a module. If so, the module will
|
||||||
be called pmbus.
|
be called pmbus.
|
||||||
|
@ -85,6 +86,15 @@ config SENSORS_IBM_CFFPS
|
||||||
This driver can also be built as a module. If so, the module will
|
This driver can also be built as a module. If so, the module will
|
||||||
be called ibm-cffps.
|
be called ibm-cffps.
|
||||||
|
|
||||||
|
config SENSORS_DPS920AB
|
||||||
|
tristate "Delta DPS920AB Power Supply"
|
||||||
|
help
|
||||||
|
If you say yes here you get hardware monitoring support for Delta
|
||||||
|
DPS920AB Power Supplies.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module will
|
||||||
|
be called dps920ab.
|
||||||
|
|
||||||
config SENSORS_INSPUR_IPSPS
|
config SENSORS_INSPUR_IPSPS
|
||||||
tristate "INSPUR Power System Power Supply"
|
tristate "INSPUR Power System Power Supply"
|
||||||
help
|
help
|
||||||
|
@ -248,6 +258,15 @@ config SENSORS_MAX8688
|
||||||
This driver can also be built as a module. If so, the module will
|
This driver can also be built as a module. If so, the module will
|
||||||
be called max8688.
|
be called max8688.
|
||||||
|
|
||||||
|
config SENSORS_MP2888
|
||||||
|
tristate "MPS MP2888"
|
||||||
|
help
|
||||||
|
If you say yes here you get hardware monitoring support for MPS
|
||||||
|
MP2888 Digital, Multi-Phase, Pulse-Width Modulation Controller.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module will
|
||||||
|
be called mp2888.
|
||||||
|
|
||||||
config SENSORS_MP2975
|
config SENSORS_MP2975
|
||||||
tristate "MPS MP2975"
|
tristate "MPS MP2975"
|
||||||
help
|
help
|
||||||
|
@ -257,6 +276,15 @@ config SENSORS_MP2975
|
||||||
This driver can also be built as a module. If so, the module will
|
This driver can also be built as a module. If so, the module will
|
||||||
be called mp2975.
|
be called mp2975.
|
||||||
|
|
||||||
|
config SENSORS_PIM4328
|
||||||
|
tristate "Flex PIM4328 and compatibles"
|
||||||
|
help
|
||||||
|
If you say yes here you get hardware monitoring support for Flex
|
||||||
|
PIM4328, PIM4820 and PIM4006 Power Interface Modules.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module will
|
||||||
|
be called pim4328.
|
||||||
|
|
||||||
config SENSORS_PM6764TR
|
config SENSORS_PM6764TR
|
||||||
tristate "ST PM6764TR"
|
tristate "ST PM6764TR"
|
||||||
help
|
help
|
||||||
|
|
|
@ -11,6 +11,7 @@ obj-$(CONFIG_SENSORS_BEL_PFE) += bel-pfe.o
|
||||||
obj-$(CONFIG_SENSORS_BPA_RS600) += bpa-rs600.o
|
obj-$(CONFIG_SENSORS_BPA_RS600) += bpa-rs600.o
|
||||||
obj-$(CONFIG_SENSORS_FSP_3Y) += fsp-3y.o
|
obj-$(CONFIG_SENSORS_FSP_3Y) += fsp-3y.o
|
||||||
obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o
|
obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o
|
||||||
|
obj-$(CONFIG_SENSORS_DPS920AB) += dps920ab.o
|
||||||
obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o
|
obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o
|
||||||
obj-$(CONFIG_SENSORS_IR35221) += ir35221.o
|
obj-$(CONFIG_SENSORS_IR35221) += ir35221.o
|
||||||
obj-$(CONFIG_SENSORS_IR36021) += ir36021.o
|
obj-$(CONFIG_SENSORS_IR36021) += ir36021.o
|
||||||
|
@ -28,6 +29,7 @@ obj-$(CONFIG_SENSORS_MAX20751) += max20751.o
|
||||||
obj-$(CONFIG_SENSORS_MAX31785) += max31785.o
|
obj-$(CONFIG_SENSORS_MAX31785) += max31785.o
|
||||||
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
|
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
|
||||||
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
|
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
|
||||||
|
obj-$(CONFIG_SENSORS_MP2888) += mp2888.o
|
||||||
obj-$(CONFIG_SENSORS_MP2975) += mp2975.o
|
obj-$(CONFIG_SENSORS_MP2975) += mp2975.o
|
||||||
obj-$(CONFIG_SENSORS_PM6764TR) += pm6764tr.o
|
obj-$(CONFIG_SENSORS_PM6764TR) += pm6764tr.o
|
||||||
obj-$(CONFIG_SENSORS_PXE1610) += pxe1610.o
|
obj-$(CONFIG_SENSORS_PXE1610) += pxe1610.o
|
||||||
|
@ -39,3 +41,4 @@ obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
|
||||||
obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
|
obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
|
||||||
obj-$(CONFIG_SENSORS_XDPE122) += xdpe12284.o
|
obj-$(CONFIG_SENSORS_XDPE122) += xdpe12284.o
|
||||||
obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o
|
obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o
|
||||||
|
obj-$(CONFIG_SENSORS_PIM4328) += pim4328.o
|
||||||
|
|
|
@ -611,11 +611,13 @@ static int adm1275_probe(struct i2c_client *client)
|
||||||
tindex = 8;
|
tindex = 8;
|
||||||
|
|
||||||
info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT |
|
info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT |
|
||||||
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT;
|
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
|
||||||
|
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
|
||||||
|
|
||||||
/* Enable VOUT if not enabled (it is disabled by default) */
|
/* Enable VOUT & TEMP1 if not enabled (disabled by default) */
|
||||||
if (!(config & ADM1278_VOUT_EN)) {
|
if ((config & (ADM1278_VOUT_EN | ADM1278_TEMP1_EN)) !=
|
||||||
config |= ADM1278_VOUT_EN;
|
(ADM1278_VOUT_EN | ADM1278_TEMP1_EN)) {
|
||||||
|
config |= ADM1278_VOUT_EN | ADM1278_TEMP1_EN;
|
||||||
ret = i2c_smbus_write_byte_data(client,
|
ret = i2c_smbus_write_byte_data(client,
|
||||||
ADM1275_PMON_CONFIG,
|
ADM1275_PMON_CONFIG,
|
||||||
config);
|
config);
|
||||||
|
@ -625,10 +627,6 @@ static int adm1275_probe(struct i2c_client *client)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config & ADM1278_TEMP1_EN)
|
|
||||||
info->func[0] |=
|
|
||||||
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
|
|
||||||
if (config & ADM1278_VIN_EN)
|
if (config & ADM1278_VIN_EN)
|
||||||
info->func[0] |= PMBUS_HAVE_VIN;
|
info->func[0] |= PMBUS_HAVE_VIN;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -46,6 +46,32 @@ static int bpa_rs600_read_byte_data(struct i2c_client *client, int page, int reg
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The BPA-RS600 violates the PMBus spec. Specifically it treats the
|
||||||
|
* mantissa as unsigned. Deal with this here to allow the PMBus core
|
||||||
|
* to work with correctly encoded data.
|
||||||
|
*/
|
||||||
|
static int bpa_rs600_read_vin(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
int ret, exponent, mantissa;
|
||||||
|
|
||||||
|
ret = pmbus_read_word_data(client, 0, 0xff, PMBUS_READ_VIN);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (ret & BIT(10)) {
|
||||||
|
exponent = ret >> 11;
|
||||||
|
mantissa = ret & 0x7ff;
|
||||||
|
|
||||||
|
exponent++;
|
||||||
|
mantissa >>= 1;
|
||||||
|
|
||||||
|
ret = (exponent << 11) | mantissa;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int bpa_rs600_read_word_data(struct i2c_client *client, int page, int phase, int reg)
|
static int bpa_rs600_read_word_data(struct i2c_client *client, int page, int phase, int reg)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -85,6 +111,9 @@ static int bpa_rs600_read_word_data(struct i2c_client *client, int page, int pha
|
||||||
/* These commands return data but it is invalid/un-documented */
|
/* These commands return data but it is invalid/un-documented */
|
||||||
ret = -ENXIO;
|
ret = -ENXIO;
|
||||||
break;
|
break;
|
||||||
|
case PMBUS_READ_VIN:
|
||||||
|
ret = bpa_rs600_read_vin(client);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if (reg >= PMBUS_VIRT_BASE)
|
if (reg >= PMBUS_VIRT_BASE)
|
||||||
ret = -ENXIO;
|
ret = -ENXIO;
|
||||||
|
|
|
@ -0,0 +1,206 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Driver for Delta DPS920AB PSU
|
||||||
|
*
|
||||||
|
* Copyright (C) 2021 Delta Networks, Inc.
|
||||||
|
* Copyright (C) 2021 Sartura Ltd.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/debugfs.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include "pmbus.h"
|
||||||
|
|
||||||
|
struct dps920ab_data {
|
||||||
|
char *mfr_model;
|
||||||
|
char *mfr_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int dps920ab_read_word_data(struct i2c_client *client, int page, int phase, int reg)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This masks commands which are not supported.
|
||||||
|
* PSU advertises that all features are supported,
|
||||||
|
* in reality that unfortunately is not true.
|
||||||
|
* So enable only those that the datasheet confirms.
|
||||||
|
*/
|
||||||
|
switch (reg) {
|
||||||
|
case PMBUS_FAN_COMMAND_1:
|
||||||
|
case PMBUS_IOUT_OC_WARN_LIMIT:
|
||||||
|
case PMBUS_STATUS_WORD:
|
||||||
|
case PMBUS_READ_VIN:
|
||||||
|
case PMBUS_READ_IIN:
|
||||||
|
case PMBUS_READ_VOUT:
|
||||||
|
case PMBUS_READ_IOUT:
|
||||||
|
case PMBUS_READ_TEMPERATURE_1:
|
||||||
|
case PMBUS_READ_TEMPERATURE_2:
|
||||||
|
case PMBUS_READ_TEMPERATURE_3:
|
||||||
|
case PMBUS_READ_FAN_SPEED_1:
|
||||||
|
case PMBUS_READ_POUT:
|
||||||
|
case PMBUS_READ_PIN:
|
||||||
|
case PMBUS_MFR_VOUT_MIN:
|
||||||
|
case PMBUS_MFR_VOUT_MAX:
|
||||||
|
case PMBUS_MFR_IOUT_MAX:
|
||||||
|
case PMBUS_MFR_POUT_MAX:
|
||||||
|
return pmbus_read_word_data(client, page, phase, reg);
|
||||||
|
default:
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dps920ab_write_word_data(struct i2c_client *client, int page, int reg,
|
||||||
|
u16 word)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This masks commands which are not supported.
|
||||||
|
* PSU only has one R/W register and that is
|
||||||
|
* for the fan.
|
||||||
|
*/
|
||||||
|
switch (reg) {
|
||||||
|
case PMBUS_FAN_COMMAND_1:
|
||||||
|
return pmbus_write_word_data(client, page, reg, word);
|
||||||
|
default:
|
||||||
|
return -EACCES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pmbus_driver_info dps920ab_info = {
|
||||||
|
.pages = 1,
|
||||||
|
|
||||||
|
.format[PSC_VOLTAGE_IN] = linear,
|
||||||
|
.format[PSC_VOLTAGE_OUT] = linear,
|
||||||
|
.format[PSC_CURRENT_IN] = linear,
|
||||||
|
.format[PSC_CURRENT_OUT] = linear,
|
||||||
|
.format[PSC_POWER] = linear,
|
||||||
|
.format[PSC_FAN] = linear,
|
||||||
|
.format[PSC_TEMPERATURE] = linear,
|
||||||
|
|
||||||
|
.func[0] =
|
||||||
|
PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN |
|
||||||
|
PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT |
|
||||||
|
PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 |
|
||||||
|
PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 |
|
||||||
|
PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
|
||||||
|
PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
|
||||||
|
.read_word_data = dps920ab_read_word_data,
|
||||||
|
.write_word_data = dps920ab_write_word_data,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int dps920ab_mfr_id_show(struct seq_file *s, void *data)
|
||||||
|
{
|
||||||
|
struct dps920ab_data *priv = s->private;
|
||||||
|
|
||||||
|
seq_printf(s, "%s\n", priv->mfr_id);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_SHOW_ATTRIBUTE(dps920ab_mfr_id);
|
||||||
|
|
||||||
|
static int dps920ab_mfr_model_show(struct seq_file *s, void *data)
|
||||||
|
{
|
||||||
|
struct dps920ab_data *priv = s->private;
|
||||||
|
|
||||||
|
seq_printf(s, "%s\n", priv->mfr_model);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_SHOW_ATTRIBUTE(dps920ab_mfr_model);
|
||||||
|
|
||||||
|
static void dps920ab_init_debugfs(struct dps920ab_data *data, struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct dentry *debugfs_dir;
|
||||||
|
struct dentry *root;
|
||||||
|
|
||||||
|
root = pmbus_get_debugfs_dir(client);
|
||||||
|
if (!root)
|
||||||
|
return;
|
||||||
|
|
||||||
|
debugfs_dir = debugfs_create_dir(client->name, root);
|
||||||
|
|
||||||
|
debugfs_create_file("mfr_id",
|
||||||
|
0400,
|
||||||
|
debugfs_dir,
|
||||||
|
data,
|
||||||
|
&dps920ab_mfr_id_fops);
|
||||||
|
|
||||||
|
debugfs_create_file("mfr_model",
|
||||||
|
0400,
|
||||||
|
debugfs_dir,
|
||||||
|
data,
|
||||||
|
&dps920ab_mfr_model_fops);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dps920ab_probe(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
|
||||||
|
struct dps920ab_data *data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
|
||||||
|
if (!data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&client->dev, "Failed to read Manufacturer ID\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[ret] = '\0';
|
||||||
|
if (ret != 5 || strncmp(buf, "DELTA", 5)) {
|
||||||
|
buf[ret] = '\0';
|
||||||
|
dev_err(&client->dev, "Unsupported Manufacturer ID '%s'\n", buf);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
data->mfr_id = devm_kstrdup(&client->dev, buf, GFP_KERNEL);
|
||||||
|
if (!data->mfr_id)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&client->dev, "Failed to read Manufacturer Model\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[ret] = '\0';
|
||||||
|
if (ret != 11 || strncmp(buf, "DPS-920AB", 9)) {
|
||||||
|
dev_err(&client->dev, "Unsupported Manufacturer Model '%s'\n", buf);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
data->mfr_model = devm_kstrdup(&client->dev, buf, GFP_KERNEL);
|
||||||
|
if (!data->mfr_model)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = pmbus_do_probe(client, &dps920ab_info);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
dps920ab_init_debugfs(data, client);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id __maybe_unused dps920ab_of_match[] = {
|
||||||
|
{ .compatible = "delta,dps920ab", },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(of, dps920ab_of_match);
|
||||||
|
|
||||||
|
static struct i2c_driver dps920ab_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "dps920ab",
|
||||||
|
.of_match_table = of_match_ptr(dps920ab_of_match),
|
||||||
|
},
|
||||||
|
.probe_new = dps920ab_probe,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_i2c_driver(dps920ab_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>");
|
||||||
|
MODULE_DESCRIPTION("PMBus driver for Delta DPS920AB PSU");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_IMPORT_NS(PMBUS);
|
|
@ -0,0 +1,408 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Hardware monitoring driver for MPS Multi-phase Digital VR Controllers
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 Nvidia Technologies Ltd.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include "pmbus.h"
|
||||||
|
|
||||||
|
/* Vendor specific registers. */
|
||||||
|
#define MP2888_MFR_SYS_CONFIG 0x44
|
||||||
|
#define MP2888_MFR_READ_CS1_2 0x73
|
||||||
|
#define MP2888_MFR_READ_CS3_4 0x74
|
||||||
|
#define MP2888_MFR_READ_CS5_6 0x75
|
||||||
|
#define MP2888_MFR_READ_CS7_8 0x76
|
||||||
|
#define MP2888_MFR_READ_CS9_10 0x77
|
||||||
|
#define MP2888_MFR_VR_CONFIG1 0xe1
|
||||||
|
|
||||||
|
#define MP2888_TOTAL_CURRENT_RESOLUTION BIT(3)
|
||||||
|
#define MP2888_PHASE_CURRENT_RESOLUTION BIT(4)
|
||||||
|
#define MP2888_DRMOS_KCS GENMASK(2, 0)
|
||||||
|
#define MP2888_TEMP_UNIT 10
|
||||||
|
#define MP2888_MAX_PHASE 10
|
||||||
|
|
||||||
|
struct mp2888_data {
|
||||||
|
struct pmbus_driver_info info;
|
||||||
|
int total_curr_resolution;
|
||||||
|
int phase_curr_resolution;
|
||||||
|
int curr_sense_gain;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define to_mp2888_data(x) container_of(x, struct mp2888_data, info)
|
||||||
|
|
||||||
|
static int mp2888_read_byte_data(struct i2c_client *client, int page, int reg)
|
||||||
|
{
|
||||||
|
switch (reg) {
|
||||||
|
case PMBUS_VOUT_MODE:
|
||||||
|
/* Enforce VOUT direct format. */
|
||||||
|
return PB_VOUT_MODE_DIRECT;
|
||||||
|
default:
|
||||||
|
return -ENODATA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mp2888_current_sense_gain_and_resolution_get(struct i2c_client *client, struct mp2888_data *data)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Obtain DrMOS current sense gain of power stage from the register
|
||||||
|
* , bits 0-2. The value is selected as below:
|
||||||
|
* 00b - 5µA/A, 01b - 8.5µA/A, 10b - 9.7µA/A, 11b - 10µA/A. Other
|
||||||
|
* values are reserved.
|
||||||
|
*/
|
||||||
|
ret = i2c_smbus_read_word_data(client, MP2888_MFR_SYS_CONFIG);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
switch (ret & MP2888_DRMOS_KCS) {
|
||||||
|
case 0:
|
||||||
|
data->curr_sense_gain = 85;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
data->curr_sense_gain = 97;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
data->curr_sense_gain = 100;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
data->curr_sense_gain = 50;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Obtain resolution selector for total and phase current report and protection.
|
||||||
|
* 0: original resolution; 1: half resolution (in such case phase current value should
|
||||||
|
* be doubled.
|
||||||
|
*/
|
||||||
|
data->total_curr_resolution = (ret & MP2888_TOTAL_CURRENT_RESOLUTION) >> 3;
|
||||||
|
data->phase_curr_resolution = (ret & MP2888_PHASE_CURRENT_RESOLUTION) >> 4;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mp2888_read_phase(struct i2c_client *client, struct mp2888_data *data, int page, int phase, u8 reg)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = pmbus_read_word_data(client, page, phase, reg);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (!((phase + 1) % 2))
|
||||||
|
ret >>= 8;
|
||||||
|
ret &= 0xff;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Output value is calculated as: (READ_CSx / 80 – 1.23) / (Kcs * Rcs)
|
||||||
|
* where:
|
||||||
|
* - Kcs is the DrMOS current sense gain of power stage, which is obtained from the
|
||||||
|
* register MP2888_MFR_VR_CONFIG1, bits 13-12 with the following selection of DrMOS
|
||||||
|
* (data->curr_sense_gain):
|
||||||
|
* 00b - 5µA/A, 01b - 8.5µA/A, 10b - 9.7µA/A, 11b - 10µA/A.
|
||||||
|
* - Rcs is the internal phase current sense resistor. This parameter depends on hardware
|
||||||
|
* assembly. By default it is set to 1kΩ. In case of different assembly, user should
|
||||||
|
* scale this parameter by dividing it by Rcs.
|
||||||
|
* If phase current resolution bit is set to 1, READ_CSx value should be doubled.
|
||||||
|
* Note, that current phase sensing, providing by the device is not accurate. This is
|
||||||
|
* because sampling of current occurrence of bit weight has a big deviation, especially for
|
||||||
|
* light load.
|
||||||
|
*/
|
||||||
|
ret = DIV_ROUND_CLOSEST(ret * 100 - 9800, data->curr_sense_gain);
|
||||||
|
ret = (data->phase_curr_resolution) ? ret * 2 : ret;
|
||||||
|
/* Scale according to total current resolution. */
|
||||||
|
ret = (data->total_curr_resolution) ? ret * 8 : ret * 4;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mp2888_read_phases(struct i2c_client *client, struct mp2888_data *data, int page, int phase)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (phase) {
|
||||||
|
case 0 ... 1:
|
||||||
|
ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS1_2);
|
||||||
|
break;
|
||||||
|
case 2 ... 3:
|
||||||
|
ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS3_4);
|
||||||
|
break;
|
||||||
|
case 4 ... 5:
|
||||||
|
ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS5_6);
|
||||||
|
break;
|
||||||
|
case 6 ... 7:
|
||||||
|
ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS7_8);
|
||||||
|
break;
|
||||||
|
case 8 ... 9:
|
||||||
|
ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS9_10);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -ENODATA;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mp2888_read_word_data(struct i2c_client *client, int page, int phase, int reg)
|
||||||
|
{
|
||||||
|
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||||
|
struct mp2888_data *data = to_mp2888_data(info);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case PMBUS_READ_VIN:
|
||||||
|
ret = pmbus_read_word_data(client, page, phase, reg);
|
||||||
|
if (ret <= 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* READ_VIN requires fixup to scale it to linear11 format. Register data format
|
||||||
|
* provides 10 bits for mantissa and 6 bits for exponent. Bits 15:10 are set with
|
||||||
|
* the fixed value 111011b.
|
||||||
|
*/
|
||||||
|
ret = (ret & GENMASK(9, 0)) | ((ret & GENMASK(31, 10)) << 1);
|
||||||
|
break;
|
||||||
|
case PMBUS_OT_WARN_LIMIT:
|
||||||
|
ret = pmbus_read_word_data(client, page, phase, reg);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
/*
|
||||||
|
* Chip reports limits in degrees C, but the actual temperature in 10th of
|
||||||
|
* degrees C - scaling is needed to match both.
|
||||||
|
*/
|
||||||
|
ret *= MP2888_TEMP_UNIT;
|
||||||
|
break;
|
||||||
|
case PMBUS_READ_IOUT:
|
||||||
|
if (phase != 0xff)
|
||||||
|
return mp2888_read_phases(client, data, page, phase);
|
||||||
|
|
||||||
|
ret = pmbus_read_word_data(client, page, phase, reg);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
/*
|
||||||
|
* READ_IOUT register has unused bits 15:12 with fixed value 1110b. Clear these
|
||||||
|
* bits and scale with total current resolution. Data is provided in direct format.
|
||||||
|
*/
|
||||||
|
ret &= GENMASK(11, 0);
|
||||||
|
ret = data->total_curr_resolution ? ret * 2 : ret;
|
||||||
|
break;
|
||||||
|
case PMBUS_IOUT_OC_WARN_LIMIT:
|
||||||
|
ret = pmbus_read_word_data(client, page, phase, reg);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
ret &= GENMASK(9, 0);
|
||||||
|
/*
|
||||||
|
* Chip reports limits with resolution 1A or 2A, if total current resolution bit is
|
||||||
|
* set 1. Actual current is reported with 0.25A or respectively 0.5A resolution.
|
||||||
|
* Scaling is needed to match both.
|
||||||
|
*/
|
||||||
|
ret = data->total_curr_resolution ? ret * 8 : ret * 4;
|
||||||
|
break;
|
||||||
|
case PMBUS_READ_POUT:
|
||||||
|
case PMBUS_READ_PIN:
|
||||||
|
ret = pmbus_read_word_data(client, page, phase, reg);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
ret = data->total_curr_resolution ? ret * 2 : ret;
|
||||||
|
break;
|
||||||
|
case PMBUS_POUT_OP_WARN_LIMIT:
|
||||||
|
ret = pmbus_read_word_data(client, page, phase, reg);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
/*
|
||||||
|
* Chip reports limits with resolution 1W or 2W, if total current resolution bit is
|
||||||
|
* set 1. Actual power is reported with 0.5W or 1W respectively resolution. Scaling
|
||||||
|
* is needed to match both.
|
||||||
|
*/
|
||||||
|
ret = data->total_curr_resolution ? ret * 4 : ret * 2;
|
||||||
|
break;
|
||||||
|
/*
|
||||||
|
* The below registers are not implemented by device or implemented not according to the
|
||||||
|
* spec. Skip all of them to avoid exposing non-relevant inputs to sysfs.
|
||||||
|
*/
|
||||||
|
case PMBUS_OT_FAULT_LIMIT:
|
||||||
|
case PMBUS_UT_WARN_LIMIT:
|
||||||
|
case PMBUS_UT_FAULT_LIMIT:
|
||||||
|
case PMBUS_VIN_UV_FAULT_LIMIT:
|
||||||
|
case PMBUS_VOUT_UV_WARN_LIMIT:
|
||||||
|
case PMBUS_VOUT_OV_WARN_LIMIT:
|
||||||
|
case PMBUS_VOUT_UV_FAULT_LIMIT:
|
||||||
|
case PMBUS_VOUT_OV_FAULT_LIMIT:
|
||||||
|
case PMBUS_VIN_OV_WARN_LIMIT:
|
||||||
|
case PMBUS_IOUT_OC_LV_FAULT_LIMIT:
|
||||||
|
case PMBUS_IOUT_OC_FAULT_LIMIT:
|
||||||
|
case PMBUS_POUT_MAX:
|
||||||
|
case PMBUS_IOUT_UC_FAULT_LIMIT:
|
||||||
|
case PMBUS_POUT_OP_FAULT_LIMIT:
|
||||||
|
case PMBUS_PIN_OP_WARN_LIMIT:
|
||||||
|
case PMBUS_MFR_VIN_MIN:
|
||||||
|
case PMBUS_MFR_VOUT_MIN:
|
||||||
|
case PMBUS_MFR_VIN_MAX:
|
||||||
|
case PMBUS_MFR_VOUT_MAX:
|
||||||
|
case PMBUS_MFR_IIN_MAX:
|
||||||
|
case PMBUS_MFR_IOUT_MAX:
|
||||||
|
case PMBUS_MFR_PIN_MAX:
|
||||||
|
case PMBUS_MFR_POUT_MAX:
|
||||||
|
case PMBUS_MFR_MAX_TEMP_1:
|
||||||
|
return -ENXIO;
|
||||||
|
default:
|
||||||
|
return -ENODATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mp2888_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 mp2888_data *data = to_mp2888_data(info);
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case PMBUS_OT_WARN_LIMIT:
|
||||||
|
word = DIV_ROUND_CLOSEST(word, MP2888_TEMP_UNIT);
|
||||||
|
/* Drop unused bits 15:8. */
|
||||||
|
word = clamp_val(word, 0, GENMASK(7, 0));
|
||||||
|
break;
|
||||||
|
case PMBUS_IOUT_OC_WARN_LIMIT:
|
||||||
|
/* Fix limit according to total curent resolution. */
|
||||||
|
word = data->total_curr_resolution ? DIV_ROUND_CLOSEST(word, 8) :
|
||||||
|
DIV_ROUND_CLOSEST(word, 4);
|
||||||
|
/* Drop unused bits 15:10. */
|
||||||
|
word = clamp_val(word, 0, GENMASK(9, 0));
|
||||||
|
break;
|
||||||
|
case PMBUS_POUT_OP_WARN_LIMIT:
|
||||||
|
/* Fix limit according to total curent resolution. */
|
||||||
|
word = data->total_curr_resolution ? DIV_ROUND_CLOSEST(word, 4) :
|
||||||
|
DIV_ROUND_CLOSEST(word, 2);
|
||||||
|
/* Drop unused bits 15:10. */
|
||||||
|
word = clamp_val(word, 0, GENMASK(9, 0));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -ENODATA;
|
||||||
|
}
|
||||||
|
return pmbus_write_word_data(client, page, reg, word);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mp2888_identify_multiphase(struct i2c_client *client, struct mp2888_data *data,
|
||||||
|
struct pmbus_driver_info *info)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Identify multiphase number - could be from 1 to 10. */
|
||||||
|
ret = i2c_smbus_read_word_data(client, MP2888_MFR_VR_CONFIG1);
|
||||||
|
if (ret <= 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
info->phases[0] = ret & GENMASK(3, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The device provides a total of 10 PWM pins, and can be configured to different phase
|
||||||
|
* count applications for rail.
|
||||||
|
*/
|
||||||
|
if (info->phases[0] > MP2888_MAX_PHASE)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pmbus_driver_info mp2888_info = {
|
||||||
|
.pages = 1,
|
||||||
|
.format[PSC_VOLTAGE_IN] = linear,
|
||||||
|
.format[PSC_VOLTAGE_OUT] = direct,
|
||||||
|
.format[PSC_TEMPERATURE] = direct,
|
||||||
|
.format[PSC_CURRENT_IN] = linear,
|
||||||
|
.format[PSC_CURRENT_OUT] = direct,
|
||||||
|
.format[PSC_POWER] = direct,
|
||||||
|
.m[PSC_TEMPERATURE] = 1,
|
||||||
|
.R[PSC_TEMPERATURE] = 1,
|
||||||
|
.m[PSC_VOLTAGE_OUT] = 1,
|
||||||
|
.R[PSC_VOLTAGE_OUT] = 3,
|
||||||
|
.m[PSC_CURRENT_OUT] = 4,
|
||||||
|
.m[PSC_POWER] = 1,
|
||||||
|
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT |
|
||||||
|
PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP |
|
||||||
|
PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT |
|
||||||
|
PMBUS_PHASE_VIRTUAL,
|
||||||
|
.pfunc[0] = PMBUS_HAVE_IOUT,
|
||||||
|
.pfunc[1] = PMBUS_HAVE_IOUT,
|
||||||
|
.pfunc[2] = PMBUS_HAVE_IOUT,
|
||||||
|
.pfunc[3] = PMBUS_HAVE_IOUT,
|
||||||
|
.pfunc[4] = PMBUS_HAVE_IOUT,
|
||||||
|
.pfunc[5] = PMBUS_HAVE_IOUT,
|
||||||
|
.pfunc[6] = PMBUS_HAVE_IOUT,
|
||||||
|
.pfunc[7] = PMBUS_HAVE_IOUT,
|
||||||
|
.pfunc[8] = PMBUS_HAVE_IOUT,
|
||||||
|
.pfunc[9] = PMBUS_HAVE_IOUT,
|
||||||
|
.read_byte_data = mp2888_read_byte_data,
|
||||||
|
.read_word_data = mp2888_read_word_data,
|
||||||
|
.write_word_data = mp2888_write_word_data,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int mp2888_probe(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct pmbus_driver_info *info;
|
||||||
|
struct mp2888_data *data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
data = devm_kzalloc(&client->dev, sizeof(struct mp2888_data), GFP_KERNEL);
|
||||||
|
if (!data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
memcpy(&data->info, &mp2888_info, sizeof(*info));
|
||||||
|
info = &data->info;
|
||||||
|
|
||||||
|
/* Identify multiphase configuration. */
|
||||||
|
ret = mp2888_identify_multiphase(client, data, info);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Obtain current sense gain of power stage and current resolution. */
|
||||||
|
ret = mp2888_current_sense_gain_and_resolution_get(client, data);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return pmbus_do_probe(client, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_device_id mp2888_id[] = {
|
||||||
|
{"mp2888", 0},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(i2c, mp2888_id);
|
||||||
|
|
||||||
|
static const struct of_device_id __maybe_unused mp2888_of_match[] = {
|
||||||
|
{.compatible = "mps,mp2888"},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, mp2888_of_match);
|
||||||
|
|
||||||
|
static struct i2c_driver mp2888_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "mp2888",
|
||||||
|
.of_match_table = of_match_ptr(mp2888_of_match),
|
||||||
|
},
|
||||||
|
.probe_new = mp2888_probe,
|
||||||
|
.id_table = mp2888_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_i2c_driver(mp2888_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>");
|
||||||
|
MODULE_DESCRIPTION("PMBus driver for MPS MP2888 device");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_IMPORT_NS(PMBUS);
|
|
@ -0,0 +1,233 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Hardware monitoring driver for PIM4006, PIM4328 and PIM4820
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 Flextronics International Sweden AB
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/pmbus.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include "pmbus.h"
|
||||||
|
|
||||||
|
enum chips { pim4006, pim4328, pim4820 };
|
||||||
|
|
||||||
|
struct pim4328_data {
|
||||||
|
enum chips id;
|
||||||
|
struct pmbus_driver_info info;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define to_pim4328_data(x) container_of(x, struct pim4328_data, info)
|
||||||
|
|
||||||
|
/* PIM4006 and PIM4328 */
|
||||||
|
#define PIM4328_MFR_READ_VINA 0xd3
|
||||||
|
#define PIM4328_MFR_READ_VINB 0xd4
|
||||||
|
|
||||||
|
/* PIM4006 */
|
||||||
|
#define PIM4328_MFR_READ_IINA 0xd6
|
||||||
|
#define PIM4328_MFR_READ_IINB 0xd7
|
||||||
|
#define PIM4328_MFR_FET_CHECKSTATUS 0xd9
|
||||||
|
|
||||||
|
/* PIM4328 */
|
||||||
|
#define PIM4328_MFR_STATUS_BITS 0xd5
|
||||||
|
|
||||||
|
/* PIM4820 */
|
||||||
|
#define PIM4328_MFR_READ_STATUS 0xd0
|
||||||
|
|
||||||
|
static const struct i2c_device_id pim4328_id[] = {
|
||||||
|
{"bmr455", pim4328},
|
||||||
|
{"pim4006", pim4006},
|
||||||
|
{"pim4106", pim4006},
|
||||||
|
{"pim4206", pim4006},
|
||||||
|
{"pim4306", pim4006},
|
||||||
|
{"pim4328", pim4328},
|
||||||
|
{"pim4406", pim4006},
|
||||||
|
{"pim4820", pim4820},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, pim4328_id);
|
||||||
|
|
||||||
|
static int pim4328_read_word_data(struct i2c_client *client, int page,
|
||||||
|
int phase, int reg)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (page > 0)
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
if (phase == 0xff)
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case PMBUS_READ_VIN:
|
||||||
|
ret = pmbus_read_word_data(client, page, phase,
|
||||||
|
phase == 0 ? PIM4328_MFR_READ_VINA
|
||||||
|
: PIM4328_MFR_READ_VINB);
|
||||||
|
break;
|
||||||
|
case PMBUS_READ_IIN:
|
||||||
|
ret = pmbus_read_word_data(client, page, phase,
|
||||||
|
phase == 0 ? PIM4328_MFR_READ_IINA
|
||||||
|
: PIM4328_MFR_READ_IINB);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENODATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pim4328_read_byte_data(struct i2c_client *client, int page, int reg)
|
||||||
|
{
|
||||||
|
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||||
|
struct pim4328_data *data = to_pim4328_data(info);
|
||||||
|
int ret, status;
|
||||||
|
|
||||||
|
if (page > 0)
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case PMBUS_STATUS_BYTE:
|
||||||
|
ret = pmbus_read_byte_data(client, page, PMBUS_STATUS_BYTE);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
if (data->id == pim4006) {
|
||||||
|
status = pmbus_read_word_data(client, page, 0xff,
|
||||||
|
PIM4328_MFR_FET_CHECKSTATUS);
|
||||||
|
if (status < 0)
|
||||||
|
return status;
|
||||||
|
if (status & 0x0630) /* Input UV */
|
||||||
|
ret |= PB_STATUS_VIN_UV;
|
||||||
|
} else if (data->id == pim4328) {
|
||||||
|
status = pmbus_read_byte_data(client, page,
|
||||||
|
PIM4328_MFR_STATUS_BITS);
|
||||||
|
if (status < 0)
|
||||||
|
return status;
|
||||||
|
if (status & 0x04) /* Input UV */
|
||||||
|
ret |= PB_STATUS_VIN_UV;
|
||||||
|
if (status & 0x40) /* Output UV */
|
||||||
|
ret |= PB_STATUS_NONE_ABOVE;
|
||||||
|
} else if (data->id == pim4820) {
|
||||||
|
status = pmbus_read_byte_data(client, page,
|
||||||
|
PIM4328_MFR_READ_STATUS);
|
||||||
|
if (status < 0)
|
||||||
|
return status;
|
||||||
|
if (status & 0x05) /* Input OV or OC */
|
||||||
|
ret |= PB_STATUS_NONE_ABOVE;
|
||||||
|
if (status & 0x1a) /* Input UV */
|
||||||
|
ret |= PB_STATUS_VIN_UV;
|
||||||
|
if (status & 0x40) /* OT */
|
||||||
|
ret |= PB_STATUS_TEMPERATURE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -ENODATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pim4328_probe(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
u8 device_id[I2C_SMBUS_BLOCK_MAX + 1];
|
||||||
|
const struct i2c_device_id *mid;
|
||||||
|
struct pim4328_data *data;
|
||||||
|
struct pmbus_driver_info *info;
|
||||||
|
struct pmbus_platform_data *pdata;
|
||||||
|
struct device *dev = &client->dev;
|
||||||
|
|
||||||
|
if (!i2c_check_functionality(client->adapter,
|
||||||
|
I2C_FUNC_SMBUS_READ_BYTE_DATA
|
||||||
|
| I2C_FUNC_SMBUS_BLOCK_DATA))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
data = devm_kzalloc(&client->dev, sizeof(struct pim4328_data),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
status = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, device_id);
|
||||||
|
if (status < 0) {
|
||||||
|
dev_err(&client->dev, "Failed to read Manufacturer Model\n");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
for (mid = pim4328_id; mid->name[0]; mid++) {
|
||||||
|
if (!strncasecmp(mid->name, device_id, strlen(mid->name)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!mid->name[0]) {
|
||||||
|
dev_err(&client->dev, "Unsupported device\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(client->name, mid->name))
|
||||||
|
dev_notice(&client->dev,
|
||||||
|
"Device mismatch: Configured %s, detected %s\n",
|
||||||
|
client->name, mid->name);
|
||||||
|
|
||||||
|
data->id = mid->driver_data;
|
||||||
|
info = &data->info;
|
||||||
|
info->pages = 1;
|
||||||
|
info->read_byte_data = pim4328_read_byte_data;
|
||||||
|
info->read_word_data = pim4328_read_word_data;
|
||||||
|
|
||||||
|
pdata = devm_kzalloc(dev, sizeof(struct pmbus_platform_data),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!pdata)
|
||||||
|
return -ENOMEM;
|
||||||
|
dev->platform_data = pdata;
|
||||||
|
pdata->flags = PMBUS_NO_CAPABILITY | PMBUS_NO_WRITE_PROTECT;
|
||||||
|
|
||||||
|
switch (data->id) {
|
||||||
|
case pim4006:
|
||||||
|
info->phases[0] = 2;
|
||||||
|
info->func[0] = PMBUS_PHASE_VIRTUAL | PMBUS_HAVE_VIN
|
||||||
|
| PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT;
|
||||||
|
info->pfunc[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN;
|
||||||
|
info->pfunc[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN;
|
||||||
|
break;
|
||||||
|
case pim4328:
|
||||||
|
info->phases[0] = 2;
|
||||||
|
info->func[0] = PMBUS_PHASE_VIRTUAL
|
||||||
|
| PMBUS_HAVE_VCAP | PMBUS_HAVE_VIN
|
||||||
|
| PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT;
|
||||||
|
info->pfunc[0] = PMBUS_HAVE_VIN;
|
||||||
|
info->pfunc[1] = PMBUS_HAVE_VIN;
|
||||||
|
info->format[PSC_VOLTAGE_IN] = direct;
|
||||||
|
info->format[PSC_TEMPERATURE] = direct;
|
||||||
|
info->format[PSC_CURRENT_OUT] = direct;
|
||||||
|
pdata->flags |= PMBUS_USE_COEFFICIENTS_CMD;
|
||||||
|
break;
|
||||||
|
case pim4820:
|
||||||
|
info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_TEMP
|
||||||
|
| PMBUS_HAVE_IIN;
|
||||||
|
info->format[PSC_VOLTAGE_IN] = direct;
|
||||||
|
info->format[PSC_TEMPERATURE] = direct;
|
||||||
|
info->format[PSC_CURRENT_IN] = direct;
|
||||||
|
pdata->flags |= PMBUS_USE_COEFFICIENTS_CMD;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pmbus_do_probe(client, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct i2c_driver pim4328_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "pim4328",
|
||||||
|
},
|
||||||
|
.probe_new = pim4328_probe,
|
||||||
|
.id_table = pim4328_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_i2c_driver(pim4328_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>");
|
||||||
|
MODULE_DESCRIPTION("PMBus driver for PIM4006, PIM4328, PIM4820 power interface modules");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_IMPORT_NS(PMBUS);
|
|
@ -173,13 +173,13 @@ static int pmbus_probe(struct i2c_client *client)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
device_info = (struct pmbus_device_info *)i2c_match_id(pmbus_id, client)->driver_data;
|
device_info = (struct pmbus_device_info *)i2c_match_id(pmbus_id, client)->driver_data;
|
||||||
if (device_info->flags & PMBUS_SKIP_STATUS_CHECK) {
|
if (device_info->flags) {
|
||||||
pdata = devm_kzalloc(dev, sizeof(struct pmbus_platform_data),
|
pdata = devm_kzalloc(dev, sizeof(struct pmbus_platform_data),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!pdata)
|
if (!pdata)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
pdata->flags = PMBUS_SKIP_STATUS_CHECK;
|
pdata->flags = device_info->flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
info->pages = device_info->pages;
|
info->pages = device_info->pages;
|
||||||
|
@ -193,22 +193,37 @@ static const struct pmbus_device_info pmbus_info_one = {
|
||||||
.pages = 1,
|
.pages = 1,
|
||||||
.flags = 0
|
.flags = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct pmbus_device_info pmbus_info_zero = {
|
static const struct pmbus_device_info pmbus_info_zero = {
|
||||||
.pages = 0,
|
.pages = 0,
|
||||||
.flags = 0
|
.flags = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct pmbus_device_info pmbus_info_one_skip = {
|
static const struct pmbus_device_info pmbus_info_one_skip = {
|
||||||
.pages = 1,
|
.pages = 1,
|
||||||
.flags = PMBUS_SKIP_STATUS_CHECK
|
.flags = PMBUS_SKIP_STATUS_CHECK
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct pmbus_device_info pmbus_info_one_status = {
|
||||||
|
.pages = 1,
|
||||||
|
.flags = PMBUS_READ_STATUS_AFTER_FAILED_CHECK
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Use driver_data to set the number of pages supported by the chip.
|
* Use driver_data to set the number of pages supported by the chip.
|
||||||
*/
|
*/
|
||||||
static const struct i2c_device_id pmbus_id[] = {
|
static const struct i2c_device_id pmbus_id[] = {
|
||||||
{"adp4000", (kernel_ulong_t)&pmbus_info_one},
|
{"adp4000", (kernel_ulong_t)&pmbus_info_one},
|
||||||
|
{"bmr310", (kernel_ulong_t)&pmbus_info_one_status},
|
||||||
{"bmr453", (kernel_ulong_t)&pmbus_info_one},
|
{"bmr453", (kernel_ulong_t)&pmbus_info_one},
|
||||||
{"bmr454", (kernel_ulong_t)&pmbus_info_one},
|
{"bmr454", (kernel_ulong_t)&pmbus_info_one},
|
||||||
|
{"bmr456", (kernel_ulong_t)&pmbus_info_one},
|
||||||
|
{"bmr457", (kernel_ulong_t)&pmbus_info_one},
|
||||||
|
{"bmr458", (kernel_ulong_t)&pmbus_info_one_status},
|
||||||
|
{"bmr480", (kernel_ulong_t)&pmbus_info_one_status},
|
||||||
|
{"bmr490", (kernel_ulong_t)&pmbus_info_one_status},
|
||||||
|
{"bmr491", (kernel_ulong_t)&pmbus_info_one_status},
|
||||||
|
{"bmr492", (kernel_ulong_t)&pmbus_info_one},
|
||||||
{"dps460", (kernel_ulong_t)&pmbus_info_one_skip},
|
{"dps460", (kernel_ulong_t)&pmbus_info_one_skip},
|
||||||
{"dps650ab", (kernel_ulong_t)&pmbus_info_one_skip},
|
{"dps650ab", (kernel_ulong_t)&pmbus_info_one_skip},
|
||||||
{"dps800", (kernel_ulong_t)&pmbus_info_one_skip},
|
{"dps800", (kernel_ulong_t)&pmbus_info_one_skip},
|
||||||
|
|
|
@ -375,7 +375,7 @@ enum pmbus_sensor_classes {
|
||||||
};
|
};
|
||||||
|
|
||||||
#define PMBUS_PAGES 32 /* Per PMBus specification */
|
#define PMBUS_PAGES 32 /* Per PMBus specification */
|
||||||
#define PMBUS_PHASES 8 /* Maximum number of phases per page */
|
#define PMBUS_PHASES 10 /* Maximum number of phases per page */
|
||||||
|
|
||||||
/* Functionality bit mask */
|
/* Functionality bit mask */
|
||||||
#define PMBUS_HAVE_VIN BIT(0)
|
#define PMBUS_HAVE_VIN BIT(0)
|
||||||
|
|
|
@ -523,6 +523,8 @@ static bool pmbus_check_register(struct i2c_client *client,
|
||||||
rv = func(client, page, reg);
|
rv = func(client, page, reg);
|
||||||
if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK))
|
if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK))
|
||||||
rv = pmbus_check_status_cml(client);
|
rv = pmbus_check_status_cml(client);
|
||||||
|
if (rv < 0 && (data->flags & PMBUS_READ_STATUS_AFTER_FAILED_CHECK))
|
||||||
|
data->read_status(client, -1);
|
||||||
pmbus_clear_fault_page(client, -1);
|
pmbus_clear_fault_page(client, -1);
|
||||||
return rv >= 0;
|
return rv >= 0;
|
||||||
}
|
}
|
||||||
|
@ -1327,14 +1329,14 @@ static int pmbus_add_sensor_attrs(struct i2c_client *client,
|
||||||
|
|
||||||
pages = paged ? info->pages : 1;
|
pages = paged ? info->pages : 1;
|
||||||
for (page = 0; page < pages; page++) {
|
for (page = 0; page < pages; page++) {
|
||||||
if (!(info->func[page] & attrs->func))
|
if (info->func[page] & attrs->func) {
|
||||||
continue;
|
|
||||||
ret = pmbus_add_sensor_attrs_one(client, data, info,
|
ret = pmbus_add_sensor_attrs_one(client, data, info,
|
||||||
name, index, page,
|
name, index, page,
|
||||||
0xff, attrs, paged);
|
0xff, attrs, paged);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
index++;
|
index++;
|
||||||
|
}
|
||||||
if (info->phases[page]) {
|
if (info->phases[page]) {
|
||||||
int phase;
|
int phase;
|
||||||
|
|
||||||
|
@ -2139,6 +2141,111 @@ static int pmbus_find_attributes(struct i2c_client *client,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The pmbus_class_attr_map structure maps one sensor class to
|
||||||
|
* it's corresponding sensor attributes array.
|
||||||
|
*/
|
||||||
|
struct pmbus_class_attr_map {
|
||||||
|
enum pmbus_sensor_classes class;
|
||||||
|
int nattr;
|
||||||
|
const struct pmbus_sensor_attr *attr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct pmbus_class_attr_map class_attr_map[] = {
|
||||||
|
{
|
||||||
|
.class = PSC_VOLTAGE_IN,
|
||||||
|
.attr = voltage_attributes,
|
||||||
|
.nattr = ARRAY_SIZE(voltage_attributes),
|
||||||
|
}, {
|
||||||
|
.class = PSC_VOLTAGE_OUT,
|
||||||
|
.attr = voltage_attributes,
|
||||||
|
.nattr = ARRAY_SIZE(voltage_attributes),
|
||||||
|
}, {
|
||||||
|
.class = PSC_CURRENT_IN,
|
||||||
|
.attr = current_attributes,
|
||||||
|
.nattr = ARRAY_SIZE(current_attributes),
|
||||||
|
}, {
|
||||||
|
.class = PSC_CURRENT_OUT,
|
||||||
|
.attr = current_attributes,
|
||||||
|
.nattr = ARRAY_SIZE(current_attributes),
|
||||||
|
}, {
|
||||||
|
.class = PSC_POWER,
|
||||||
|
.attr = power_attributes,
|
||||||
|
.nattr = ARRAY_SIZE(power_attributes),
|
||||||
|
}, {
|
||||||
|
.class = PSC_TEMPERATURE,
|
||||||
|
.attr = temp_attributes,
|
||||||
|
.nattr = ARRAY_SIZE(temp_attributes),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read the coefficients for direct mode.
|
||||||
|
*/
|
||||||
|
static int pmbus_read_coefficients(struct i2c_client *client,
|
||||||
|
struct pmbus_driver_info *info,
|
||||||
|
const struct pmbus_sensor_attr *attr)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
enum pmbus_sensor_classes class = attr->class;
|
||||||
|
s8 R;
|
||||||
|
s16 m, b;
|
||||||
|
|
||||||
|
data.block[0] = 2;
|
||||||
|
data.block[1] = attr->reg;
|
||||||
|
data.block[2] = 0x01;
|
||||||
|
|
||||||
|
rv = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
|
||||||
|
I2C_SMBUS_WRITE, PMBUS_COEFFICIENTS,
|
||||||
|
I2C_SMBUS_BLOCK_PROC_CALL, &data);
|
||||||
|
|
||||||
|
if (rv < 0)
|
||||||
|
return rv;
|
||||||
|
|
||||||
|
if (data.block[0] != 5)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
m = data.block[1] | (data.block[2] << 8);
|
||||||
|
b = data.block[3] | (data.block[4] << 8);
|
||||||
|
R = data.block[5];
|
||||||
|
info->m[class] = m;
|
||||||
|
info->b[class] = b;
|
||||||
|
info->R[class] = R;
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pmbus_init_coefficients(struct i2c_client *client,
|
||||||
|
struct pmbus_driver_info *info)
|
||||||
|
{
|
||||||
|
int i, n, ret = -EINVAL;
|
||||||
|
const struct pmbus_class_attr_map *map;
|
||||||
|
const struct pmbus_sensor_attr *attr;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(class_attr_map); i++) {
|
||||||
|
map = &class_attr_map[i];
|
||||||
|
if (info->format[map->class] != direct)
|
||||||
|
continue;
|
||||||
|
for (n = 0; n < map->nattr; n++) {
|
||||||
|
attr = &map->attr[n];
|
||||||
|
if (map->class != attr->class)
|
||||||
|
continue;
|
||||||
|
ret = pmbus_read_coefficients(client, info, attr);
|
||||||
|
if (ret >= 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&client->dev,
|
||||||
|
"No coefficients found for sensor class %d\n",
|
||||||
|
map->class);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Identify chip parameters.
|
* Identify chip parameters.
|
||||||
* This function is called for all chips.
|
* This function is called for all chips.
|
||||||
|
@ -2214,21 +2321,26 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
|
||||||
data->has_status_word = true;
|
data->has_status_word = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enable PEC if the controller supports it */
|
/* Enable PEC if the controller and bus supports it */
|
||||||
if (!(data->flags & PMBUS_NO_CAPABILITY)) {
|
if (!(data->flags & PMBUS_NO_CAPABILITY)) {
|
||||||
ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
|
ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
|
||||||
if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK))
|
if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) {
|
||||||
|
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) {
|
||||||
client->flags |= I2C_CLIENT_PEC;
|
client->flags |= I2C_CLIENT_PEC;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the chip is write protected. If it is, we can not clear
|
* Check if the chip is write protected. If it is, we can not clear
|
||||||
* faults, and we should not try it. Also, in that case, writes into
|
* faults, and we should not try it. Also, in that case, writes into
|
||||||
* limit registers need to be disabled.
|
* limit registers need to be disabled.
|
||||||
*/
|
*/
|
||||||
|
if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) {
|
||||||
ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT);
|
ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT);
|
||||||
if (ret > 0 && (ret & PB_WP_ANY))
|
if (ret > 0 && (ret & PB_WP_ANY))
|
||||||
data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK;
|
data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK;
|
||||||
|
}
|
||||||
|
|
||||||
if (data->info->pages)
|
if (data->info->pages)
|
||||||
pmbus_clear_faults(client);
|
pmbus_clear_faults(client);
|
||||||
|
@ -2255,6 +2367,17 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data->flags & PMBUS_USE_COEFFICIENTS_CMD) {
|
||||||
|
if (!i2c_check_functionality(client->adapter,
|
||||||
|
I2C_FUNC_SMBUS_BLOCK_PROC_CALL))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
ret = pmbus_init_coefficients(client, info);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
#include "pmbus.h"
|
#include "pmbus.h"
|
||||||
|
|
||||||
enum chips { zl2004, zl2005, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105,
|
enum chips { zl2004, zl2005, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105,
|
||||||
zl9101, zl9117 };
|
zl8802, zl9101, zl9117, zls1003, zls4009 };
|
||||||
|
|
||||||
struct zl6100_data {
|
struct zl6100_data {
|
||||||
int id;
|
int id;
|
||||||
|
@ -34,6 +34,13 @@ struct zl6100_data {
|
||||||
|
|
||||||
#define ZL6100_MFR_XTEMP_ENABLE BIT(7)
|
#define ZL6100_MFR_XTEMP_ENABLE BIT(7)
|
||||||
|
|
||||||
|
#define ZL8802_MFR_USER_GLOBAL_CONFIG 0xe9
|
||||||
|
#define ZL8802_MFR_TMON_ENABLE BIT(12)
|
||||||
|
#define ZL8802_MFR_USER_CONFIG 0xd1
|
||||||
|
#define ZL8802_MFR_XTEMP_ENABLE_2 BIT(1)
|
||||||
|
#define ZL8802_MFR_DDC_CONFIG 0xd3
|
||||||
|
#define ZL8802_MFR_PHASES_MASK 0x0007
|
||||||
|
|
||||||
#define MFR_VMON_OV_FAULT_LIMIT 0xf5
|
#define MFR_VMON_OV_FAULT_LIMIT 0xf5
|
||||||
#define MFR_VMON_UV_FAULT_LIMIT 0xf6
|
#define MFR_VMON_UV_FAULT_LIMIT 0xf6
|
||||||
#define MFR_READ_VMON 0xf7
|
#define MFR_READ_VMON 0xf7
|
||||||
|
@ -132,7 +139,7 @@ static int zl6100_read_word_data(struct i2c_client *client, int page,
|
||||||
struct zl6100_data *data = to_zl6100_data(info);
|
struct zl6100_data *data = to_zl6100_data(info);
|
||||||
int ret, vreg;
|
int ret, vreg;
|
||||||
|
|
||||||
if (page > 0)
|
if (page >= info->pages)
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
if (data->id == zl2005) {
|
if (data->id == zl2005) {
|
||||||
|
@ -191,7 +198,7 @@ static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg)
|
||||||
struct zl6100_data *data = to_zl6100_data(info);
|
struct zl6100_data *data = to_zl6100_data(info);
|
||||||
int ret, status;
|
int ret, status;
|
||||||
|
|
||||||
if (page > 0)
|
if (page >= info->pages)
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
zl6100_wait(data);
|
zl6100_wait(data);
|
||||||
|
@ -230,7 +237,7 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg,
|
||||||
struct zl6100_data *data = to_zl6100_data(info);
|
struct zl6100_data *data = to_zl6100_data(info);
|
||||||
int ret, vreg;
|
int ret, vreg;
|
||||||
|
|
||||||
if (page > 0)
|
if (page >= info->pages)
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
switch (reg) {
|
switch (reg) {
|
||||||
|
@ -271,7 +278,7 @@ static int zl6100_write_byte(struct i2c_client *client, int page, u8 value)
|
||||||
struct zl6100_data *data = to_zl6100_data(info);
|
struct zl6100_data *data = to_zl6100_data(info);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (page > 0)
|
if (page >= info->pages)
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
zl6100_wait(data);
|
zl6100_wait(data);
|
||||||
|
@ -287,6 +294,10 @@ static const struct i2c_device_id zl6100_id[] = {
|
||||||
{"bmr462", zl2008},
|
{"bmr462", zl2008},
|
||||||
{"bmr463", zl2008},
|
{"bmr463", zl2008},
|
||||||
{"bmr464", zl2008},
|
{"bmr464", zl2008},
|
||||||
|
{"bmr465", zls4009},
|
||||||
|
{"bmr466", zls1003},
|
||||||
|
{"bmr467", zls4009},
|
||||||
|
{"bmr469", zl8802},
|
||||||
{"zl2004", zl2004},
|
{"zl2004", zl2004},
|
||||||
{"zl2005", zl2005},
|
{"zl2005", zl2005},
|
||||||
{"zl2006", zl2006},
|
{"zl2006", zl2006},
|
||||||
|
@ -295,15 +306,18 @@ static const struct i2c_device_id zl6100_id[] = {
|
||||||
{"zl2106", zl2106},
|
{"zl2106", zl2106},
|
||||||
{"zl6100", zl6100},
|
{"zl6100", zl6100},
|
||||||
{"zl6105", zl6105},
|
{"zl6105", zl6105},
|
||||||
|
{"zl8802", zl8802},
|
||||||
{"zl9101", zl9101},
|
{"zl9101", zl9101},
|
||||||
{"zl9117", zl9117},
|
{"zl9117", zl9117},
|
||||||
|
{"zls1003", zls1003},
|
||||||
|
{"zls4009", zls4009},
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(i2c, zl6100_id);
|
MODULE_DEVICE_TABLE(i2c, zl6100_id);
|
||||||
|
|
||||||
static int zl6100_probe(struct i2c_client *client)
|
static int zl6100_probe(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret, i;
|
||||||
struct zl6100_data *data;
|
struct zl6100_data *data;
|
||||||
struct pmbus_driver_info *info;
|
struct pmbus_driver_info *info;
|
||||||
u8 device_id[I2C_SMBUS_BLOCK_MAX + 1];
|
u8 device_id[I2C_SMBUS_BLOCK_MAX + 1];
|
||||||
|
@ -367,18 +381,70 @@ static int zl6100_probe(struct i2c_client *client)
|
||||||
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
|
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ZL2004, ZL9101M, and ZL9117M support monitoring an extra voltage
|
* ZL2004, ZL8802, ZL9101M, ZL9117M and ZLS4009 support monitoring
|
||||||
* (VMON for ZL2004, VDRV for ZL9101M and ZL9117M). Report it as vmon.
|
* an extra voltage (VMON for ZL2004, ZL8802 and ZLS4009,
|
||||||
|
* VDRV for ZL9101M and ZL9117M). Report it as vmon.
|
||||||
*/
|
*/
|
||||||
if (data->id == zl2004 || data->id == zl9101 || data->id == zl9117)
|
if (data->id == zl2004 || data->id == zl8802 || data->id == zl9101 ||
|
||||||
|
data->id == zl9117 || data->id == zls4009)
|
||||||
info->func[0] |= PMBUS_HAVE_VMON | PMBUS_HAVE_STATUS_VMON;
|
info->func[0] |= PMBUS_HAVE_VMON | PMBUS_HAVE_STATUS_VMON;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ZL8802 has two outputs that can be used either independently or in
|
||||||
|
* a current sharing configuration. The driver uses the DDC_CONFIG
|
||||||
|
* register to check if the module is running with independent or
|
||||||
|
* shared outputs. If the module is in shared output mode, only one
|
||||||
|
* output voltage will be reported.
|
||||||
|
*/
|
||||||
|
if (data->id == zl8802) {
|
||||||
|
info->pages = 2;
|
||||||
|
info->func[0] |= PMBUS_HAVE_IIN;
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_word_data(client, ZL8802_MFR_DDC_CONFIG);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
data->access = ktime_get();
|
||||||
|
zl6100_wait(data);
|
||||||
|
|
||||||
|
if (ret & ZL8802_MFR_PHASES_MASK)
|
||||||
|
info->func[1] |= PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
|
||||||
|
else
|
||||||
|
info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
|
||||||
|
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
data->access = ktime_get();
|
||||||
|
zl6100_wait(data);
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_CONFIG);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (ret & ZL8802_MFR_XTEMP_ENABLE_2)
|
||||||
|
info->func[i] |= PMBUS_HAVE_TEMP2;
|
||||||
|
|
||||||
|
data->access = ktime_get();
|
||||||
|
zl6100_wait(data);
|
||||||
|
}
|
||||||
|
ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_GLOBAL_CONFIG);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (ret & ZL8802_MFR_TMON_ENABLE)
|
||||||
|
info->func[0] |= PMBUS_HAVE_TEMP3;
|
||||||
|
} else {
|
||||||
ret = i2c_smbus_read_word_data(client, ZL6100_MFR_CONFIG);
|
ret = i2c_smbus_read_word_data(client, ZL6100_MFR_CONFIG);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (ret & ZL6100_MFR_XTEMP_ENABLE)
|
if (ret & ZL6100_MFR_XTEMP_ENABLE)
|
||||||
info->func[0] |= PMBUS_HAVE_TEMP2;
|
info->func[0] |= PMBUS_HAVE_TEMP2;
|
||||||
|
}
|
||||||
|
|
||||||
data->access = ktime_get();
|
data->access = ktime_get();
|
||||||
zl6100_wait(data);
|
zl6100_wait(data);
|
||||||
|
|
|
@ -64,7 +64,6 @@ static const char * const SCH5627_IN_LABELS[SCH5627_NO_IN] = {
|
||||||
|
|
||||||
struct sch5627_data {
|
struct sch5627_data {
|
||||||
unsigned short addr;
|
unsigned short addr;
|
||||||
struct sch56xx_watchdog_data *watchdog;
|
|
||||||
u8 control;
|
u8 control;
|
||||||
u8 temp_max[SCH5627_NO_TEMPS];
|
u8 temp_max[SCH5627_NO_TEMPS];
|
||||||
u8 temp_crit[SCH5627_NO_TEMPS];
|
u8 temp_crit[SCH5627_NO_TEMPS];
|
||||||
|
@ -357,16 +356,6 @@ static const struct hwmon_chip_info sch5627_chip_info = {
|
||||||
.info = sch5627_info,
|
.info = sch5627_info,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int sch5627_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct sch5627_data *data = platform_get_drvdata(pdev);
|
|
||||||
|
|
||||||
if (data->watchdog)
|
|
||||||
sch56xx_watchdog_unregister(data->watchdog);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sch5627_probe(struct platform_device *pdev)
|
static int sch5627_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct sch5627_data *data;
|
struct sch5627_data *data;
|
||||||
|
@ -460,7 +449,7 @@ static int sch5627_probe(struct platform_device *pdev)
|
||||||
return PTR_ERR(hwmon_dev);
|
return PTR_ERR(hwmon_dev);
|
||||||
|
|
||||||
/* Note failing to register the watchdog is not a fatal error */
|
/* Note failing to register the watchdog is not a fatal error */
|
||||||
data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr,
|
sch56xx_watchdog_register(&pdev->dev, data->addr,
|
||||||
(build_code << 24) | (build_id << 8) | hwmon_rev,
|
(build_code << 24) | (build_id << 8) | hwmon_rev,
|
||||||
&data->update_lock, 1);
|
&data->update_lock, 1);
|
||||||
|
|
||||||
|
@ -472,7 +461,6 @@ static struct platform_driver sch5627_driver = {
|
||||||
.name = DRVNAME,
|
.name = DRVNAME,
|
||||||
},
|
},
|
||||||
.probe = sch5627_probe,
|
.probe = sch5627_probe,
|
||||||
.remove = sch5627_remove,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module_platform_driver(sch5627_driver);
|
module_platform_driver(sch5627_driver);
|
||||||
|
|
|
@ -54,7 +54,6 @@ static const u16 SCH5636_REG_FAN_VAL[SCH5636_NO_FANS] = {
|
||||||
struct sch5636_data {
|
struct sch5636_data {
|
||||||
unsigned short addr;
|
unsigned short addr;
|
||||||
struct device *hwmon_dev;
|
struct device *hwmon_dev;
|
||||||
struct sch56xx_watchdog_data *watchdog;
|
|
||||||
|
|
||||||
struct mutex update_lock;
|
struct mutex update_lock;
|
||||||
char valid; /* !=0 if following fields are valid */
|
char valid; /* !=0 if following fields are valid */
|
||||||
|
@ -372,9 +371,6 @@ static int sch5636_remove(struct platform_device *pdev)
|
||||||
struct sch5636_data *data = platform_get_drvdata(pdev);
|
struct sch5636_data *data = platform_get_drvdata(pdev);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (data->watchdog)
|
|
||||||
sch56xx_watchdog_unregister(data->watchdog);
|
|
||||||
|
|
||||||
if (data->hwmon_dev)
|
if (data->hwmon_dev)
|
||||||
hwmon_device_unregister(data->hwmon_dev);
|
hwmon_device_unregister(data->hwmon_dev);
|
||||||
|
|
||||||
|
@ -495,8 +491,7 @@ static int sch5636_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Note failing to register the watchdog is not a fatal error */
|
/* Note failing to register the watchdog is not a fatal error */
|
||||||
data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr,
|
sch56xx_watchdog_register(&pdev->dev, data->addr, (revision[0] << 8) | revision[1],
|
||||||
(revision[0] << 8) | revision[1],
|
|
||||||
&data->update_lock, 0);
|
&data->update_lock, 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -20,8 +20,8 @@
|
||||||
#include "sch56xx-common.h"
|
#include "sch56xx-common.h"
|
||||||
|
|
||||||
/* Insmod parameters */
|
/* Insmod parameters */
|
||||||
static int nowayout = WATCHDOG_NOWAYOUT;
|
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||||
module_param(nowayout, int, 0);
|
module_param(nowayout, bool, 0);
|
||||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
||||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||||
|
|
||||||
|
@ -378,8 +378,8 @@ static const struct watchdog_ops watchdog_ops = {
|
||||||
.set_timeout = watchdog_set_timeout,
|
.set_timeout = watchdog_set_timeout,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent,
|
void sch56xx_watchdog_register(struct device *parent, u16 addr, u32 revision,
|
||||||
u16 addr, u32 revision, struct mutex *io_lock, int check_enabled)
|
struct mutex *io_lock, int check_enabled)
|
||||||
{
|
{
|
||||||
struct sch56xx_watchdog_data *data;
|
struct sch56xx_watchdog_data *data;
|
||||||
int err, control, output_enable;
|
int err, control, output_enable;
|
||||||
|
@ -393,23 +393,22 @@ struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent,
|
||||||
mutex_unlock(io_lock);
|
mutex_unlock(io_lock);
|
||||||
|
|
||||||
if (control < 0)
|
if (control < 0)
|
||||||
return NULL;
|
return;
|
||||||
if (output_enable < 0)
|
if (output_enable < 0)
|
||||||
return NULL;
|
return;
|
||||||
if (check_enabled && !(output_enable & SCH56XX_WDOG_OUTPUT_ENABLE)) {
|
if (check_enabled && !(output_enable & SCH56XX_WDOG_OUTPUT_ENABLE)) {
|
||||||
pr_warn("Watchdog not enabled by BIOS, not registering\n");
|
pr_warn("Watchdog not enabled by BIOS, not registering\n");
|
||||||
return NULL;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
data = kzalloc(sizeof(struct sch56xx_watchdog_data), GFP_KERNEL);
|
data = devm_kzalloc(parent, sizeof(struct sch56xx_watchdog_data), GFP_KERNEL);
|
||||||
if (!data)
|
if (!data)
|
||||||
return NULL;
|
return;
|
||||||
|
|
||||||
data->addr = addr;
|
data->addr = addr;
|
||||||
data->io_lock = io_lock;
|
data->io_lock = io_lock;
|
||||||
|
|
||||||
strlcpy(data->wdinfo.identity, "sch56xx watchdog",
|
strscpy(data->wdinfo.identity, "sch56xx watchdog", sizeof(data->wdinfo.identity));
|
||||||
sizeof(data->wdinfo.identity));
|
|
||||||
data->wdinfo.firmware_version = revision;
|
data->wdinfo.firmware_version = revision;
|
||||||
data->wdinfo.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT;
|
data->wdinfo.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT;
|
||||||
if (!nowayout)
|
if (!nowayout)
|
||||||
|
@ -421,8 +420,7 @@ struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent,
|
||||||
data->wddev.timeout = 60;
|
data->wddev.timeout = 60;
|
||||||
data->wddev.min_timeout = 1;
|
data->wddev.min_timeout = 1;
|
||||||
data->wddev.max_timeout = 255 * 60;
|
data->wddev.max_timeout = 255 * 60;
|
||||||
if (nowayout)
|
watchdog_set_nowayout(&data->wddev, nowayout);
|
||||||
set_bit(WDOG_NO_WAY_OUT, &data->wddev.status);
|
|
||||||
if (output_enable & SCH56XX_WDOG_OUTPUT_ENABLE)
|
if (output_enable & SCH56XX_WDOG_OUTPUT_ENABLE)
|
||||||
set_bit(WDOG_ACTIVE, &data->wddev.status);
|
set_bit(WDOG_ACTIVE, &data->wddev.status);
|
||||||
|
|
||||||
|
@ -438,24 +436,14 @@ struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent,
|
||||||
data->watchdog_output_enable = output_enable;
|
data->watchdog_output_enable = output_enable;
|
||||||
|
|
||||||
watchdog_set_drvdata(&data->wddev, data);
|
watchdog_set_drvdata(&data->wddev, data);
|
||||||
err = watchdog_register_device(&data->wddev);
|
err = devm_watchdog_register_device(parent, &data->wddev);
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_err("Registering watchdog chardev: %d\n", err);
|
pr_err("Registering watchdog chardev: %d\n", err);
|
||||||
kfree(data);
|
devm_kfree(parent, data);
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sch56xx_watchdog_register);
|
EXPORT_SYMBOL(sch56xx_watchdog_register);
|
||||||
|
|
||||||
void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data)
|
|
||||||
{
|
|
||||||
watchdog_unregister_device(&data->wddev);
|
|
||||||
kfree(data);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(sch56xx_watchdog_unregister);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* platform dev find, add and remove functions
|
* platform dev find, add and remove functions
|
||||||
*/
|
*/
|
||||||
|
@ -516,37 +504,18 @@ static int __init sch56xx_device_add(int address, const char *name)
|
||||||
struct resource res = {
|
struct resource res = {
|
||||||
.start = address,
|
.start = address,
|
||||||
.end = address + REGION_LENGTH - 1,
|
.end = address + REGION_LENGTH - 1,
|
||||||
|
.name = name,
|
||||||
.flags = IORESOURCE_IO,
|
.flags = IORESOURCE_IO,
|
||||||
};
|
};
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
sch56xx_pdev = platform_device_alloc(name, address);
|
|
||||||
if (!sch56xx_pdev)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
res.name = sch56xx_pdev->name;
|
|
||||||
err = acpi_check_resource_conflict(&res);
|
err = acpi_check_resource_conflict(&res);
|
||||||
if (err)
|
if (err)
|
||||||
goto exit_device_put;
|
|
||||||
|
|
||||||
err = platform_device_add_resources(sch56xx_pdev, &res, 1);
|
|
||||||
if (err) {
|
|
||||||
pr_err("Device resource addition failed\n");
|
|
||||||
goto exit_device_put;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = platform_device_add(sch56xx_pdev);
|
|
||||||
if (err) {
|
|
||||||
pr_err("Device addition failed\n");
|
|
||||||
goto exit_device_put;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
exit_device_put:
|
|
||||||
platform_device_put(sch56xx_pdev);
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
sch56xx_pdev = platform_device_register_simple(name, -1, &res, 1);
|
||||||
|
|
||||||
|
return PTR_ERR_OR_ZERO(sch56xx_pdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init sch56xx_init(void)
|
static int __init sch56xx_init(void)
|
||||||
|
|
|
@ -14,6 +14,6 @@ int sch56xx_read_virtual_reg16(u16 addr, u16 reg);
|
||||||
int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg,
|
int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg,
|
||||||
int high_nibble);
|
int high_nibble);
|
||||||
|
|
||||||
struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent,
|
void sch56xx_watchdog_register(struct device *parent, u16 addr, u32 revision,
|
||||||
u16 addr, u32 revision, struct mutex *io_lock, int check_enabled);
|
struct mutex *io_lock, int check_enabled);
|
||||||
void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data);
|
void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data);
|
||||||
|
|
|
@ -0,0 +1,296 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) Linumiz 2021
|
||||||
|
*
|
||||||
|
* sht4x.c - Linux hwmon driver for SHT4x Temperature and Humidity sensor
|
||||||
|
*
|
||||||
|
* Author: Navin Sankar Velliangiri <navin@linumiz.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/crc8.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/hwmon.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/jiffies.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Poll intervals (in milliseconds)
|
||||||
|
*/
|
||||||
|
#define SHT4X_MIN_POLL_INTERVAL 2000
|
||||||
|
|
||||||
|
/*
|
||||||
|
* I2C command delays (in microseconds)
|
||||||
|
*/
|
||||||
|
#define SHT4X_MEAS_DELAY 1000
|
||||||
|
#define SHT4X_DELAY_EXTRA 10000
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Command Bytes
|
||||||
|
*/
|
||||||
|
#define SHT4X_CMD_MEASURE_HPM 0b11111101
|
||||||
|
#define SHT4X_CMD_RESET 0b10010100
|
||||||
|
|
||||||
|
#define SHT4X_CMD_LEN 1
|
||||||
|
#define SHT4X_CRC8_LEN 1
|
||||||
|
#define SHT4X_WORD_LEN 2
|
||||||
|
#define SHT4X_RESPONSE_LENGTH 6
|
||||||
|
#define SHT4X_CRC8_POLYNOMIAL 0x31
|
||||||
|
#define SHT4X_CRC8_INIT 0xff
|
||||||
|
#define SHT4X_MIN_TEMPERATURE -45000
|
||||||
|
#define SHT4X_MAX_TEMPERATURE 125000
|
||||||
|
#define SHT4X_MIN_HUMIDITY 0
|
||||||
|
#define SHT4X_MAX_HUMIDITY 100000
|
||||||
|
|
||||||
|
DECLARE_CRC8_TABLE(sht4x_crc8_table);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct sht4x_data - All the data required to operate an SHT4X chip
|
||||||
|
* @client: the i2c client associated with the SHT4X
|
||||||
|
* @lock: a mutex that is used to prevent parallel access to the i2c client
|
||||||
|
* @update_interval: the minimum poll interval
|
||||||
|
* @last_updated: the previous time that the SHT4X was polled
|
||||||
|
* @temperature: the latest temperature value received from the SHT4X
|
||||||
|
* @humidity: the latest humidity value received from the SHT4X
|
||||||
|
*/
|
||||||
|
struct sht4x_data {
|
||||||
|
struct i2c_client *client;
|
||||||
|
struct mutex lock; /* atomic read data updates */
|
||||||
|
bool valid; /* validity of fields below */
|
||||||
|
long update_interval; /* in milli-seconds */
|
||||||
|
long last_updated; /* in jiffies */
|
||||||
|
s32 temperature;
|
||||||
|
s32 humidity;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sht4x_read_values() - read and parse the raw data from the SHT4X
|
||||||
|
* @sht4x_data: the struct sht4x_data to use for the lock
|
||||||
|
* Return: 0 if successful, -ERRNO if not
|
||||||
|
*/
|
||||||
|
static int sht4x_read_values(struct sht4x_data *data)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
u16 t_ticks, rh_ticks;
|
||||||
|
unsigned long next_update;
|
||||||
|
struct i2c_client *client = data->client;
|
||||||
|
u8 crc;
|
||||||
|
u8 cmd[SHT4X_CMD_LEN] = {SHT4X_CMD_MEASURE_HPM};
|
||||||
|
u8 raw_data[SHT4X_RESPONSE_LENGTH];
|
||||||
|
|
||||||
|
mutex_lock(&data->lock);
|
||||||
|
next_update = data->last_updated +
|
||||||
|
msecs_to_jiffies(data->update_interval);
|
||||||
|
|
||||||
|
if (data->valid && time_before_eq(jiffies, next_update))
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN);
|
||||||
|
if (ret < 0)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
usleep_range(SHT4X_MEAS_DELAY, SHT4X_MEAS_DELAY + SHT4X_DELAY_EXTRA);
|
||||||
|
|
||||||
|
ret = i2c_master_recv(client, raw_data, SHT4X_RESPONSE_LENGTH);
|
||||||
|
if (ret != SHT4X_RESPONSE_LENGTH) {
|
||||||
|
if (ret >= 0)
|
||||||
|
ret = -ENODATA;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_ticks = raw_data[0] << 8 | raw_data[1];
|
||||||
|
rh_ticks = raw_data[3] << 8 | raw_data[4];
|
||||||
|
|
||||||
|
crc = crc8(sht4x_crc8_table, &raw_data[0], SHT4X_WORD_LEN, CRC8_INIT_VALUE);
|
||||||
|
if (crc != raw_data[2]) {
|
||||||
|
dev_err(&client->dev, "data integrity check failed\n");
|
||||||
|
ret = -EIO;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
crc = crc8(sht4x_crc8_table, &raw_data[3], SHT4X_WORD_LEN, CRC8_INIT_VALUE);
|
||||||
|
if (crc != raw_data[5]) {
|
||||||
|
dev_err(&client->dev, "data integrity check failed\n");
|
||||||
|
ret = -EIO;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->temperature = ((21875 * (int32_t)t_ticks) >> 13) - 45000;
|
||||||
|
data->humidity = ((15625 * (int32_t)rh_ticks) >> 13) - 6000;
|
||||||
|
data->last_updated = jiffies;
|
||||||
|
data->valid = true;
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t sht4x_interval_write(struct sht4x_data *data, long val)
|
||||||
|
{
|
||||||
|
data->update_interval = clamp_val(val, SHT4X_MIN_POLL_INTERVAL, UINT_MAX);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sht4x_interval_read() - read the minimum poll interval in milliseconds */
|
||||||
|
static size_t sht4x_interval_read(struct sht4x_data *data, long *val)
|
||||||
|
{
|
||||||
|
*val = data->update_interval;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sht4x_temperature1_read() - read the temperature in millidegrees */
|
||||||
|
static int sht4x_temperature1_read(struct sht4x_data *data, long *val)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = sht4x_read_values(data);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
*val = data->temperature;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sht4x_humidity1_read() - read a relative humidity in millipercent */
|
||||||
|
static int sht4x_humidity1_read(struct sht4x_data *data, long *val)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = sht4x_read_values(data);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
*val = data->humidity;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static umode_t sht4x_hwmon_visible(const void *data,
|
||||||
|
enum hwmon_sensor_types type,
|
||||||
|
u32 attr, int channel)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case hwmon_temp:
|
||||||
|
case hwmon_humidity:
|
||||||
|
return 0444;
|
||||||
|
case hwmon_chip:
|
||||||
|
return 0644;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sht4x_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
|
||||||
|
u32 attr, int channel, long *val)
|
||||||
|
{
|
||||||
|
struct sht4x_data *data = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case hwmon_temp:
|
||||||
|
return sht4x_temperature1_read(data, val);
|
||||||
|
case hwmon_humidity:
|
||||||
|
return sht4x_humidity1_read(data, val);
|
||||||
|
case hwmon_chip:
|
||||||
|
return sht4x_interval_read(data, val);
|
||||||
|
default:
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sht4x_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
|
||||||
|
u32 attr, int channel, long val)
|
||||||
|
{
|
||||||
|
struct sht4x_data *data = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case hwmon_chip:
|
||||||
|
return sht4x_interval_write(data, val);
|
||||||
|
default:
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct hwmon_channel_info *sht4x_info[] = {
|
||||||
|
HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL),
|
||||||
|
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
|
||||||
|
HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT),
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct hwmon_ops sht4x_hwmon_ops = {
|
||||||
|
.is_visible = sht4x_hwmon_visible,
|
||||||
|
.read = sht4x_hwmon_read,
|
||||||
|
.write = sht4x_hwmon_write,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct hwmon_chip_info sht4x_chip_info = {
|
||||||
|
.ops = &sht4x_hwmon_ops,
|
||||||
|
.info = sht4x_info,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int sht4x_probe(struct i2c_client *client,
|
||||||
|
const struct i2c_device_id *sht4x_id)
|
||||||
|
{
|
||||||
|
struct device *device = &client->dev;
|
||||||
|
struct device *hwmon_dev;
|
||||||
|
struct sht4x_data *data;
|
||||||
|
u8 cmd[] = {SHT4X_CMD_RESET};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* we require full i2c support since the sht4x uses multi-byte read and
|
||||||
|
* writes as well as multi-byte commands which are not supported by
|
||||||
|
* the smbus protocol
|
||||||
|
*/
|
||||||
|
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
data = devm_kzalloc(device, sizeof(*data), GFP_KERNEL);
|
||||||
|
if (!data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
data->update_interval = SHT4X_MIN_POLL_INTERVAL;
|
||||||
|
data->client = client;
|
||||||
|
|
||||||
|
mutex_init(&data->lock);
|
||||||
|
|
||||||
|
crc8_populate_msb(sht4x_crc8_table, SHT4X_CRC8_POLYNOMIAL);
|
||||||
|
|
||||||
|
ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
if (ret != SHT4X_CMD_LEN)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
hwmon_dev = devm_hwmon_device_register_with_info(device,
|
||||||
|
client->name,
|
||||||
|
data,
|
||||||
|
&sht4x_chip_info,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct i2c_device_id sht4x_id[] = {
|
||||||
|
{ "sht4x", 0 },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, sht4x_id);
|
||||||
|
|
||||||
|
static struct i2c_driver sht4x_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "sht4x",
|
||||||
|
},
|
||||||
|
.probe = sht4x_probe,
|
||||||
|
.id_table = sht4x_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_i2c_driver(sht4x_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Navin Sankar Velliangiri <navin@linumiz.com>");
|
||||||
|
MODULE_DESCRIPTION("Sensirion SHT4x humidity and temperature sensor driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -43,6 +43,36 @@
|
||||||
*/
|
*/
|
||||||
#define PMBUS_NO_CAPABILITY BIT(2)
|
#define PMBUS_NO_CAPABILITY BIT(2)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PMBUS_READ_STATUS_AFTER_FAILED_CHECK
|
||||||
|
*
|
||||||
|
* Some PMBus chips end up in an undefined state when trying to read an
|
||||||
|
* unsupported register. For such chips, it is necessary to reset the
|
||||||
|
* chip pmbus controller to a known state after a failed register check.
|
||||||
|
* This can be done by reading a known register. By setting this flag the
|
||||||
|
* driver will try to read the STATUS register after each failed
|
||||||
|
* register check. This read may fail, but it will put the chip in a
|
||||||
|
* known state.
|
||||||
|
*/
|
||||||
|
#define PMBUS_READ_STATUS_AFTER_FAILED_CHECK BIT(3)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PMBUS_NO_WRITE_PROTECT
|
||||||
|
*
|
||||||
|
* Some PMBus chips respond with invalid data when reading the WRITE_PROTECT
|
||||||
|
* register. For such chips, this flag should be set so that the PMBus core
|
||||||
|
* driver doesn't use the WRITE_PROTECT command to determine its behavior.
|
||||||
|
*/
|
||||||
|
#define PMBUS_NO_WRITE_PROTECT BIT(4)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PMBUS_USE_COEFFICIENTS_CMD
|
||||||
|
*
|
||||||
|
* When this flag is set the PMBus core driver will use the COEFFICIENTS
|
||||||
|
* register to initialize the coefficients for the direct mode format.
|
||||||
|
*/
|
||||||
|
#define PMBUS_USE_COEFFICIENTS_CMD BIT(5)
|
||||||
|
|
||||||
struct pmbus_platform_data {
|
struct pmbus_platform_data {
|
||||||
u32 flags; /* Device specific flags */
|
u32 flags; /* Device specific flags */
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue