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