Commit 2a5c6184 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 69609a91 a5f6c0f8
......@@ -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
......
.. 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 supplys current. A third ADC monitors
Two ADCs simultaneously measure each supply's current. A third ADC monitors
the input voltages and four auxiliary external voltages.
......
......@@ -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)
================== === =======================================================
.. 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**
.. 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 Intels VR12.5 processors and memories.
performance digital controller designed to power Intel's VR12.5 processors and memories.
The device utilizes digital technology to implement all control and power management
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
......
.. 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
* Renesas / Intersil / Zilker Labs ZLS1003, ZLS4009
Prefix: 'zls1003', zls4009
Addresses scanned: -
Datasheet: Not published
* Ericsson BMR450, BMR451
* 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
* 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
http://archive.ericsson.net/service/internet/picov/get?DocNo=28701-EN/LZT146256
* 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 "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.
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.
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, &regval);
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)
if (fan_config & MAX31790_FAN_CFG_CTRL_MON)
*val = 0;
else if (fan_config & MAX31790_FAN_CFG_RPM_MODE)
*val = 2;
else if (fan_config & MAX31790_FAN_CFG_TACH_INPUT_EN)
*val = 1;
else
*val = 0;
*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;
......
// 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);
This diff is collapsed.
// 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;
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;
if (ret & ZL6100_MFR_XTEMP_ENABLE)
info->func[0] |= PMBUS_HAVE_TEMP2;
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;
err = platform_device_add_resources(sch56xx_pdev, &res, 1);
if (err) {
pr_err("Device resource addition failed\n");
goto exit_device_put;
}
err = platform_device_add(sch56xx_pdev);
if (err) {
pr_err("Device addition failed\n");
goto exit_device_put;
}
return 0;
return err;
exit_device_put:
platform_device_put(sch56xx_pdev);
sch56xx_pdev = platform_device_register_simple(name, -1, &res, 1);
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);
// 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 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment