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:
Linus Torvalds 2021-06-28 11:13:26 -07:00
commit 2a5c61843e
45 changed files with 2203 additions and 263 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.
======================= ======================================================

View File

@ -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

View File

@ -19,7 +19,7 @@ Authors:
Description Description
----------- -----------
The IR36021 is a dualloop digital multiphase 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

View File

@ -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'

View File

@ -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 supplys 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.

View File

@ -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)
================== === ======================================================= ================== === =======================================================

View File

@ -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**

View File

@ -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.
======================= ========================================================

View File

@ -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 Intels 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

View File

@ -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.

View File

@ -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

View File

@ -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.
============== =============================================

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
} }

View File

@ -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)

View File

@ -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, &regval); ret = ina3221_read_value(ina, reg, &regval);
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:

View File

@ -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,

View File

@ -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);

View File

@ -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,22 +1748,41 @@ 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,

View File

@ -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,

View File

@ -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,8 +173,11 @@ 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]);
rpm = RPM_FROM_REG(data->tach[channel], sr); if (data->tach[channel] == FAN_COUNT_REG_MAX)
rpm = 0;
else
rpm = RPM_FROM_REG(data->tach[channel], sr);
*val = rpm; *val = rpm;
return 0; return 0;
case hwmon_fan_target: case hwmon_fan_target:
@ -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;

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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},

View File

@ -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)

View File

@ -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,11 +2321,14 @@ 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)) {
client->flags |= I2C_CLIENT_PEC; if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) {
client->flags |= I2C_CLIENT_PEC;
}
}
} }
/* /*
@ -2226,9 +2336,11 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
* 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.
*/ */
ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT); if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) {
if (ret > 0 && (ret & PB_WP_ANY)) ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT);
data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; if (ret > 0 && (ret & PB_WP_ANY))
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;
} }

View File

@ -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;
ret = i2c_smbus_read_word_data(client, ZL6100_MFR_CONFIG); /*
if (ret < 0) * ZL8802 has two outputs that can be used either independently or in
return ret; * 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;
if (ret & ZL6100_MFR_XTEMP_ENABLE) ret = i2c_smbus_read_word_data(client, ZL8802_MFR_DDC_CONFIG);
info->func[0] |= PMBUS_HAVE_TEMP2; 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);
if (ret < 0)
return ret;
if (ret & ZL6100_MFR_XTEMP_ENABLE)
info->func[0] |= PMBUS_HAVE_TEMP2;
}
data->access = ktime_get(); data->access = ktime_get();
zl6100_wait(data); zl6100_wait(data);

View File

@ -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,9 +449,9 @@ 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);
return 0; return 0;
} }
@ -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);

View File

@ -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,9 +491,8 @@ 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;

View File

@ -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; return err;
err = platform_device_add_resources(sch56xx_pdev, &res, 1); sch56xx_pdev = platform_device_register_simple(name, -1, &res, 1);
if (err) {
pr_err("Device resource addition failed\n");
goto exit_device_put;
}
err = platform_device_add(sch56xx_pdev); return PTR_ERR_OR_ZERO(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;
} }
static int __init sch56xx_init(void) static int __init sch56xx_init(void)

View File

@ -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);

296
drivers/hwmon/sht4x.c Normal file
View File

@ -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");

View File

@ -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 */